項目整理中,完成後補上Github連接
Typescript正普遍成爲前端工程師開發項目的首選,我手頭上有一些使用js編寫Vue項目,最近準備使用ts重寫。項目中單單是頁面的數量就超過100個,更不用提組件的數量,若是對這麼多Vue文件進行一一重寫的話,工程量浩大,而且十分枯燥。其實在此以前也手動轉換過幾個項目,發現重寫過程都是類似的,經過代碼是有可能自動地完成重寫。固然從js轉換到ts下,不可避免地會出現類型問題,如今的自動重寫程序只要求完成重複性地工做,當真的須要類型信息時,仍是須要手動處理。javascript
使用ts來編寫項目時,可使用兩種不一樣的代碼風格:前端
具體應該選擇哪一種方案,見仁見智。我所採用的是方法2。爲何選擇它,若是使用方法1的話重寫起來豈不是很方便?選擇方法2是由於在Vue中大量使用this
關鍵字,使用class形式更加符合直覺——全部的內容都是在class實例上。vue
實現思路就和把大象裝進冰箱同樣簡單:java
關於什麼是抽象語法樹,能夠在網上查找相關資料詳細瞭解(我以爲對於抽象語法樹有必定的瞭解是頗有必要的)。簡單來講,js代碼能夠用一個樹形結構表示,這個樹形結構就是抽象語法樹。例如:git
function foo() { return a + b; }
對應的AST多是下圖這樣的github
<img src="https://static.playground.forzoom.tech/article/2.png" />web
若是但願將代碼中的b修改成c,那麼只須要修改樹中的節點就能夠,例如這樣:typescript
<img src="https://static.playground.forzoom.tech/article/1.png" />npm
以後再生成代碼就能夠了。api
recast是一個能夠方便對代碼和AST進行轉換的庫,能夠幫咱們打開冰箱門和關上冰箱門。
這裏必須再提到兩個概念,分別是estree和ast-types。
estree是將js代碼解析成AST的一個社區標準,也就是,最終生成的AST節點中有哪些值,目前基本上都應該參照estree中的說明進行實現。對這個標準有一些的瞭解,或者說對於編譯原理有必定的瞭解,能夠提升以後修改代碼的效率。
ast-types是recast中所使用的庫,提供了語法樹節點定義、遍歷等功能。ast-types中所定義的類型兼容estree,但實際使用中,感受有時會有一些缺失,例如在某些狀況下,會存在decorators字段不存在的狀況,能夠經過d.ts文件對ast-types中的類型定義進行擴展。
若是對於編譯原理了解的不是那麼清楚的話,那麼也能夠經過recast.parse一些代碼,來了解應該如何寫,以後依葫蘆畫瓢編寫代碼就能夠。
在recast.parse解析代碼時,會默認使用esprima來進行語法解析,esprima(目前爲4.0.1版本)對js新語法已經有了較多的支持,可是對於目前的項目中說,仍是有部分語法沒法解析。爲了解決這個問題,recast也能夠自定義所使用的語法解析器。
我還找到另外兩個語法解析庫,分別是@typescript-eslint/typescript-estree和@babel/parser,其中@typescript-eslint/typescript-estree對於目前vue-property-decorator中使用的修飾器語法並不支持,因此最終選擇@babel/parser。
// 使用自定義的語法解析庫 const ast = recast.parse(jsScript, { parser: { parse(source: string, options: any) { return parser.parse(source, Object.assign(options, { plugins: [ 'estree', // 支持estree格式 'decorators-legacy', // 支持修飾器語法 ], tokens: true, // 必要的參數。默認爲false,解析結果中缺乏tokens內容,當缺乏tokens時,recast將會從新使用esprima進行解析操做 })) }, }, tabWidth: 4, });
在Node中使用fs來完成對於文件的遍歷
import fs from 'fs'; const dir = '/Volumes/Repo2/repo/vue/tourye_web_ts/src'; const dist = '/Volumes/Repo2/repo/vue/tourye_web_ts_ast/src'; const pageDir = dir + '/pages'; const queue = [ pageDir ]; while (queue.length > 0) { const filePath = queue.shift(); if (filePath) { const stats = fs.statSync(filePath); const isDirectory = stats.isDirectory(); if (isDirectory) { // 若是是文件夾,將全部的子路徑加入queue const children = fs.readdirSync(filePath); queue.unshift(...children.map(child => filePath + '/' + child)); } else { // 若是是文件,判斷是不是.vue文件 if (filePath.indexOf('.vue') >= 0) { const output = dist + filePath.substr(dir.length); fs.mkdirSync(path.dirname(output), { recursive: true, mode: 0o755, }); handleVue(filePath, output); // 對於vue文件進行處理 } } } }
既然能夠完成遷移到ts語法的過程,在Vue@3正式發佈以後,可能會考慮是否能將舊代碼,轉換成composition-api的格式。