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