偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

編譯TS 代碼用TSC 還是Babel?

開發(fā) 前端
babel 和 tsc 的編譯流程大同小異,都有把源碼轉(zhuǎn)換成 AST 的 Parser,都會(huì)做語義分析(作用域分析)和 AST 的 transform,最后都會(huì)用 Generator(或者 Emitter)把 AST 打印成目標(biāo)代碼并生成 sourcemap。

編譯 TypeScript 代碼用什么編譯器?

那還用說,肯定是 ts 自帶的 compiler 呀。

但其實(shí) babel 也能編譯 ts 代碼,那用 babel 和 tsc 編譯 ts 代碼有什么區(qū)別呢?

我們分別來看一下:

tsc 的編譯流程

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

源碼要先用 Scanner 進(jìn)行詞法分析,拆分成一個(gè)個(gè)不能細(xì)分的單詞,叫做 token。

然后用 Parser 進(jìn)行語法分析,組裝成抽象語法樹(Abstract Syntax Tree)AST。

之后做語義分析,包括用 Binder 進(jìn)行作用域分析,和有 Checker 做類型檢查。如果有類型的錯(cuò)誤,就是在 Checker 這個(gè)階段報(bào)的。

如果有 Transformer 插件(tsc 支持 custom transform),會(huì)在 Checker 之后調(diào)用,可以對 AST 做各種增刪改。

類型檢查通過后就會(huì)用 Emmiter 把 AST 打印成目標(biāo)代碼,生成類型聲明文件 d.ts,還有 sourcemap。

sourcemap 的作用是映射源碼和目標(biāo)代碼的代碼位置,這樣調(diào)試的時(shí)候打斷點(diǎn)可以定位到相應(yīng)的源碼,線上報(bào)錯(cuò)的時(shí)候也能根據(jù) sourcemap 定位到源碼報(bào)錯(cuò)的位置。

tsc 生成的 AST 可以用 astexplorer.net 可視化的查看:

生成的目標(biāo)代碼和 d.ts 和報(bào)錯(cuò)信息也可以用 ts playground 來直接查看:

大概了解了 tsc 的編譯流程,我們再來看下 babel 的:

babel 的編譯流程

babel 的編譯流程是這樣的:

源碼經(jīng)過 Parser 做詞法分析和語法分析,生成 token 和 AST。

AST 會(huì)做語義分析生成作用域信息,然后會(huì)調(diào)用 Transformer 進(jìn)行 AST 的轉(zhuǎn)換。

最后會(huì)用 Generator 把 AST 打印成目標(biāo)代碼并生成 sourcemap。

babel 的 AST 和 token 也可以用 astexplorer.net 可視化的查看:

如果想看到 tokens,需要點(diǎn)開設(shè)置,開啟 tokens:

而且 babel 也有 playground(babel 的叫 repl) 可以直接看編譯之后生成的代碼:

其實(shí)對比下 tsc 的編譯流程,區(qū)別并不大:

Parser 對應(yīng) tsc 的 Scanner 和 Parser,都是做詞法分析和語法分析,只不過 babel 沒有細(xì)分。

Transform 階段做語義分析和代碼轉(zhuǎn)換,對應(yīng) tsc 的 Binder 和 Transformer。只不過 babel 不會(huì)做類型檢查,沒有 Checker。

Generator 做目標(biāo)代碼和 sourcemap 的生成,對應(yīng) tsc 的 Emitter。只不過因?yàn)闆]有類型信息,不會(huì)生成 d.ts。

對比兩者的編譯流程,會(huì)發(fā)現(xiàn) babel 除了不會(huì)做類型檢查和生成類型聲明文件外,tsc 能做的事情,babel 都能做。

看起來好像是這樣的,但是 babel 和 tsc 實(shí)現(xiàn)這些功能是有區(qū)別的:

babel 和 tsc 的區(qū)別

拋開類型檢查和生成 d.ts 這倆 babel 不支持的功能不談,我們看下其他功能的對比:

分別對比下語法支持和代碼生成兩方面:

語法支持

tsc 默認(rèn)支持最新的 es 規(guī)范的語法和一些還在草案階段的語法(比如 decorators),想支持新語法就要升級 tsc 的版本。

babel 是通過 @babel/preset-env 按照目標(biāo)環(huán)境 targets 的配置自動(dòng)引入需要用到的插件來支持標(biāo)準(zhǔn)語法,對于還在草案階段的語法需要單獨(dú)引入 @babel/proposal-xx 的插件來支持。

