前端修仙路-Babel 7.x 详解
前言:
Babel
是一个JavaScript
编译器,主要用于将ECMAScript 2015+
版本的代码转换为向后兼容的JavaScript
语法,以便能够运行在当前和旧版本的浏览器或其他环境中。本文主要介绍Babel
常用的预设和插件相关概念,以及在webpack
中如何使用。
babel 的前置知识
网上流传的很多是 babel6 的教程,babel6 和 babel7 的区别,可以根据命名来区分,babel7 的包一般以@babel/xxx
开头,babel6则是babel-xxx
开头,本文一切以现有最新版为准。babel6 之后,默认的转换被删除,如果不指定插件和预设,babel 只会返回原始代码。
概念
- Presets 预设,babel预设的一组插件集合。
- Plugins 插件,插件分为以下两种
- Transform Plugins 转换插件,这种类型的插件一般负责将ES6 / ES2015转换为ES5,转换为ES3,jsx,以及实验性功能等。
- Syntax Plugins 语法插件,一般转换插件里已经含有语法解析,不需要再额外引入。
- Helpers 帮助程序,含有一些帮助编译的方法,这些主要提供给插件内部使用
- stage-x 实验性的预设,预设中含有的各种提案阶段
- 提案经过TC39(ECMAScript 标准背后的技术委员会)4个阶段:
- babel-preset-stage-0 设想(Strawman):只是一个想法,可能有 Babel插件。
- babel-preset-stage-1 建议(Proposal):这是值得跟进的。
- babel-preset-stage-2 草案(Draft):初始规范。
- babel-preset-stage-3 候选(Candidate):完成规范并在浏览器上初步实现。
- 提案经过TC39(ECMAScript 标准背后的技术委员会)4个阶段:
注意stage-4不存在,因为4已经采纳成为标准,将在下一个年度版本发布。
- polyfill 这个词得分开看,poly为单词前缀,通常表示多的,多数的,意味着有多种技术,fill 填补(漏洞),合起来就是用技术去填补漏洞,通常指的是对低版本浏览器某些不兼容的api的一种修复方式。
babel的使用方式
- babel-cli 命令行中使用 babel 编译文件的简单方法
- babel-register 只需要在文件头部添加
require("@babel/register");
即可编译该文件,原理是通过require
的钩子进行即时编译模块 - babel-core 以编程的方式来使用 Babel,
var babel = require("@babel/core");
- babel-loader 在webpack作为loader加载使用
babel的配置方式
- loader中配置,webpack中的loader传递选项可以进行配置
- package.json中配置,提供有babel键值对进行配置
- 配置文件配置,一般约定有.babelrc、.babelrc.json、babel.config.json、babel.config.js、.babelrc.js这些配置文件。
- cli命令行传递选项或者使用api进行配置
babel的编译流程
input string -> @babel/parser parser -> AST -> transformer[s] -> AST -> @babel/generator -> output string
babel如何使用
接下来介绍babel在webpack中如何使用
安装
npm install babel-loader@8 @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
npm install @babel/runtime-corejs3
- babel-loader@8 babel-loader8.0 版本, 以 loader 的方式去编译js模块
- @babel/core babel 的编译核心,babel-loader 需要该核心模块支持
- @babel/preset-env babel 的预设,可以理解为一套组合 babel 插件集合,集成了一套转换规则
- @babel/plugin-transform-runtime 可重用Babel注入的帮助程序代码以节省代码大小。如果使用preset-env,可以不安装该模块。
- @babel/runtime-corejs3 babel 的模块运行时,含有助手方法以及 polyfill
使用
webpack中配置loader
module.exports = {
module: {
rules: [
{
// 以 .js 结尾的文件, 使用 babel-loader
test: /\.js$/,
// 编译时, 不去node_modules 目录下找
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
// ...省略无关代码
]
}
}
babel.config.js文件中的配置
// 第一种方式
module.exports = {
"presets":[
["env",{
"targets":{ // 编译目标,根据平台来编译所需
"edge":"17",
"firefox":"60",
"chrome":"67",
"safari":"11.1",
"ie":"8"
},
"useBuiltIns":"usage", // 不需要手动引入 @babel/polyfill,按需引入
"corejs":3 // 指定使用的corejs版本
}],
// "es2015", // 需要安装@babel/preset-es2015
// "stage-2" // 需要安装@babel/preset-stage-2,慎重使用提案阶段内容,因为可能会发生改变。
],
"plugins": []
}
//第二种方式
module.exports = {
"presets": [
[
"env"
]
],
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": 3
}]
]
}
两种配置的区别是:
- 方案一使用的是presets配置方式,采用修改全局对象的方式去polyfill。适合平时的业务项目使用。如果使用第一种方式可以不安装
@babel/plugin-transform-runtime
插件。 - 方案二使用的是
@babel/plugin-transform-runtime
插件的方式,采用不污染全局对象模式去polyfill。适合开发第三方类库使用。
小知识:凡是前缀以
@babel/preset-
的开头的预设都可以用缩写形式进行配置,上面看到的预设是缩写形式。@babel/preset-env
预设大幅度简化了配置,不需要再配置es2015,es2017预设什么的,也不再需要配置一大堆插件,自动采用最新的语法,并且按需引入。
plugins
的执行顺序是顺序执行,而presets
的执行顺序是倒序执行,如同webpack里的loader一样。`
@babel/preset-env中重要选项
{
"presets": [
[
"env",
{
"targets": "> 5%",
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
-
targets
babel根据目标平台来编译{ "targets": "> 0.25%, not dead" } // or { "targets": { "chrome": "58", "ie": "11" } }
环境可以指定:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron.
推荐将该平台配置,写在 .browserslistrc 或者 package.json中,这样所有包都可以共享。
-
useBuiltIns
这个选项用来告诉@babel/preset-env
如何处理polyfills
,具有以下几个参数:- usage 按需引用,根据编译目标以及实际使用情况按需导入polyfills。
- false 不引入,由用户自己决定引入部分。
- entry 在核心入口文件中使用
import '@babel/polyfill'
语句引入,并不推荐,引入的包可能含有自己并不需要的东西。
-
corejs
仅当与 useBuiltIns: usage 或 useBuiltIns: entry 一起使用时才有效,可以指定corejs版本 2 | 3,建议使用3,2对于最新新增的polyfill不支持。
其他选项见官网
创建自己的预设
可以为自己公司或团队创建预设:
// index.js
module.exports = {
presets: [
require("@babel/preset-es2015"),
require("@babel/preset-react")
],
plugins: [
require("@babel/plugin-transform-flow-strip-types")
]
};
// package.json
{
"name": "@babel/preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle <me@thejameskyle.com>",
"dependencies": {
"@babel/preset-es2015": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/plugin-transform-flow-strip-types": "^7.0.0"
}
}
然后将这个包发布到npm上,就能安装使用自己的预设了
npm install @babel/preset-my-awesome-preset -D
关于@ babel / polyfill
@babel/polyfill 是对 core-js 的封装,引入该模块时,实际是引用了 core-js,以及regenerator-runtime。自Babel 7.4.0开始,官方已不推荐使用此软件包。
import "@babel/polyfill";
上下两种是等价写法
import "core-js/stable";
import "regenerator-runtime/runtime";
对于绝大部分情况,使用 @babel/preset-env + useBuiltIns: ‘usage’ 选项是最好的选择。
其他环境中使用
vue
vue 环境没有提供外部使用的preset,其@vue/babel-preset-app也是基于vue-cli的。如果是用vue-cli,只需在babel的配置文件中配置即可。
react
module.exports = {
"presets": ["@babel/preset-react"]
}
typescript
module.exports = {
"presets": ["@babel/preset-typescript"]
}
然后配置tsconfig.json,具体配置项见ts官网
附录
下面列出一些插件与预设仅供参考:
预设
省略包前缀@babel/preset-如下:
-
env 集合了最新的javascript的预设,以及polyfill,
-
es2015 es2015插件集合
-
react react插件预设
-
stage-x es所在阶段的提案语法
-
typescript ts的预设
转换插件
省略包前缀@babel/plugin-transform 如下:
ES3
- member-expression-literals
- property-literals
- reserved-words
ES5
- property-mutators
ES2015
- arrow-functions
- block-scoped-functions
- block-scoping
- classes
- computed-properties
- destructuring
- duplicate-keys
- for-of
- function-name
- instanceof
- literals
- new-target
- object-super
- parameters
- shorthand-properties
- spread
- sticky-regex
- template-literals
- typeof-symbol
- unicode-escapes
- unicode-regex
ES2016
- exponentiation-operator
ES2017
- async-to-generator
ES2018
- async-generator-functions
- dotall-regex
- named-capturing-groups-regex
- object-rest-spread
- optional-catch-binding
- unicode-property-regex
Experimental
- class-properties
- decorators
- do-expressions
- export-default-from
- export-namespace-from
- function-bind
- function-sent
- logical-assignment-operators
- nullish-coalescing-operator
- numeric-separator
- optional-chaining
- partial-application
- pipeline-operator
- private-methods
- throw-expressions
- private-property-in-object
各个插件的具体用法见官网
关于babel的分享到此结束 😃