Babel7 概念和用法
https://github.com/YvetteLau/Blog
参考
https://mp.weixin.qq.com/s/xTfjMG2graIrfGGqhue_Jg
概念
Babel 是一个 JS 编译器。
主要将 ECMAScript2015+版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境。
- 语法转换
- 通过
Polyfill
方式在目标环境中添加缺失特性(@babel/polyfill
模块) - 源码转换
核心库
@babel/core 使用 babel 必须安装
命令行工具
@babel/cli 适合安装在项目里
@babel/node 适合全局安装
1 | npm install --save-dev @babel/core @babel/cli |
命令配置,package.json
1 | "scripts": { |
执行npm run compiler
就可以编译了
现在运行不会有编译任何文件,如果需要Babel
做一些实际工作,就需要为其添加插件(plugin
)
配置文件
babel.config.js
以编程的方式配置文件或者希望编node_modules
目录下的模块
根目录下创建babel.config.js
具体规则参考:文档
.babelrc
简单的并且用于单个软件包的配置
根目录下创建.babelrc
1
2
3
4{
"presets": [],
"plugins": []
}
具体规则参考:文档
- package.json
可以将.babelrc
中的配置信息作为babel
key 添加到package.json
文件中
1 | { |
- .babelrc.js
配置与.babelrc
相同,但是可以使用 js 编写
1 | //可以在其中调用 Node.js 的API |
插件
语法插件
解析特定类型的语法转换插件
转换插件会启用相应的语法插件,先解析,再转换
插件的使用
根目录新建.babelrc
文件,直接使用插件名字,Babel
会自动检查 是否已安装
1 | //.babelrc |
运行npm run compiler
,index.js
编译后
1 | const a = function () { |
箭头函数转为正常函数了,如果想将其他新的 JS 特性转换成低版本,需要对应的plugin
,如果一个一个配置太麻烦,可以使用预设
预设
创建一个preset
,可以使用一组插件
- @babel/preset-env
- @babel/preset-flow
- @babel/preset-React
- @babel/preset-typescript
注意,babel7 开始,所有针对标准天阶段的功能所编写的预设(stage preset)已经被弃用,以及移除@babel/preset-stage-x
@babel/preset-env
作用:对我们所使用的并且目标浏览器缺失的功能进行代码转换和加载polyfill
。不进行任何配置时,@babel/preset-env
所包含的插件将支持所有最新的 JS 特性(ES2015/es6,ES2016/es7 等,不包括 stage 阶段),将其转换成 ES5 代码。如果使用了 stage 阶段的语法,编译时会报错,需要安装对应的插件。@babel/preset-env
会很具配置的目标环境,来生成插件列表来编译,对于基于浏览的项目,可以使用.browserslistrc
文件来指定目标环境。也可以在 package.json 里面添加 key 为browserslist
配置。
1 | //create-React-app默认配置 |
新建.browserslistrc
,配置如下
1 | last 2 Chrome versions |
index 编译后箭头函数还在,因为 chrome 最新的两个版本已经支持箭头函数
1 | "use strict"; |
如果不是要兼容所有的浏览器和环境,推荐你指定目标环境,编译代码能够保持最小。
内置函数、实例方法
修改index.js
1 | const isHas = [1, 2, 3].includes(2); |
编译后
1 | "use strict"; |
语法转换只是将高版本的语法转换为低版本的,但是新的内置函数和实例方法无法转换。polyfill
垫平不同浏览器或者不同环境下的差异,让新的内置函数、实例方法等在低版本浏览器中也可以使用。
@babel/polyfill
包括core-js
和一个自定义的regenerator runtime
模块,可以模拟完整的 es2015+环境(不包括第 4 阶段前的提议)
内置组件:Promise
,WeakMap
等
静态方法:Array.from
,Object.assign
等
实例方法:Array.prototype.includes
等
生成器函数(还需要@babel/plugin-transform-regenerator
插件)
为了实现以上功能,polyfill 将添加到全局范围和类似 String 的内置原型中(会造成全局污染)
安装
1 | npm install --save @babel/polyfill |
注意:不使用 –save-dev,因为这是一个需要在源码之前运行的垫片。
1 | import "@babel/polyfill"; |
编译后
1 | "use strict"; |
@babel/polyfill
需要在其他代码之前引入,在webpack
中可以进行配置
1 | entry:{ |
.polyfills.js
1 | //当然,还可能有一些其它的 polyfill,例如 stage 4之前的一些 polyfill |
编译后
1 | "use strict"; |
现在的代码在高版本或低版本或 node 环境都能运行了。但是会引入整个@babel/polyfill
包,会导致打包的体积增大,我们期望按需加载。
- 参数
useBuiltIns
参数,设置值为usage
时,就只会包含代码需要的polyfill
- 必须同时设置
core.js
,默认’core.js’:2
注意:需要安装@babel/polyfill
,默认安装 corejs@2
使用 core-js@3 的原因 core-js@2 分支已经不会添加新特性,新特性都会添加到 core-js@3。例如 Array.prototype.flat()
安装依赖
1 | npm install --save core-js@3 |
修改配置文件
1 | { |
编译后
1 | "use strict"; |
在 useBuiltIns 参数值为 usage 时,仍然需要安装 @babel/polyfill,
如果使用到了 async/await,那么编译出来的代码需要 require(“regenerator-runtime/runtime”),在 @babel/polyfill 的依赖中。也可以只安装 regenerator-runtime/runtime 取代安装 @babel/polyfill。
1 | import "@babel/polyfill"; |
编译为
1 | "use strict"; |
@
Babel 会使用很小的辅助函数来实现类似 _createClass 等公共方法。默认情况下,它将被添加(inject)到需要它的每个文件中。
修改 index.js
1 | import "@babel/polyfill"; |
编译后
1 | "use strict"; |
如果多个文件使用了 class,_classCallCheck
,_defineProperties
,_createClass
,这些方法就会被多次inject
,导致包变大。
@babel/plugin-transform-runtime
一个可以重复使用Babel
注入的帮助程序,以节省代码大小的插件。@babel/plugin-transform-runtime
需要配合@babel/runtime
使用。
安装
1 | npm install --save-dev @babel/plugin-transform-runtime |
除了减少编译后代码的体积外,@babel/plugin-tranform-runtime
还会创建一个沙盒环境,如果使用@babel/polyfill
及其提供的内置程序会污染全局,虽然这对于应用程序或命令行工具可能是可以的,但是如果你的代码是要发布供他人使用的库,或者无法完全控制代码运行的环境,则将成为一个问题。
@babel/plugin-transform-runtime
会将这些内置别名为core-js
的别名,因此可以无缝使用它们,无需polyfill
。
修改配置
1 | { |
重新编译 index.js
1 | "use strict"; |
帮助函数不是被直接inject
到代码中,而是从@babel/runtime
中引入。
如何避免全局污染
index.js
1 | import "@babel/polyfill"; |
编译后
1 | "use strict"; |
现在的配置,Array.prototype 上新增了 includes 方法,并且新增了全局的 Promise 方法,污染了全局环境。
如果希望@babel/plugin-transform-runtime
不仅处理帮助函数,同时也能加载polyfill
,需要增加配置信息。
新增依赖@babel/runtimr-corejs3
1 | npm install @babel/runtime-corejs3 --save |
需要改配置文件,移除@babe/preset-env 的 useBuitIns 的配置,不然就重复了
1 | { |
index.js
1 | const isHas = [1, 2, 3].includes(2); |
编译为
1 | "use strict"; |
没有直接去修改 Array.prototype,或者是新增 Promise 方法,避免了全局污染。如果上面 @babel/plugin-transform-runtime 配置的 core-js 是 “2”,其中不包含实例的 polyfill 需要单独引入。
插件/预设补充知识
执行顺序
- 插件在预设前运行
- 插件从前往后调用
- 预设从后往前调用
1 | { |
先执行 @babel/plugin-proposal-class-properties,后执行 @babel/plugin-syntax-dynamic-import
先执行 @babel/preset-React, 后执行 @babel/preset-env。
参数结构
插件和预设都可以接受参数,参数由插件名和参数对象组成一个数组。
插件短名称
如果插件名称为 @babel/plugin-XXX,可以使用短名称@babel/XXX
如果插件名称为 babel-plugin-XXX,可以使用端名称 XXX,该规则同样适用于带有 scope 的插件
1 | { |
stage1,2,3 阶段的编译插件
解决方法:https://segmentfault.com/a/1190000020237923
1 | { |