這篇文章主要介紹“為什么要用Babel編譯Typescript”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“為什么要用Babel編譯Typescript”文章能幫助大家解決問(wèn)題。
專注于為中小企業(yè)提供成都網(wǎng)站建設(shè)、成都做網(wǎng)站服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)柳江免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千余家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。typescript 給 javascript 擴(kuò)展了類型的語(yǔ)法和語(yǔ)義,讓 js 代碼達(dá)到了靜態(tài)類型語(yǔ)言級(jí)別的類型安全,之前只能在運(yùn)行時(shí)發(fā)現(xiàn)的類型不安全的問(wèn)題,現(xiàn)在能在編譯期間發(fā)現(xiàn)了,所以大項(xiàng)目越來(lái)越多的選擇用 typescript 來(lái)寫(xiě)。除此之外,typescript 還能夠配合 ide 做更好的智能提示,這也是用 typescript 的一個(gè)理由。
類型安全:如果一個(gè)類型的變量賦值給它不兼容類型的值,這就是類型不安全,如果一個(gè)類型的對(duì)象,調(diào)用了它沒(méi)有的方法,這也是類型不安全。反之,就是類型安全。類型安全就是變量的賦值、對(duì)象的函數(shù)調(diào)用都是在類型支持的范圍內(nèi)。
最開(kāi)始 typescript 代碼只有自帶的 tyepscript compiler(tsc)能編譯,編譯不同版本的 typescript 代碼需要用不同版本的 tsc,通過(guò)配置 tsconfig.json 來(lái)指定如何編譯。
但是 tsc 編譯 ts 代碼為 js 是有問(wèn)題的:
tsc 不支持很多還在草案階段的語(yǔ)法,這些語(yǔ)法都是通過(guò) babel 插件來(lái)支持的,所以很多項(xiàng)目的工具鏈?zhǔn)怯?tsc 編譯一遍 ts 代碼,之后再由 babel 編譯一遍。這樣編譯鏈路長(zhǎng),而且生成的代碼也不夠精簡(jiǎn)。
所以,typescript 找 babel 團(tuán)隊(duì)合作,在 babel7 中支持了 typescript 的編譯,可以通過(guò)插件來(lái)指定 ts 語(yǔ)法的編譯。比如 api 中是這樣用:
const parser = require('@babel/parser'); parser.parse(sourceCode, { plugins: ['typescript'] });
這個(gè)插件是 typescript 團(tuán)隊(duì)與 babel 團(tuán)隊(duì)合作了一年的成果。
但是,這個(gè)插件真的能支持所有 typescript 代碼么?答案是否定的。
我們來(lái)看一下 babel 不支持哪些 ts 語(yǔ)法,為什么不支持。
babel 的編譯流程是這樣的:
parser: 把源碼 parse 成 ast
traverse:遍歷 ast,生成作用域信息和 path,調(diào)用各種插件來(lái)對(duì) ast 進(jìn)行轉(zhuǎn)換
generator:把轉(zhuǎn)換以后的 ast 打印成目標(biāo)代碼,并生成 sourcemap
而 typescript compiler 的編譯流程是這樣的:
scanner + parser:分詞和組裝 ast,從源碼到 ast 的過(guò)程
binder + checker:生成作用域信息,進(jìn)行類型推導(dǎo)和檢查
transform:對(duì)經(jīng)過(guò)類型檢查之后的 ast 進(jìn)行轉(zhuǎn)換
emitter:打印 ast 成目標(biāo)代碼,生成 sourcemap 和類型聲明文件(根據(jù)配置)
其實(shí) babel 的編譯階段和 tsc 的編譯階段是類似的,只是 tsc 多了一個(gè) checker,其余的部分沒(méi)什么區(qū)別。
babel 的 parser 對(duì)應(yīng) tsc 的 scanner + parser
babel 的 traverse 階段 對(duì)應(yīng) tsc 的 binder + transform
babel 的 generator 對(duì)應(yīng) tsc 的 emitter
那么能不能基于 babel 的插件在 traverse 的時(shí)候?qū)崿F(xiàn) checker 呢?
答案是不可以。
因?yàn)?tsc 的類型檢查是需要拿到整個(gè)工程的類型信息,需要做類型的引入、多個(gè)文件的 namespace、enum、interface 等的合并,而 babel 是單個(gè)文件編譯的,不會(huì)解析其他文件的信息。所以做不到和 tsc 一樣的類型檢查。
一個(gè)是在編譯過(guò)程中解析多個(gè)文件,一個(gè)是編譯過(guò)程只針對(duì)單個(gè)文件,流程上的不同,導(dǎo)致 babel 無(wú)法做 tsc 的類型檢查。
那么 babel 是怎么編譯 typescript 的呢?
其實(shí) babel 只是能夠 parse ts 代碼成 ast,不會(huì)做類型檢查,會(huì)直接把類型信息去掉,然后打印成目標(biāo)代碼。
這導(dǎo)致了有一些 ts 語(yǔ)法是 babel 所不支持的:
const enum 不支持。const enum 是在編譯期間把 enum 的引用替換成具體的值,需要解析類型信息,而 babel 并不會(huì)解析,所以不支持??梢杂孟鄳?yīng)的插件把 const enum 轉(zhuǎn)成 enum。
namespace 部分支持。不支持 namespace 的跨文件合并,不支持導(dǎo)出非 const 的值。這也是因?yàn)?babel 不會(huì)解析類型信息且是單文件編譯。
上面兩種兩個(gè)是因?yàn)榫幾g方式的不同導(dǎo)致的不支持。
export = import = 這種 ts 特有語(yǔ)法不支持,可以通過(guò)插件轉(zhuǎn)為 esm
如果開(kāi)啟了 jsx 編譯,那么
這四種就是 babel 不支持的 ts 語(yǔ)法,其實(shí)影響并不大,這幾個(gè)特性不用就好了。
結(jié)論:babel 不能編譯所有 typescript 代碼,但是除了 namespace 的兩個(gè)特性外,其余的都可以做編譯。
babel 是可以編譯 typescript 代碼,那么為什么要用 babel 編譯呢?
babel 編譯 typescript 代碼有 3 個(gè)主要的優(yōu)點(diǎn):
產(chǎn)物體積更小
tsc
tsc 如何配置編譯目標(biāo)呢?
在 compilerOptions 里面配置 target,target 設(shè)置目標(biāo)語(yǔ)言版本
{ compilerOptions: { target: "es5" // es3、es2015 } }
typescript 如何引入 polyfill 呢?
在入口文件里面引入 core-js.
import 'core-js';
babel7
babel7 是如何配置編譯目標(biāo)呢?
在 preset-env 里面指定 targets,直接指定目標(biāo)運(yùn)行環(huán)境(瀏覽器、node)版本,或者指定 query 字符串,由 browserslist 查出具體的版本。
{ presets: [ [ "@babel/preset-env", { targets: { chrome: 45 } } ] ] }
{ presets: [ [ "@babel/preset-env", { targets: "last 1 version,> 1%,not dead" } ] ] }
babel7 如何引入 polyfill 呢?
也是在 @babel/preset-env 里面配置,除了指定 targets 之外,還要指定 polyfill 用哪個(gè)(corejs2 還是 corejs3),如何引入(entry 在入口引入 ,usage 每個(gè)模塊單獨(dú)引入用到的)。
{ presets: [ [ "@babel/preset-env", { targets: "last 1 version,> 1%,not dead", corejs: 3, useBuiltIns: 'usage' } ] ] }
這樣可以根據(jù) @babel/compat-data 的數(shù)據(jù)來(lái)針對(duì)的做語(yǔ)法轉(zhuǎn)換和 api 的 polyfill:
先根據(jù) targets 查出支持的目標(biāo)環(huán)境的版本,再根據(jù)目標(biāo)環(huán)境的版本來(lái)從所有特性中過(guò)濾支持的,剩下的就是不支持的特性。只對(duì)這些特性做轉(zhuǎn)換和 polyfill 即可。
而且 babel 還可以通過(guò) @babel/plugin-transform-runtime 來(lái)把全局的 corejs 的 import 轉(zhuǎn)成模塊化引入的方式。
顯然,用 babel 編譯 typescript 從產(chǎn)物上看有兩個(gè)優(yōu)點(diǎn):
能夠做更精準(zhǔn)的按需編譯和 polyfill,產(chǎn)物體積更小
能夠通過(guò)插件來(lái)把 polyfill 變成模塊化的引入,不污染全局環(huán)境
從產(chǎn)物來(lái)看,babel 勝。
typescript 默認(rèn)支持很多 es 的特性,但是不支持還在草案階段的特性,babel 的 preset-env 支持所有標(biāo)準(zhǔn)特性,還可以通過(guò) proposal 來(lái)支持更多還未進(jìn)入標(biāo)準(zhǔn)的特性。
{ plugins: ['@babel/proposal-xxx'], presets: ['@babel/presets-env', {...}] }
從支持的語(yǔ)言特性來(lái)看,babel 勝。
tsc 會(huì)在編譯過(guò)程中進(jìn)行類型檢查,類型檢查需要綜合多個(gè)文件的類型信息,要對(duì) AST 做類型推導(dǎo),比較耗時(shí),而 babel 不做類型檢查,所以編譯速度會(huì)快很多。
從編譯速度來(lái)看, babel 勝。
總之,從編譯產(chǎn)物大小(主要)、支持的語(yǔ)言特性、編譯速度來(lái)看,babel 完勝。
但是,babel 不做類型檢查,那怎么類型檢查呢?
babel 可以編譯生成更小的產(chǎn)物,有更快的編譯速度和更多的特性支持,所以我們選擇用 babel 編譯 typescript 代碼。但是類型檢查也是需要的,可以在 npm scripts 中配一個(gè)命令:
{ "scripts": { "typeCheck": "tsc --noEmit" } }
這樣在需要進(jìn)行類型檢查的時(shí)候單獨(dú)執(zhí)行一下 npm run typeCheck 就行了,但最好在 git commit 的 hook 里(通過(guò) husky 配置)再執(zhí)行一次強(qiáng)制的類型檢查。
關(guān)于“為什么要用Babel編譯Typescript”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。