溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

用 Babel 編譯 Typescript 的原因

發(fā)布時間:2021-06-16 10:49:18 來源:億速云 閱讀:562 作者:chen 欄目:web開發(fā)

這篇文章主要介紹“用 Babel 編譯 Typescript 的原因”,在日常操作中,相信很多人在用 Babel 編譯 Typescript 的原因問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”用 Babel 編譯 Typescript 的原因”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

typescript 給 javascript 擴展了類型的語法和語義,讓 js  代碼達到了靜態(tài)類型語言級別的類型安全,之前只能在運行時發(fā)現(xiàn)的類型不安全的問題,現(xiàn)在能在編譯期間發(fā)現(xiàn)了,所以大項目越來越多的選擇用 typescript  來寫。除此之外,typescript 還能夠配合 ide 做更好的智能提示,這也是用 typescript 的一個理由。

類型安全:如果一個類型的變量賦值給它不兼容類型的值,這就是類型不安全,如果一個類型的對象,調用了它沒有的方法,這也是類型不安全。反之,就是類型安全。類型安全就是變量的賦值、對象的函數(shù)調用都是在類型支持的范圍內。

最開始 typescript 代碼只有自帶的 tyepscript compiler(tsc)能編譯,編譯不同版本的 typescript  代碼需要用不同版本的 tsc,通過配置 tsconfig.json 來指定如何編譯。

但是 tsc 編譯 ts 代碼為 js 是有問題的:

tsc 不支持很多還在草案階段的語法,這些語法都是通過 babel 插件來支持的,所以很多項目的工具鏈是用 tsc 編譯一遍 ts 代碼,之后再由  babel 編譯一遍。這樣編譯鏈路長,而且生成的代碼也不夠精簡。

所以,typescript 找 babel 團隊合作,在 babel7 中支持了 typescript 的編譯,可以通過插件來指定 ts 語法的編譯。比如  api 中是這樣用:

const parser = require('@babel/parser');  parser.parse(sourceCode, {     plugins: ['typescript'] });

這個插件是 typescript 團隊與 babel 團隊合作了一年的成果。

但是,這個插件真的能支持所有 typescript 代碼么?答案是否定的。

我們來看一下 babel 不支持哪些 ts 語法,為什么不支持。

babel 能編譯所有 typescript 代碼么?

babel 的編譯流程是這樣的:

用 Babel 編譯 Typescript 的原因

  • parser: 把源碼 parse 成 ast

  • traverse:遍歷 ast,生成作用域信息和 path,調用各種插件來對 ast 進行轉換

  • generator:把轉換以后的 ast 打印成目標代碼,并生成 sourcemap

而 typescript compiler 的編譯流程是這樣的:

用 Babel 編譯 Typescript 的原因

  • scanner + parser:分詞和組裝 ast,從源碼到 ast 的過程

  • binder + checker:生成作用域信息,進行類型推導和檢查

  • transform:對經(jīng)過類型檢查之后的 ast 進行轉換

emitter:打印 ast 成目標代碼,生成 sourcemap 和類型聲明文件(根據(jù)配置)

其實 babel 的編譯階段和 tsc 的編譯階段是類似的,只是 tsc 多了一個 checker,其余的部分沒什么區(qū)別。

  • babel 的 parser 對應 tsc 的 scanner + parser

  • babel 的 traverse 階段 對應 tsc 的 binder + transform

  • babel 的 generator 對應 tsc 的 emitter

那么能不能基于 babel 的插件在 traverse 的時候實現(xiàn) checker 呢?

答案是不可以。

因為 tsc 的類型檢查是需要拿到整個工程的類型信息,需要做類型的引入、多個文件的 namespace、enum、interface 等的合并,而  babel 是單個文件編譯的,不會解析其他文件的信息。所以做不到和 tsc 一樣的類型檢查。

一個是在編譯過程中解析多個文件,一個是編譯過程只針對單個文件,流程上的不同,導致 babel 無法做 tsc 的類型檢查。

那么 babel 是怎么編譯 typescript 的呢?

其實 babel 只是能夠 parse ts 代碼成 ast,不會做類型檢查,會直接把類型信息去掉,然后打印成目標代碼。

這導致了有一些 ts 語法是 babel 所不支持的:

  • const enum 不支持。const enum 是在編譯期間把 enum 的引用替換成具體的值,需要解析類型信息,而 babel  并不會解析,所以不支持。可以用相應的插件把 const enum 轉成 enum。

  • namespace 部分支持。不支持 namespace 的跨文件合并,不支持導出非 const 的值。這也是因為 babel  不會解析類型信息且是單文件編譯。

