前言
对于浏览器来说,包括但不限于 JSX
、TS
、ES6+
的代码并不能正常的在所有浏览器上渲染。为了解决这个问题 babel
应运而生。babel
可以解析这些代码,并将其转换为浏览器可兼容的低级 js 代码。
它工作时将根据 browserslist
所指定的兼容浏览器范围进行代码转换。对于 browserslist
的说明,可以参考 browserslist及其使用。
环境准备
1 | npm i @babel/core @babel/cli @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping -D |
上面安装的两个包分别为:
- @babel/core:babel 的核心库,但除了解析功能以外什么都干不了,想实现功能需要其他插件帮助。
- @babel/cli:babel 不允许直接在终端中使用,如需使用则需要借助
@babel/cli
工具。 - @babel/plugin-transform-arrow-functions:对箭头函数代码进行转换。
- @babel/plugin-transform-block-scoping:将
let
与const
转换为var
。
更多的包均可在 babel -> package 里找到。
食用方式
这里有 ./src/js/babel.js
:
1 | const foo = () => { |
此时的 .browserslistrc
配置为:
1 | >0.2% |
转换箭头函数和 let 与 const
下面的语句可以将上面的 js 文件进行解析,解析结果被放在目录 babel-output
中(此处 src/js/babel.js
可以改为目录名称,将会对目录下所有 js 文件进行解析)。
1 | npx babel src/js/babel.js --out-dir babel-output --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping |
babel-output/babel.js
:
1 | var foo = function () { |
可见 let
与箭头函数成功的被转换为了兼容性代码,且并未丢失代码原本意义。
@babel/preset-env
对于上面的示例代码中,似乎还有不少 ES6 的特性未被转换,但每一种转换都要手动添加工具似乎太过繁琐了。因此便有了整合了一些预设的功能插件 @babel/preset-env
。而且该插件与上面有所不同的是,他会参考 browserslist
配置进行兼容。
1 | npm i @babel/preset-env -D |
这里我们对处理指令进行一些修改,不再使用 --plugins
而是改为 --presets
。
1 | npx babel src/js/babel.js --out-dir babel-output --presets=@babel/preset-env |
babel-output/babel.js
:
1 | ; |
虽然该预设插件集成了许多转换功能,比如 const 关键字与 箭头函数等。但当遇到 Prmise、generator 生成器、Symbol、Reflect 等深层次语法时,它是无法转换的。这种情况就要使用 Polyfill
了,本文后面将会讲解
webpack 中使用 babel
在 webpack 中,babel 的加载器为 babel-loader
,此时仅需要安装 babel-loader
,不需要安装 @babel/core
与 @babel/cli
。
尽管该加载器与 sass-loader
和 less-loader
类似,在内部依赖一个工具解析,在这里为 @babel/core
。但不同的是,babel-loader
已经内部安装了 @babel/core
,不需要我们再额外去安装。而 @babel/cli
仅用于以命令方式交互使用 babel, 因此也不需要安装。
1 | npm i babel-loader -D |
由于 babel
的工作原理是对 css 代码进行解析,得到兼容后的 css 代码,不难理解 babel-loader
该放置的位置。
1 | module.exports = { |
但这样其实是不会起任何作用的。我们知道 babel
除了解析 js
代码以外是什么也做不了的,想实现功能还需要使用插件。下面将会讲解插件的加载方法。
插件的配置方法
插件的种类很多,这里还是使用转换箭头函数和 let 与 const 的插件 @babel/plugin-transform-arrow-functions
和 @babel/plugin-transform-block-scoping
。
1 | npm i @babel/preset-env -D |
使用插件后,也就不能像其他 loader 那样简单配置了,而是需要在 options
中进行相应配置。不同的 loader 有不同的配置方法,可以参考各自的官方文档。
1 | module.exports = { |
配置许多个 plugins
或许有些繁琐,下面提供整合了一些预设的功能插件 @babel/preset-env
的配置方法。
1 | module.exports = { |
presets
是默认按照 browserslist
指定的范围进行编译的,同时也可以通过配置 targets
属性来覆盖 browserslist
中的配置,如下。
1 | module.exports = { |
babel.config.js
上述配置方式可以发现一个问题,babel-loader
的配置代码相对比较繁琐,嵌套比较深管理起来非常臃肿。
同时,使用交互命令执行时,--plugins
或 --preset
参数每次都要输入也让人厌烦。便有了 babel.config.js
配置文件。
在项目根目录下创建 babel.config.js
,输入以下内容。
该配置文件可以为
json
、cjs
、mjs
,在 babel 7 之前还命名为babelrc.json(js)
。
1 | module.exports = { |
配置好后就可以优化交互指令或 webpack 配置文件的内容了。
指令方式
此时可以省略 --plugins
或 --preset
参数。
1 | npx babel src/js/babel.js --out-dir babel-output |
webpack
此时可以将 babel-loader
改为简写模式。
1 | module.exports = { |
polyfill
上面所讲的 @babel/preset-env
可以帮我们解决一些如 const 关键字、箭头函数等的兼容性问题,但当遇到 Prmise、generator 生成器、Symbol、Reflect 等深层次语法时,它是无法转换的。这时就有了 polyfill。
polyfill 字面意思填充,把一些高级原生语法实现后填充到低版本浏览器上。由于会导致打包后的产出内容过大,webpack 5 后取消内置。
安装
需要注意的是,polyfill 需要安装为生产依赖
babel 7 之前安装方式:
1 | npm i @babel/polyfill |
babel 7 后不建议使用上述方式安装,因为直接按上面那样安装太大了,改为使用两个核心库,babel 官网 上是这样讲的:
从 Babel 7.4.0 开始,这个包已经被弃用,取而代之的是直接包含core-js/stable(以填充 ECMAScript 特性):
1 import "core-js/stable";如果您正在将生成器或异步函数编译到 ES5,并且您使用的版本低于
@babel/core
或@babel/plugin-transform-regenerator
低于7.18.0
,则还必须加载该regenerator runtime
包。使用@babel/preset-env
的useBuiltIns: "usage"
选项或时会自动加载@babel/plugin-transform-runtime
。
这里对两个包做一下说明:
- core-js/stable:加载 js 核心库下的已经形成标准的部分(stable)
- regenerarator-runtime/runtime:转换 generator 生成器的时候(比如 promise、async/await 底层就是 generator 生成器)
现在安装时按照如下安装方式即可:
1 | npm i core-js |
使用
在 bable.config.js
或 webpack.config.js
中对 @babel/preset-env
进行配置。
1 | module.exports = { |
属性解释:
- useBuiltIns:存在两个可选值
- false:默认值,polyfill 不会对当前打包进行处理
- usage:根据原代码中使用到的新语法,按需进行填充(不会理睬
browserslist
配置) - entry: 根据
browserslist
对所有浏览器所需要的新语法进行填充(不会理睬源代码)
- corejs:指定 corejs 版本,默认为
2
。由于现在安装的最新版本corejs
为 3.x,当useBuiltIns
不为false
时,若不手动指定为3
,会引发报错。
当使用 useBuiltIns
设置为 entry
时,还需要在入口文件引入两个包
1 | import "core-js/stable"; |
注意事项
由于第三方包在打包过程中可能会存在使用了 polyfill 填充过的情况,此时再使用 polyfill 对其进行处理可能会出现问题。因此往往会在 webpack.config.js
中做如下处理:
1 | module.exports = { |
处理 TypeScript
babel 支持对 TypeScript 进行包括 polyfill 的处理,此时需要使用使用预设 @babel/preset-typescript
。
1 | npm install @babel/preset-typescript -D |
该预设仅对 TypeScript 进行编译处理,若进一步进行 polyfill 等的处理,需要依赖 @babel/preset-env
预设。
对 babel.config.js
进行如下配置:
1 | module.exports = { |
@babel/preset-typescript
拥有一些配置选项,性详见官网 @babel/preset-typescript
尽管该预设可以直接解析 TypeScript,但他并不拥有编译时检查语法错误的功能。因此可以尝试通过联合使用命令的方式来达成需求。
package.json
中新增如下命令,使用 noEmit
参数来指定 ts 编译器仅校验,不便宜 js 操作。
1 | { |
个人觉得这样操作属实啰嗦,不如联合使用
ts-loader
与babel-loader
,见仁见智了。