作為一個(gè)程序員,當(dāng)然總是期望自己的代碼能「一次編寫,四處運(yùn)行」,但真實(shí)經(jīng)驗(yàn)往往是「一處修改,百處填坑」,依賴落后了好幾個(gè)版本了想要升級(jí)、老代碼已經(jīng)看著很不爽了打算重構(gòu),都需要下堅(jiān)決的決心,畢竟哪里漏掉了或者改錯(cuò)了都可能釀成大禍,我們一般都怎么搞呢?
放棄不搞
頓時(shí)就減輕了痛苦有木有…但如果你堅(jiān)持要搞,請(qǐng)往下看!
土辦法的真香和局限
對(duì)于一些簡(jiǎn)單的需求,比如最近在掘金上看到了一個(gè)例子,去掉項(xiàng)目中?console.log(xxx)?代碼,我相信大家平時(shí)遇到這種需求第一個(gè)想法都是直接選擇編輯器批量文本替換成空字符串:
利用正則表達(dá)式,我們還是可以搞定很多需求的,但這樣真的能包含所有情況么?有的同事是真的喜歡回車。
console.log('aaa')復(fù)制代碼
這種情況下,如果面對(duì)更復(fù)雜的需求或者嚴(yán)謹(jǐn)?shù)膱?chǎng)景,要么我們編寫更復(fù)雜的正則表達(dá)式,要么我們就不得不去硬肝 AST 操作了。
AST有點(diǎn)復(fù)雜
上網(wǎng)搜索了一下,還真找到了利用 jscodeshift 操作 AST 去掉 console.log 的示例:
exportdefault(fileInfo, api) => {constj = api.jscodeshift;constroot = j(fileInfo.source)constcallExpressions = root.find(j.CallExpression, {callee: {type:'MemberExpression',object: {type:'Identifier',name:'console'},? ? ? },? ? }? );? callExpressions.remove();returnroot.toSource();};復(fù)制代碼
這需要大家對(duì) AST 結(jié)構(gòu)比較熟悉,在編寫的時(shí)候需要對(duì)著解析好的節(jié)點(diǎn)結(jié)構(gòu)才能緩緩寫出,過一段時(shí)間再一看,也不會(huì)比彎彎繞繞的正則更好理解——大家平時(shí)太少接觸 AST 了。
試著結(jié)合一下
有一次正當(dāng)我為一個(gè)項(xiàng)目 API 大重構(gòu)發(fā)愁,準(zhǔn)備人肉爆肝的時(shí)候,我旁邊的小姐姐實(shí)在看不下去了——她的項(xiàng)目比我更早地做了重構(gòu),人家不僅沒爆肝,還順手做了個(gè)工具?GoGoCode,這個(gè)工具借鑒了 jQuery 的兩大思想:
選擇器和鏈?zhǔn)秸{(diào)用
用這工具去掉?console.log(xxx)?,其實(shí)就是一句話的事:
const$ =require('gogocode')/** 刻意凌亂的代碼 **/constinput =`
? ? console
.log(`a, b,c`);
`// 關(guān)鍵代碼constoutput = $(input).replace('console.log()','').generate()console.log(output)復(fù)制代碼
它的創(chuàng)新之處就在于,把你輸入的?console.log()?解析成一段 AST 節(jié)點(diǎn)去源代碼里做節(jié)點(diǎn)樹的匹配,這樣自然就沒有代碼格式的問題了。你輸入的代碼就相當(dāng)于 jQuery 里面的選擇器,只不過這一次選擇的是代碼節(jié)點(diǎn)。
更多例子
清理?console.log?這個(gè)操作還是太簡(jiǎn)單了,我再舉一個(gè)栗子!
我們經(jīng)常使用這樣的枚舉列表:
constlist = [? {text:"A策略",value:1,tips:"Atip",? },? {text:"B策略",value:2,tips:"Btip",? },? {text:"C策略",value:3,tips:"Ctip",? },];復(fù)制代碼
突然有一天,為了統(tǒng)一代碼里的各種枚舉,我們需要把 text 屬性更名為 name,把 value 屬性更名為 id,這個(gè)用正則很難精確匹配容易誤傷,操作AST樹還有些麻煩,用?GoGoCode?只需要這么替換一下就行了:
const$ =require('gogocode')constinput =`
const list = [
? {
? ? text: "A策略",
? ? value: 1,
? ? tips: "Atip",
? },
? {
? ? text: "B策略",
? ? value: 2,
? ? tips: "Btip",
? },
? {
? ? text: "C策略",
? ? value: 3,
? ? tips: "Ctip",
? },
];
// ts的類型標(biāo)記,這種正則替換會(huì)被錯(cuò)誤替換的,在 gogocode 里就不會(huì)
const text: string = ''
// 這一段因?yàn)闆]有 value 就不會(huì)被選擇器匹配到,也不會(huì)被錯(cuò)誤替換
const cfg = {
? text: ''
}
`constoutput = $(input2).replace('{ text: $_$1, value: $_$2, $$$ }','{ name: $_$1, id: $_$2, $$$ }').generate();復(fù)制代碼
其中?$_$1?和?$_$2?相當(dāng)于正則中的通配符,但是在這里只會(huì)匹配代碼里有效的 AST 節(jié)點(diǎn),$$$?則可以匹配剩下的節(jié)點(diǎn),有點(diǎn)像 es6 里的?...?,這段代碼匹配出了?text?和?value?這對(duì)應(yīng)的值填給了?name?和?id,剩下的原封不動(dòng)放回去。
而下半部分我刻意加了一些「干擾代碼」,以往我通過字符串替換?text:為?name:的土辦法遇到這樣的就會(huì)誤傷了,但?GoGoCode?不會(huì)。
再看前一段社區(qū)里的一個(gè)例子
正巧,前一段我在掘金看到了文章?像玩 jQuery 一樣玩 AST,里面介紹了一個(gè)用 jscodeshift 進(jìn)行 React jsx 代碼轉(zhuǎn)換的例子:
打算對(duì)這樣一份代碼做修改:
從 @alifd/next 導(dǎo)入改成 antd
轉(zhuǎn)譯前 改成 轉(zhuǎn)譯后
Button 中 type 參數(shù)轉(zhuǎn)換:normal -> default,medium -> middle
Button 中有 text 參數(shù)的改成 type="link"
Button 中warning 參數(shù)的改成 danger
import*asReactfrom'react';importstylesfrom'./index.module.scss';import{ Button }from"@alifd/next";constBtn =() =>{return(
轉(zhuǎn)譯前
大概是這樣:
need-to-insert-img
這種需求其實(shí)挺常見的,原文提供了一個(gè)?基于 jscodeshift 的實(shí)現(xiàn),深入到了 AST 進(jìn)行操作,但如果用?GoGoCode?就會(huì)直觀很多:
// 省略依賴和 inputconstoutput = $(input)? .replace(`import { $$$ } from "@alifd/next"`,`import { $$$ } from "antd"`)? .replace(`<h2>轉(zhuǎn)譯前</h2>`,`<h2>轉(zhuǎn)譯后</h2>`)? .replace(`<Button type="normal" $$$></Button>`,`<Button type="default" $$$></Button>`)? .replace(`<Button size="medium" $$$></Button>`,`<Button size="middle" $$$></Button>`)? .replace(`<Button text $$$></Button>`,`<Button type="link" $$$></Button>`)? .replace(`<Button warning $$$></Button>`,`<Button danger $$$></Button>`)? .generate();復(fù)制代碼
相信你不要講解也能知道這段代碼是要做什么了~
開源了,希望能得到大家的反饋
我覺得這個(gè)項(xiàng)目挺有趣的,可以說是專門給代碼做了一個(gè) replace 程序,小姐姐說我看到得太淺顯,其實(shí)這個(gè)項(xiàng)目不僅僅是 replace 這一招,這個(gè)項(xiàng)目支撐了我們幾個(gè)幾萬行前端工程的架構(gòu)升級(jí)計(jì)劃,就算需求更復(fù)雜也是有辦法搞定的,大家可以關(guān)注我們的賬號(hào)或?qū)?,后面作者?huì)發(fā)表更專業(yè)全面的介紹文章。
小姐姐是我們阿里媽媽 MUX 團(tuán)隊(duì)的葉兮,我們團(tuán)隊(duì)以前有過?iconfont、Rap、MockJS?這樣受到社區(qū)歡迎的項(xiàng)目,這一次我們把?GoGoCode?也開源到 Github:github.com/thx/gogocod…,希望能對(duì)同樣有大量代碼修改需求的朋友有所幫助。
我們很希望了解到大家會(huì)經(jīng)常遇到怎樣的轉(zhuǎn)換難題,如果你用現(xiàn)在?GoGoCode?不方便解決或者出了錯(cuò),希望你能提給我們(issue:https://github.com/thx/gogocode?QQ群:735216094)。
最后:新項(xiàng)目求 star 支持!
Github:https://github.com/thx/gogocode
官網(wǎng):gogocode.io
作者:阿里媽媽前端快爆
鏈接:https://juejin.cn/post/6938601548192677918
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。