上面兩種兩個是因為編譯方式的不同導致的不支持。

  • export = import = 這種 ts 特有語法不支持,可以通過插件轉為 esm

  • 如果開啟了 jsx 編譯,那么

    aa 這種類型斷言不支持,通過 aa as string  來替代。這是因為這兩種語法有沖突,在兩個語法插件(jsx、typescript)里,解決沖突的方式就是用 as 代替。

這四種就是 babel 不支持的 ts 語法,其實影響并不大,這幾個特性不用就好了。

結論:babel 不能編譯所有 typescript 代碼,但是除了 namespace 的兩個特性外,其余的都可以做編譯。

babel 是可以編譯 typescript 代碼,那么為什么要用 babel 編譯呢?

為什么要用 babel 編譯 typescript 代碼?

babel 編譯 typescript 代碼有 3 個主要的優(yōu)點:

產(chǎn)物體積更小

tsc

tsc 如何配置編譯目標呢?

在 compilerOptions 里面配置 target,target 設置目標語言版本

{     compilerOptions: {         target: "es5" // es3、es2015     } }

typescript 如何引入 polyfill 呢?

在入口文件里面引入 core-js.

import 'core-js';

babel7

babel7 是如何配置編譯目標呢?

在 preset-env 里面指定 targets,直接指定目標運行環(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 用哪個(corejs2 還是  corejs3),如何引入(entry 在入口引入 ,usage 每個模塊單獨引入用到的)。

{     presets: [         [             "@babel/preset-env",             {                 targets: "last 1 version,> 1%,not dead",                 corejs: 3,                 useBuiltIns: 'usage'             }         ]     ] }

這樣可以根據(jù) @babel/compat-data 的數(shù)據(jù)來針對的做語法轉換和 api 的 polyfill:

先根據(jù) targets 查出支持的目標環(huán)境的版本,再根據(jù)目標環(huán)境的版本來從所有特性中過濾支持的,剩下的就是不支持的特性。只對這些特性做轉換和  polyfill 即可。

用 Babel 編譯 Typescript 的原因

而且 babel 還可以通過 @babel/plugin-transform-runtime 來把全局的 corejs 的 import  轉成模塊化引入的方式。

顯然,用 babel 編譯 typescript 從產(chǎn)物上看有兩個優(yōu)點:

  • 能夠做更精準的按需編譯和 polyfill,產(chǎn)物體積更小

  • 能夠通過插件來把 polyfill 變成模塊化的引入,不污染全局環(huán)境

從產(chǎn)物來看,babel 勝。

支持的語言特性

typescript 默認支持很多 es 的特性,但是不支持還在草案階段的特性,babel 的 preset-env 支持所有標準特性,還可以通過  proposal 來支持更多還未進入標準的特性。

{     plugins: ['@babel/proposal-xxx'],     presets: ['@babel/presets-env', {...}] }

從支持的語言特性來看,babel 勝。

編譯速度

tsc 會在編譯過程中進行類型檢查,類型檢查需要綜合多個文件的類型信息,要對 AST 做類型推導,比較耗時,而 babel  不做類型檢查,所以編譯速度會快很多。

從編譯速度來看, babel 勝。

總之,從編譯產(chǎn)物大小(主要)、支持的語言特性、編譯速度來看,babel 完勝。

但是,babel 不做類型檢查,那怎么類型檢查呢?

babel 和 tsc 的結合

babel 可以編譯生成更小的產(chǎn)物,有更快的編譯速度和更多的特性支持,所以我們選擇用 babel 編譯 typescript  代碼。但是類型檢查也是需要的,可以在 npm scripts 中配一個命令:

{     "scripts": {         "typeCheck": "tsc --noEmit"     } }

這樣在需要進行類型檢查的時候單獨執(zhí)行一下 npm run typeCheck 就行了,但最好在 git commit 的 hook 里(通過 husky  配置)再執(zhí)行一次強制的類型檢查。

總結

typescript 給 js 擴展了靜態(tài)類型的支持,使得代碼能夠在編譯期間檢查出賦值類型不匹配、調用了沒有的方法等錯誤,保證類型安全。

除了 tsc 之外,babel7 也能編譯 typescript 代碼了,這是兩個團隊合作一年的結果。

但是 babel 因為單文件編譯的特點,做不了和 tsc 的多文件類型編譯一樣的效果,有幾個特性不支持(主要是 namespace 的跨文件合并、導出非  const 的值),不過影響不大,整體是可用的。

babel 做代碼編譯,還是需要用 tsc 來進行類型檢查,單獨執(zhí)行 tsc --noEmit 即可。

babel 編譯 ts 代碼,相比 tsc 有很多優(yōu)點:產(chǎn)物體積更小,支持更多特性,編譯速度更快。所以用 babel 編譯 typescript  是一個更好的選擇。

到此,關于“用 Babel 編譯 Typescript 的原因”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI