使用重構件(Codemod)加速JavaScript開發和重構
使用重構件(Codemod)加速 JavaScript 開發
在花園裡耕耘樂趣無窮,但如果除草不勤,最後收穫可能是一團揪心。漏掉一次除草本身可能並無大礙,但積少成多最後會毀掉整座花園。沒有雜草的花園讓維護工作神清氣爽。這個道理對程式碼庫也類似。
我通常討厭除草,經常忘記這事的結果就是一團糟。謝天謝地在程式設計界有像 ESLint 和 SCSS-Lint 這樣的好東西提醒我們勤理程式碼。但是如果面對的是大段大段的歷史程式碼,光是想想要手動調整成百十千萬的空格和逗號,悲傷便逆流成河。
8年來有幾百萬行 JavaScript 程式碼進入 Airbnb 的版本控制系統中。同時,前端界風起雲湧。新功能,新框架,甚至 JavaScript 本身都在快速進化。儘管遵循良好的程式碼風格會讓變革少些疼痛,但還是很容易累積出不再遵循最新”最佳實踐”的巨大程式碼庫。每一處程式碼風格的不一致都是一棵雜草,唯一歸宿就是被剷掉,化作春泥更護花,好讓開發團隊保持高效。來看看我們花園現在的樣子:
我執著於增加團隊效率,也深知保持一致性的程式碼能增速團隊反饋和減少無效溝通。我們最近開始了一個整理程式碼的專案,準備把許多陳舊的 JavaScript 程式碼轉化得符合我們的程式碼風格,亦使我們的程式碼檢驗器有更多用武之地。若全都手動完成,會是件十分無聊和耗時的苦差,所以我們藉助工具幫我們自動化此工作。雖說使用 eslint -fix
是個不錯的開始,但它現在所能有限。儘管他們最近開始接受修復所有規則的PR,也準備構建 JavaScript 的具體語法樹,但等這些功能完成還需要些時間。感謝上蒼我們發現了 Facebook 的 jscodeshift,這是一個重構工具(協助大型程式碼庫的自動化重構)。如果程式碼庫是個花園,那麼 jscodeshift 就像個除草機器人。
此工具將 JavaScript 解析為一棵 抽象語法樹,並在其上進行變換,然後輸出符合指定程式碼風格的新 JavaScript 程式碼。轉換過程是用 JavaScript 本身實現的,所以我們團隊很樂意使用此工具。尋找或是建立轉換程式碼能加速我們乏味的重構,讓我們團隊能夠專注於更有意義的工作。
執行幾個程式碼重構件後,我們的花園整潔了點:
策略
鑑於多數重構件能在一分鐘內處理上千檔案,我發現它是我打發主要工作的等待間隙(例如等程式碼審查)的不錯選擇。它幫我最大化提升了工作效率從而讓我能在更大和更重要的專案中有所建樹。
大規模重構主要面臨四大挑戰。溝通、正確性、程式碼審查以及衝突合併。我採取以下策略來應對這些挑戰。
重構件不總是能產出我需要的結果,因此對其結果的審查和改動十分重要。以下命令在跑完重構件後很有用:
git diff
git add --patch
git checkout --patch
保持每個提交和 PR 在小的體量是好的做法,對於重構件也不例外。我通常一段時間內進行一類重構,減少程式碼審查和衝突合併的麻煩。我亦經常讓重構件自動提交重構結果,而後若有必要,再手動清理。這樣在衍合分支時解決衝突會輕鬆點,因為我可以使用
git checkout --ours path/to/conflict
然後在那個檔案上再執行一次重構件,之後也不會弄亂我自己的手動提交。
有時重構件生成了很大的變動,我覺得在此情況下根據目錄或檔名來分成數次提交或 PR 會比較好。例如,一個提交重構 .js 檔案,另一個提交重構.jsx 檔案。這樣之後程式碼審查和衝突合併會相對輕鬆一點。謹遵 Unix 哲學,分批進行檔案重構簡單到僅需調整 find
命令的引數:
find app/assets/javascripts -name *.jsx -not -path */vendor/* |
xargs jscodeshift -t ~/path/to/transform.js
為避免和別人的程式碼衝突,我通常在週五早上才推送我的重構件生成的提交,然後週一趕在大家開始工作之前進行衍合和合並。這樣其他人週末放假前不被你的重構件阻礙,能好好整理自己的工作成果。
我們用得順手的重構件
雖然此工具還比較新,已然有了一些實用的重構件。以下是一些我們成功上手了的。
輕量級重構件
以下是些用著不那麼痛苦的,立刻上手感受成效。
js-codemod/arrow-function: 謹慎地把函式轉為箭頭函式
使用前:
[1, 2, 3].map(function(x) {
return x * x;
}.bind(this));
使用後:
[1, 2, 3].map(x => x * x);
js-codemod/no-vars: 將 var`_ 安全轉化為 _
const_ 或 _
let`。
使用前:
var belong = `anywhere`;
使用後:
const belong = `anywhere`;
js-codemod/object-shorthand: 把物件字面量轉為 ES6 的簡寫表示。
使用前:
const things = {
belong: belong,
anywhere: function() {},
};
使用後:
const things = {
belong,
anywhere() {},
};
js-codemod/unchain-variables: 分離連續宣告的變數。
使用前:
const belong = `anywhere`, welcome = `home`;
使用後:
const belong = `anywhere`;
const welcome = `home`;
js-codemod/unquote-properties: 移除物件屬性的引號。
使用前:
const things = {
`belong`: `anywhere`,
};
使用後:
const things = {
belong: `anywhere`,
};
重量級重構件
以下重構件或是改動很多程式碼引發合併和衝突之痛,或是需要更多後續的手動更改以保證程式碼還能看得下去。
react-codemod/class: 把 React.createClass
轉為 ES6 class 的實現。
此重構件在有 mixin 的時候不會變換,在類似於 propTypes
、預設 props 和 initial state 定義這樣的必要轉換做得很好,還能將事件回撥函式繫結到構造器上。
使用前:
const BelongAnywhere = React.createClass({
// ...
});
使用後:
class BelongAnywhere extends React.Component {
// ...
}
react-codemod/sort-comp: 根據 ESLint react/sort-comp rule 重新組織 React component 的方法宣告順序。
這個會調整大量程式碼,git 不會自動合併衝突。我覺得在使用此重構件前最好最好跟隊友打個招呼,在不太容易發生衝突的時候(例如週末)進行重構。當我衍合此重構的提交且遇上衝突的時候,我會:
git checkout --ours path/to/conflict
然後再執行一次重構件。
使用前:
class BelongAnywhere extends React.Component {
render() {
return <div>Belong Anywhere</div>;
}
componentWillMount() {
console.log(`Welcome home`);
}
}
使用後:
class BelongAnywhere extends React.Component {
componentWillMount() {
console.log(`Welcome home`);
}
render() {
return <div>Belong Anywhere</div>;
}
}
js-codemod/template-literals: 把字串的串聯轉換為字串模板字面量表示。
因為我們多處用到字串串聯,而且這個重構件盡其所能把所有字串都轉成模板,我發現很多轉換結果其實並不合理。我之所以這個重構件放到”重量級”列表裡,是因為它會改動很多檔案,而且之後我們還得進行大量的手動修改才能得到滿意的結果。
使用前:
const belong = `anywhere `+ welcomeHome;
使用後:
const belong = `anywhere ${welcomeHome}`;
資源
若你想寫自己的重構件,或是看看它能做什麼,可以看下下面的資源。
- 逐步改進複雜系統:來自 Christoph Pojer 於 JSConf EU 2015 上關於 Facebook 的重構件的演講。(亦可見高效的 JavaScript 重構件)。
- 如何寫重構件: 帶你寫一個把字串串聯轉化為字串模板字面量的重構件的教程。
- AST 探索: 可檢視由多種語法分析程式產生的 AST 的工具。好東西,可以檢視你想轉換的程式碼的 AST。
- NFL C重構件: 海量程式碼遷移: 關於 NFL 如何使用重構件的一個使用案例。
- react-codemod: 一系列關於 React 的重構件。
- js-codemod: 一系列常用的 JavaScript 重構件。
影響
在使用了一些現成的和我們自己寫的並貢獻給社群的重構件之後,我們的舊程式碼質量獲得很大的提升。我不費吹灰之力便重構了40000行程式碼,將舊程式碼調整至符合 ES6 程式碼風格。花園煥然一新,我們之後的工作也更有效率和樂趣。
使用已有的重構件僅是牛刀小試,只有在你拿起鍵盤寫出自己的重構件時,真正的能量才會釋放。無論是對程式碼風格重構,或是對失效 API 的調整,重構件都能大顯身手,你可以盡情想象發揮。這些技術值得學習投入,能省下你和使用你的專案使用者很多時間精力。
相關文章
- Laravel 高效開發模板 (重構)Laravel
- 程式碼重構與單元測試——重構6:使用“多型”取代條件表示式(九)多型
- 重構你的javascript程式碼JavaScript
- 程式碼重構--大話重構
- 重構
- 複合條件查詢的重構
- 程式碼重構之法——方法重構分析
- PyCharm使用技巧:PyCharm重構PyCharm
- JavaScript進階之路——認識和使用Promise,重構你的Js程式碼JavaScriptPromiseJS
- 原型重構原型
- CSS重構CSS
- 重構之路:webpack區分生產環境和開發環境Web開發環境
- 基於ArkUI框架開發-ImageKnife渲染層重構UI框架
- 頁面重構WEEX開發問題總結
- 使用策略模式重構電商折扣和支付場景模式
- 低程式碼平臺搭建CRM 加速重構業務模式模式
- 程式碼重構:類重構的 8 個小技巧
- .NET重構(型別碼的設計、重構方法)型別
- .NET重構—單元測試的程式碼重構
- “整潔架構”和商家前端的重構之路架構前端
- b/s架構和c/s架構(重點)架構
- 重視B/S架構系統的發展和開發設計理念架構
- 出庫重構
- 前端重構感想前端
- 程式碼重構
- 重構與模式模式
- Kruskal 重構樹
- Kruskal重構樹
- 程式碼的壞味道和重構
- 螞蟻金服 mPaaS 模組化開發與架構重構深度解析架構
- APP解構重構:勿忘初心APP
- 專案重構之架構架構
- 重構:你可能不知道的重構場景
- 重構改善既有的程式碼設計(重構原則)
- 程式碼重構:函式重構的 7 個小技巧函式
- 為什麼要重構?深入探討重構的原則、範圍和時機
- 程式碼重構與單元測試——“提取方法”重構(三)
- 重構、重新架構、再設計與重寫的區別架構