前端工程化还包括bable
# Babel
Babel主要做的就是这几点:
- 语法转换:将新的语法转换成兼容性更好的旧语法。
- 特性垫片:通过
Polyfill
实现目标环境中缺少的特性,将环境的特性差异垫平,说白了还是做兼容。
比如下面这段代码
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);
// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});
2
3
4
5
6
7
babel的使用场景主要分为两种:命令行和构建工具。
babel为命令行提供了@babel/cli
工具,支持在命令行直接执行babel命令。
npm install --save-dev @babel/core @babel/cli
安装好在项目中执行babel
命令就可以进行转换了。
babel src --out-dir lib # 将src下的文件处理后输出到lib
在构建工具中使用是我们使用babel更常用的方式。
以webpack为例,babel提供了一个loader:babel-loader
,使用这个loader我们就能很轻松实现代码转换。
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
// 配置项
}
}
]
}
2
3
4
5
6
7
8
9
10
11
bable的运行方式:
Babel的运行过程分为三个阶段:解析 --- 转换 --- 生成
- 解析:解析代码,并生成AST抽象语法树
- 转换:将上一步的AST转换成目标环境支持的AST
- 生成:根据转换后的AST生成目标代码
# bable配置
babel7支持多种方式来进行配置:
babel.config.json
(opens new window)(项目范围).babelrc.json
(opens new window)(相对文件)package.json
中添加babel字段
除了json文件之外,babel还支持其他格式的配置文件,比如 .js
,.cjs
(Commonjs)和.mjs
(ESModule),它们相对于json格式更加灵活,可以在配置中进行编程处理,但是会使配置无法静态分析,失去可缓存性。
// babel.config.js
module.exports = {
presets: [],
plugins: []
};
2
3
4
5
babel虽然有多种方式来写配置文件,但是配置的内容都是类似的,主要包含plugins
和presets
两部分,不过这里需要注意几点:
配置格式:plugins和presets配置项均为数组,如果每项不需要配置的话,直接放入数组(值为项的字符串名称);如果要配置的话,则需要把格式改成数组,该数组的第一个元素是该项的字符串名字,第二个元素是该项的配置对象。这里填写的plugins和presets,babel会自动检查是否已被安装在node_modules下面,当然,也可以使用相对路径来使用本地文件。
"plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ], "@babel/plugin-transform-runtime" ]
1
2
3
4
5
6
7
8
9
10执行顺序:
plugins
会从前到后执行,presets
会从后向前执行,且plugins
会比presets
先执行presets
逆序执行的目的是为了保证向后兼容,我们在配置的时候按照规范的时间顺序列出即可,如['es2015', 'stage-1']
,这样就会先使用stage-1
,否则就会出现错误。短名称:在配置plugin和preset名字的时候,可以省略名字中的
babel-plugin-
和preset-
字样,仅使用插件的名字就可以了。{ "plugins": [ "myPlugin", // 完整名字:"babel-plugin-myPlugin" "@org/myPlugin" // 完整名字:"@org/babel-plugin-myPlugin" ], "presets":[ "myPreset", // 完整名字:"babel-preset-myPreset" "@babel/myPreset" // 完整名字:@babel/preset-myPreset ] }
1
2
3
4
5
6
7
8
9
10
# plugin
babel的转换完全依靠插件,babel拥有的能力完全取决于给它配置了哪些插件。
babel的插件分为两种:
语法插件:赋予babel解析特定语法的能力,可以理解一个为babel服务的翻译官,仅做翻译解析工作,工作在
AST
转换阶段。转译插件:转译插件即把特定的语法转换成指定标准的语法。
比如将箭头函数
(x) => x
转换成function (x) {return x}
,只需要配置箭头函数转译插件即可。
插件的使用大概分为两步:
- 将插件添加到配置文件的
plugins
属性中,可根据插件文档做相应配置。 - 安装插件到项目中,使用npm或者yarn均可
每个插件的功能是单一的,所以一般转换需求要使用的插件数量比较多,这时候可以考虑presets
# 编写插件
和webpack一样,bable可以使用自定义插件实现功能
module.exports = function() {
return {
visitor: {
Identifier(path) {
const name = path.node.name;
// reverse the name: JavaScript -> tpircSavaJ
path.node.name = name
.split("")
.reverse()
.join("");
},
},
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# preset
在实际的开发中,我们如果用ES6来进行开发的话,那需要转换的语法是非常多的,难道要一个一个去配置吗?还要不要配陪妹子了?
这个时候,就该presets登场了。presets可以理解为一组插件的集合,就像你去德克士直接点一个全家桶,而不必一个一个给服务员说要鸡腿、鸡翅、薯条...(如果想和服务员小姐姐多待一会,也不是不可以)
配置文件:通过配置文件的targets属性指定目标环境
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"], "node": "12.10" } }] ] }
1
2
3
4
5
6
7
8
9
10browserslistrc:如果是浏览器或者
Electron
,官方推荐使用.browserslistrc文件来指定目标环境(可以和autoprefixer、stylelint等其他工具共享配置)。
不过如果在配置文件中设置了
targets
或ignoreBrowserslistConfig
,.browserslistrc
中配置的内容将不会生效。
presets中的其他配置:
modules: 用于指定转换后的模块规范,可设置为
"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false
,默认为"auto"
。useBuiltIns
:此选项配置如何@babel/preset-env
处理polyfill,在后面介绍polyfill我们会详细介绍。corejs
:用于指定corejs的版本,仅当使用了useBuiltIns: usage
或者useBuiltIns: entry
此配置才有效,使用时一定要配置正确的版本,否则会报错。当前默认的版本是
2.0
,不过建议使用3.0
版本,因为2.0已经不会再添加新的特性了,如果使用了一些较新的语法,将会无法转换。
@babel/preset-env
这个是我们最常用的,也是官方现在推荐的preset,它是一个智能预设,我们无需再关心特定环境下所需的转换插件(只需要指定目标环境),就可以使用最新的语法。除了能够减少使用难度,还能够使转换出来的代码体积更小。
preset-env会根据配置的目标环境中缺少的功能,选择对应的插件进行必要的转换和polyfill
,而目标环境支持的特性就不会转换,而不是无脑的一把梭哈。
# polyfill
前面我们的配置其实是不完美的,转换出来的代码直接在浏览器运行是可能会出问题的。这是因为babel默认只转换了语法,而对于一些新的API是并没有处理的,如Generator、Set、Maps、Proxy、Promise 等全局对象及其方法都不会转码,如果我们在代码中使用到了就会报错。
此时,我们就需要做Polyfill,也就是所谓的'垫片',作用就是把浏览器的差异垫平,人话就是通过模拟为这些浏览器补全缺失的全局对象及API。
# 常用插件
@babel/plugin-transform-runtime
是一个可重复使用Babel注入的帮助程序的插件,避免重复注入,以节省代码大小。通常还要搭配@babel/runtime
一起使用。
安装
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime # 提供缺失的特性,须要在生产环境中安装
2
配置
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [["@babel/plugin-transform-runtime"]]
}
2
3
4
5
6
7
8
9
10
11
12
13