所以如果你只用標(biāo)準(zhǔn)語法,那用 tsc 或者 babel 都行,但是如果你想用一些草案階段的語法,tsc 可能很多都不支持,而 babel 卻可以引入 @babel/poposal-xx 的插件來支持。

從支持的語法特性上來說,babel 更多一些。

代碼生成

tsc 生成的代碼沒有做 polyfill 的處理,想做兼容處理就需要在入口引入下 core-js(polyfill 的實(shí)現(xiàn))。

import "core-js";
Promise.resolve;

babel 的 @babel/preset-env 可以根據(jù) targets 的配置來自動(dòng)引入需要的插件,引入需要用到的 core-js 模塊,

引入方式可以通過 useBuiltIns 來配置:

entry 是在入口引入根據(jù) targets 過濾出的所有需要用的 core-js。

usage 則是每個(gè)模塊按照使用到了哪些來按需引入。

module.exports = {
presets: [
[
'@babel/preset-typescript',
'@babel/preset-env',
{
targets: '目標(biāo)環(huán)境',
useBuiltIns: 'entry' // ‘usage
}
]
]
}

此外,babel 會(huì)注入一些 helper 代碼,可以通過 @babel/plugin-transform-runtime 插件抽離出來,從 @babel/runtime 包引入。

使用 transform-runtime 之前:

使用 transform-runtime 之后:

(transform runtime 顧名思義就是 transform to runtime,轉(zhuǎn)換成從 runtime 包引入 helper 代碼的方式)

所以一般babel 都會(huì)這么配:

module.exports = {
presets: [
[
'@babel/preset-typescript',
'@babel/preset-env',
{
targets: '目標(biāo)環(huán)境',
useBuiltIns: 'usage' // ‘entry
}
]
],
plugins: [ '@babel/plugin-transform-runtime']
}

當(dāng)然,這里不是講 babel 怎么配置,我們繞回主題,babel 和 tsc 生成代碼的區(qū)別:

tsc 生成的代碼沒有做 polyfill 的處理,需要全量引入 core-js,而 babel 則可以用 @babel/preset-env 根據(jù) targets 的配置來按需引入 core-js 的部分模塊,所以生成的代碼體積更小。

看起來用 babel 編譯 ts 代碼全是優(yōu)點(diǎn)?

也不全是,babel 有一些 ts 語法并不支持:

babel 不支持的 ts 語法

babel 是每個(gè)文件單獨(dú)編譯的,而 tsc 不是,tsc 是整個(gè)項(xiàng)目一起編譯,會(huì)處理類型聲明文件,會(huì)做跨文件的類型聲明合并,比如 namespace 和 interface 就可以跨文件合并。

所以 babel 編譯 ts 代碼有一些特性是沒法支持的:

const enum 不支持

enum 編譯之后是這樣的:

而 const enum 編譯之后是直接替換用到 enum 的地方為對應(yīng)的值,是這樣的:

const enum 是在編譯期間把 enum 的引用替換成具體的值,需要解析類型信息,而 babel 并不會(huì)解析,所以它會(huì)把 const enum 轉(zhuǎn)成 enum 來處理:

namespace 部分支持:不支持 namespace 的合并,不支持導(dǎo)出非 const 的值

比如這樣一段 ts 代碼:

namespace Guang {
export const name = 'guang';
}
namespace Guang {
export const name2 = name;
}
console.log(Guang.name2);

按理說 Guang.name2 是 'dong',因?yàn)?ts 會(huì)自動(dòng)合并同名 namespace。

ts 編譯之后的代碼是這樣的:

都掛到了 Guang 這個(gè)對象上,所以 name2 就能取到 name 的值。

而 babel 對每個(gè) namespace 都是單獨(dú)處理,所以是這樣的:

因?yàn)椴粫?huì)做 namespace 的合并,所以 name 為 undefined。

還有 namespace 不支持導(dǎo)出非 const 的值。

ts 的 namespace 是可以導(dǎo)出非 const 的值的,后面可以修改:

但是 babel 并不支持:

原因也是因?yàn)椴粫?huì)做 namespace 的解析,而 namespace 是全局的,如果在另一個(gè)文件改了 namespace 導(dǎo)出的值,babel 并不能處理。所以不支持對 namespace 導(dǎo)出的值做修改。

除此以外,還有一些語法也不支持:

部分語法不支持

像 export = import = 這種過時(shí)的模塊語法并不支持:

開啟了 jsx 編譯之后,不能用尖括號的方式做類型斷言:

我們知道,ts 是可以做類型斷言來修改某個(gè)類型到某個(gè)類型的,用 as xx 或者尖括號的方式。

