如何讓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,而無視各自包配置中規定的依賴版本.