如何让taro旧项目在Node17/18上跑起来
虽然人常说版本能用就不要动,但是有些时候总是想在新的环境中运行旧的项目,Taro就是个典型的例子,当下载了官方的实例项目之后,会发现在新版的Node中运行不起来,这篇文章就是来解决两个最为常见的问题.
什么是Taro
参照Taro官方文档
Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ / 飞书 小程序 / H5 / RN 等应用。
显而易见,其是一个主要用于小程序开发的跨平台框架,通过使用Taro组件库提供的公共接口以及生命周期,就能实现一端编写,编译成多个平台,甚至是RN.![]()
为什么是Taro
原生开发大多基于Vue的template-Scripts范式,对于React开发者不太友好,并且也不利于组件化.并且使用
Taro的运行原理
在微信小程序使用类Vue的模板形式开发的情况下,对于React
开发者而言,只能自己实现转换.就像Babel
一样,对源代码进行词法分析,知道你写了哪些词,然后再进行语法分析,判断你的词有没有意义,符不符合基本(语)法,如果ok就进行语义分析,构建出一个表达代码功能含义的AST(虚拟语法树).因此只要使用官方提供的标准组件以及路由,在编译的时候会根据编译目标平台自动替换成相应的组件以及接口.
更多内容可以参照 官方博客中介绍AST的部分.
但是,这意味着编译后的代码与React
毫无关系,只是单纯的代码像是React
而已,这就要求开发者在写代码的时候严格按照Taro
组件库的约束进行.既然eact最终还是调用浏览器的DOM
接口,那直接将其进行抽象,从让调用者适应小程序变为让小程序配对调用的接口,最终实现运行时适配.
关于针对React
的兼容性适配以及事件机制的实现可以参见 官方介绍
Taro旧项目在新版本(例如17/18以上)时可能会出现的问题
当直接把旧项目在新版的Node环境下运行,会出现两个错误,一个是少了点东西,一个是多了点东西.
ERR_OSSL_EVP_UNSUPPORTED
在进行构建的时候,有可能会输出以下的报错
{
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
如该回答所示,具体原因在于中间React等包在生成哈希的时候使用了被OpenSSL 3.0
所废弃的算法.
If you hit an
ERR_OSSL_EVP_UNSUPPORTED
error in your application with Node.js 17, it’s likely that your application or a module you’re using is attempting to use an algorithm or key size which is no longer allowed by default with OpenSSL 3.0. A command-line option,--openssl-legacy-provider
, has been added to revert to the legacy provider as a temporary workaround for these tightened restrictions.
解决办法
-
设置环境变量使得
Node
使用旧版的openssl环境.以windows为例,以windows+powershell为例,在package.json中使用”pre”脚本来在运行build之前自动设置变量.具体而言,如果目标是运行”build”,那么在scripts中增加json "prebuild ":"echo \"set node ssl config\"&& $env:NODE_OPTIONS=\"--openssl-legacy-provider\"
"即可. -
手动指定
Webpack
提供的哈希方法.如文档所示,通过在Taro
的Webpack
配置文件config/index.js
中配置hashFunction
,可以使用npm包,而不是系统提供的ssl算法来进行哈希生成.The hashing algorithm to use. All functions from Node.JS'
crypto.createHash
are supported. Since4.0.0-alpha2
, thehashFunction
can now be a constructor to a custom hash function. You can provide a non-crypto hash function for performance reasons.module.exports = { //... output: { hashFunction: require('metrohash').MetroHash64, }, };
Make sure that the hashing function will have an
update
anddigest
methods available.
You must provide the URL of lib/mappings.wasm by calling SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) before using SourceMapConsumer
在打包的时候还会出现这个问题
根据查找,可以发现这个问题出现在source-map
这个包里.作为为混淆后代码提供源代码参照的文件,其被React Refresh Webpack Plugin
等多个插件在打包时被使用.
Source-map干了什么
SourceMap 与前端异常监控
一开始还会好奇为什么只要禁用了新版本Node提供的原生fetch就可以解决问题,实际查看解决问题的Commit后才会感觉哭笑不得: ```diff - if (typeof fetch === "function") { + / Determine browser vs node environment by testing the default top level + context. Solution courtesy of: http://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser / + const isBrowserEnvironment = (function() { + // eslint-disable-next-line no-undef + return (typeof window !== "undefined") && (this === window); + }).call(); + + if (isBrowserEnvironment) { // Web version of reading a wasm file into an array buffer.
let mappingsWasm = null;
``
好吧,原来一开始这个包使用是否有
fetch方法来判断运行环境是浏览器还是
Node.不过也难怪,其实谁都没想到一直都闷声发大财的
Node咋突然就支持
fetch了.现在判断
this的指向总不会错了,毕竟
Node中的
this指向为
Global或者
{}(根据运行环节的不同,参见[这篇文章](http://zhuanlan.zhihu.com/p/489467530)),但是浏览器就是指向window.
#### 解决方法
既然是包的问题,并且在新的版本里已经修复了,那就只要更新项目的包依赖就好了...吗?没这么简单.直接在
package.json的
dependencies`里设定Source-map的版本,只会更新第一层依赖的版本,例如使用npm list source-map查看 项目的依赖包信息,如下图:
为了修改子依赖的版本,需要使用Override来进行更新.
如官方文档所示:
json
{ "overrides": {
"[email protected]": {
"foo": "1.0.0"
}
}
}
通过嵌套可以指定覆盖特定包,甚至指定版本的包的子依赖包版本,并且对下面无限深的嵌套都会默认覆盖(子依赖,孙子依赖等),适合这种安全更新发版,但是一些依赖直接写死了版本号导致不能应用新补丁的情况.
以本项目为例,在overrides的第一层规定source-map的版本为最新,就能使得所有依赖了这个包的包都将版本更新到0.7.4,而无视各自包配置中规定的依赖版本.