1 為什么要遷移 Vue3.x
說點什么呢?
總之。。。 這不是我的錯!
首先先要寫個案例,找到一個可執行方案。
2 方案選擇
參考Vue2轉3官方文檔
https://v3.cn.vuejs.org/guide/migration/introduction.html
面對自己項目百八十個文件,人工爆肝肯定是不可能的,想都不能想!
唯一的方案是基于AST(抽象語法樹)解構代碼,根據Vue官網給出升級文檔的修改建議,批量修改輸出文件的方案。
只是。。。AST操作有點復雜。
調研了幾個工具,GitHub上找到了幾個去掉代碼文件里 console.log 的示例學習學習:(這兩處代碼引用作對比方案,可以跳過)
一. 利用 jscodeshift 操作 AST去掉console.log 的示例:
export default (fileInfo, api) => {
const j = api.jscodeshift;
const root = j(fileInfo.source)
const callExpressions = root.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: { type: 'Identifier', name: 'console' },
},
}
);
callExpressions.remove();
return root.toSource();
};
二. 利用 babel 操作 AST去掉console.log 的示例:
(基于篇幅省略了部分代碼,有興趣的同學請訪問https://github.com/mattphillips/babel-plugin-console)
export default function({
types,
}: typeof BabelCore): PluginObj<ConsoleTransformState> {
return {
name: 'console-transform',
visitor: {
CallExpression(path, { opts, file }) {
validateSchema(schema, opts);
const { env, removeMethods, additionalStyleMethods } = opts;
const callee = path.get('callee');
/*
基于篇幅限制
此處省略40+行代碼
*/
},
},
};
}
基于本人實力,這兩個方案實在是勸退案例。
正在我為這個項目重構發愁,發量迅速減少,混跡于社區尋找解救方案的時候,忽然發現了GoGoCode這個工具。
貼一段官方介紹:
GoGoCode是一個操作AST的工具,可以降低使用AST的門檻,幫助開發者從繁瑣的AST操作中解放出來,更專注于代碼分析轉換邏輯的開發。簡單的替換甚至不用學習AST,而初步學習了AST節點結構(可參考AST查看器)后就可以完成更復雜的分析轉換。
GoGoCode的官方文檔 https://gogocode.io/zh/docs/specification/getting-started
這不正是我想要的么,遇到你就是古話說的,瞌睡來了有人遞枕頭!
而GoGoCode操作 AST 去掉 代碼中console.log,僅僅只需要一行代碼!!
$(`要轉換的代碼段`).find(`console.log($_$)`).remove()
熟悉的 $符號,熟悉的find、remove等API,扣一下題,說零成本操作AST不算標題黨吧
爆贊!!!方案選定!!!
3 開工
舉個栗子:按鍵修飾符的遷移方案
Vue2轉3官方文檔 - 按鍵修飾符的遷移
https://v3.cn.vuejs.org/guide/migration/keycode-modifiers.html#%E6%A6%82%E8%A7%88
以下是變更的簡要總結:
- 非兼容:不再支持使用數字 (即鍵碼) 作為v-on修飾符
- 非兼容:不再支持config.keyCodes
按照文檔寫一個待轉化的Demo
<template>
<div>
<h1>遷移:按鍵修飾符</h1>
<p>遷移策略:
1.Vue3不再支持使用數字 (即鍵碼) 作為 v-on 修飾符
2.不再支持 config.keyCodes</p>
<div class="mt20 text-left">
<div>space:<input type="text" @keyup.space="keys('space')" /></div>
<div>space:<input type="text" @keyup.32="keys('keycode 32 space')" /> </div>
<div>space:<input type="text" @keyup.customSpace="keys('keycode 32 space')" /> </div>
</div>
</div>
</template>
<script>
import Vue from 'vue';
Vue.config.keyCodes = {
customSpace: 32,
customDelete: 46
};
export default {
name: '按鍵修飾符',
methods: {
keys(key) {
alert('您按下的是' + key);
},
},
};
</script>
任務確定
根據https://astexplorer.net分析下要轉換的內容
安利一下astexplorer.net這個工具,我們可以利用它很方便的查看某段代碼的AST語法樹結構
這里要做的有三件事:
1)提取代碼里自定義的keyCodes(上圖藍框Vue.config.keyCodes的內容部分),與系統keyCodes合并為一個map,后續要在template替換時使用;
2)移除Vue.config.keyCodes 代碼(Vue3不再支持);
3)遍歷所有標簽以及它的屬性,使用合并后的keyCodes替換標簽(上圖紅框 xx.32與xx.customSpace部分)。
轉換邏輯編寫
1.項目里運行安裝GoGoCode
npm install gogocode
- 初始化script的AST對象
const $ = require('gogocode');
//script代碼,用$轉為AST節點
let scriptAst = $(`
import Vue from 'vue';
Vue.config.keyCodes = {
customSpace: 32,
customDelete: 46
};
export default {
name: '按鍵修飾符',
methods: {
keys(key) {
alert('您按下的是' + key);
},
},
};`)
3.我們要尋找script里自定義的keyCodes(Vue.config.keyCodes的內容部分)
使用GoGoCode的find API,加上匹配通配符,拿到所有的自定義keyCode數組
// 匹配取出自定義的keyCode,結果:Node數組
const customKeyCodeList = scriptAst.find(`Vue.config.keyCodes = {$_$}`).match[0]
這里要注意構造匹配的 Vue.config.keyCodes = {
} 的字符串,需要符合可轉AST節點的規范
可以拿到 astexplorer.net工具中驗證一下
image.png
image.png
控制臺打出customKeyCodeList,是一個Node數組
4.customKeyCodeList加上系統的keyCode,構造全量的keyCodeMap
// 全量的keyCode對照表,基于篇幅這里只列出3個
// https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/keyCode
let keyCodeMap = {46: 'delete',32: 'space',112: 'f1'}
//加上自定義keyCode構造匯總所有的keyCodeMap,待會替換template內容的時候需要使用
//結果:{46: 'delete',32: 'space',112: 'f1', customSpace: 'space', customDelete: 'delete'}
for(let i = 0;i< customKeyCodeList.length; i=i+2){
Object.assign(keyCodeMap, {
[customKeyCodeList[i].value] : keyCodeMap[customKeyCodeList[i+1].value]
})
}
控制臺打出keyCodeMap構造結果
5.Vue3要求: Vue.config.keyCodes不再支持,需要移除。find到這個節點,使用remove API移除
scriptAst.find(`Vue.config.keyCodes = $_$`).remove()
6.初始化template節點,html模板需要帶{ parseOptions: { html: true } }參數
let templateAst = $(`<template>
<div>
<p>遷移:按鍵修飾符</p>
<p>遷移策略:
1.Vue3不再支持使用數字 (即鍵碼) 作為 v-on 修飾符
2.不再支持 config.keyCodes</p>
<div class="mt20 text-left">
<div>space:<input type="text" @keyup.space="keys('space')" /></div>
<div>space:<input type="text" @keyup.32="keys('keycode 32 space')" /> </div>
<div>space:<input type="text" @keyup.customSpace="keys('keycode 32 space')" /> </div>
</div>
</div>
</template>`, { parseOptions: { html: true } })
7.使用find、each、attr API遍歷所有的標簽以及它的屬性,使用keyCodeMap,替換屬性名稱
//find+each遍歷所有的標簽項
templateAst.find(['<$_$></$_$>', '<$_$ />']).each((node) => {
//如果節點含有屬性,則遍歷它的屬性
if (Array.isArray(node.attr('content.attributes'))) {
node.attr('content.attributes').forEach((attr) => {
//使用上文構造出來的匯總keyCodeMap,替換匹配到的屬性名 如@keyup.32 -> @keyup.space
for (let keyItem in keyCodeMap) {
if (attr.key.content.endsWith(`.${keyItem}`)) {
attr.key.content = attr.key.content.replace(`.${keyItem}`,`.${keyCodeMap[keyItem]}`)
}
}
})
}
})
這里用到的node.attr('content.attributes'),就是剛才從astexplorer.net工具查到的
8.最后輸出,大功告成,對比一下轉換的結果
4 總結
這段代碼里的AST相關操作只有10行左右,其余都是核心轉換邏輯。GoGoCode可以像使用Jquery操作DOM的一樣的體驗來操作AST,入門簡單使用也方便
starter里還有批量處理文件的demo
https://github.com/thx/gogocode/tree/main/packages/gogocode-starter
Github上提issues也很快就有回應!
https://github.com/thx/gogocode/issues?q=is%3Aissue+is%3Aclosed
實在是代碼轉換利器
如果文中有任何問題,歡迎您的反饋。謝謝,祝你有美好的一天!
GoGoCode 相關鏈接
GoGoCode的Github倉庫(新項目求star _) github.com/thx/gogocod…
GoGoCode的官網 gogocode.io/
可以來 playground 快速體驗一下 play.gogocode.io/