但是如果開啟了 jsx 編譯之后,尖括號的形式會(huì)和 jsx 的語法沖突,所以就不支持做類型斷言了:

tsc 都不支持,babel 當(dāng)然也是一樣:

babel 不支持 ts 這些特性,那是否可以用 babel 編譯 ts 呢?

babel 還是 tsc?

babel 不支持 const enum(會(huì)作為 enum 處理),不支持 namespace 的跨文件合并,導(dǎo)出非 const 的值,不支持過時(shí)的 export = import = 的模塊語法。

這些其實(shí)影響并不大,只要代碼里沒用到這些語法,完全可以用 babel 來編譯 ts。

babel 編譯 ts 代碼的優(yōu)點(diǎn)是可以通過插件支持更多的語言特性,而且生成的代碼是按照 targets 的配置按需引入 core-js 的,而 tsc 沒做這方面的處理,只能全量引入。

而且 tsc 因?yàn)橐鲱愋蜋z查所以是比較慢的,而 babel 不做類型檢查,編譯會(huì)快很多。

那用 babel 編譯,就不做類型檢查了么?

可以用 tsc --noEmit 來做類型檢查,加上 noEmit選項(xiàng)就不會(huì)生成代碼了。

如果你要生成 d.ts,也要單獨(dú)跑下 tsc 編譯。

總結(jié)

babel 和 tsc 的編譯流程大同小異,都有把源碼轉(zhuǎn)換成 AST 的 Parser,都會(huì)做語義分析(作用域分析)和 AST 的 transform,最后都會(huì)用 Generator(或者 Emitter)把 AST 打印成目標(biāo)代碼并生成 sourcemap。

但是 babel 不做類型檢查,也不會(huì)生成 d.ts 文件。

tsc 支持最新的 es 標(biāo)準(zhǔn)特性和部分草案的特性(比如 decorator),而 babel 通過 @babel/preset-env 支持所有標(biāo)準(zhǔn)特性,也可以通過 @babel/proposal-xx 來支持各種非標(biāo)準(zhǔn)特性,支持的語言特性上 babel 更強(qiáng)一些。

tsc 沒有做 polyfill 的處理,需要全量引入 core-js,而 babel 的 @babel/preset-env 會(huì)根據(jù) targets 的配置按需引入 core-js,引入方式受 useBuiltIns 影響 (entry 是在入口引入 targets 需要的,usage 是每個(gè)模塊引入用到的)。

但是 babel 因?yàn)槭敲總€(gè)文件單獨(dú)編譯的(tsc 是整個(gè)項(xiàng)目一起編譯),而且也不解析類型,所以 const enum,namespace 合并,namespace 導(dǎo)出非 const 值并不支持。而且過時(shí)的 export = 的模塊語法也不支持。

但這些影響不大,完全可以用 babel 編譯 ts 代碼來生成體積更小的代碼,不做類型檢查編譯速度也更快。

如果想做類型檢查可以單獨(dú)執(zhí)行 tsc --noEmit。

當(dāng)然,文中只是討論了 tsc 和 babel 編譯 ts 代碼的區(qū)別,并沒有說最好用什么,具體用什么編譯 ts,大家可以根據(jù)場景自己選擇。

責(zé)任編輯:姜華 來源: 神光的編程秘籍
相關(guān)推薦

2021-12-01 19:32:14

原理Node代碼

2022-07-27 16:50:39

BabelTypeScript前端

2021-06-01 06:00:06

typescriptjavascript

2024-07-05 15:26:59

代碼Merge分支

2022-05-06 08:26:21

babel編譯器

2021-01-19 06:16:05

前端Babel 技術(shù)熱點(diǎn)

2022-02-25 14:04:56

TS前端代碼

2011-04-14 09:42:06

DataReaderDataSet

2021-12-09 17:21:48

TypeScript TS 前端

2013-10-15 10:18:17

2013-10-15 10:24:23

hadoop大數(shù)據(jù)

2022-05-22 21:16:46

TypeScriptOmit 工具

2017-02-20 13:54:14

Java代碼編譯

2022-12-27 09:22:06

Nest.js框架

2010-03-31 17:01:07

2011-09-05 10:30:51

重構(gòu)代碼庫業(yè)務(wù)模型

2020-09-21 06:58:56

TS 代碼建議

2022-02-25 08:32:07

nodemon搭Node.jsJavascript

2018-10-31 14:00:05

LispJavaScript編程語言

2013-07-01 11:15:55

代碼產(chǎn)品
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號