前端技术之webpack(3.10.0)
————babel
V1.0
https://doc.webpack-china.org/
https://github.com/webpack/webpack
babel是前端的一个生态系统,babel的核心功能 是将ES6代码 -> 可兼容的ES5代码
ES6语法 + API 谷歌支持程度差不多达到99%
ES6语法 + API IE支持程度差不多达到9%
babel: 兼容性到达70%
可以看做 babel 的编译器。babel 的核心 api 都在这里面,比如 transform,
主要都是处理转码的。它会把我们的 js 代码,抽象成 ast,即 abstract syntax tree 的 缩写,
是源代码的抽象语法结构的树状表现形式。我们可以理解为,它定义的一种 分析 js 语法的树状结构。
也就是说 es6 的新语法,跟老语法是不一样的,那我们 怎么去定义这个语法呢。所以必须要先转成 ast,
去发现这个语法的 kind,分别做 对应的处理,才能转化成 es5.
@babel/cli 是 babel 提供的命令行工具,用于命令行下编译源代码。
这里假定我们已通过 npm init 初始化项目。
首先,在项目中安装 @babel/cli:
npm install --save-dev @babel/core @babel/cli
如果你用过 babel 6,可能要问,怎么不是 npm install --save-dev babel-cli?@ 符号 又是什么?
这是 babel 7 的一大调整,原来的 babel-xx 包统一迁移到babel 域下 - 域由 @ 符号来标识,
一来便于区别官方与非官方的包,二来避免可能的包命名冲突。
现在假定我们的项目下有一个 script.js 文件,内容是:
let fun = () => console.log('hello babel.js')
我们试试运行
npx babel script.js
结果:
let fun = () => console.log('hello babel.js');
问题:
还是原来的代码,没有任何变化。说好的编译呢?
这个调整则是在 babel 6 里发生的。Babel 6 做了大量模块化的工作,
将原来集成一体的各种编译功能分离出去,独立成插件。
这意味着,默认情况下,当下版本的 babel 不会编译代码。我们需 要安装各式各样的插件
随着各种新插件的加入,我们的命令行参数会越来越长。
这时,我们可以新建一个 .babelrc 文件,把各种命令行参数统一到其中。
比如,要配置前面提到过的箭头函数插件:
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
之后,在命令行只要运行 npx babel script.js 即可,babel 会自动读取 .babelrc 里的配 置并应用到编译中:
$ npx babel script.js
let fun = function () {
return console.log('hello babel.js');
};
@babel/preset-env
假如我们现在有一个项目,页面要求支持 IE 10,但 IE 10 不支持箭头函数、 class 及 const,
可是你喜欢用这些新增的 JavaScript 语法,你在项目里写了这么 一段代码:
const alertMe = (msg) => {
window.alert(msg)
}
class Robot {
constructor (msg) {
this.message = msg
}
say () {
alertMe(this.message)
}
}
const marvin = new Robot('hello babel')
显然,在 IE 10 下这段代码报错了。
好消息是,babel 有各种插件满足你的上述需求。我们来安装相应插件:
$ npm install --save-dev @babel/plugin-transform-arrow-functions
@babel/plugin-transform-block-scoping @babel/plugin-transform-classes
接着,将它们加入 .babelrc 配置文件中:
{
"plugins": [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-classes"
]
}
然后运行 npx babel script.js,就有编译结果了。
问题:
这样安装插件、配置 .babelrc 的过程非常乏味,而且容易出错。通常,我们 不会关心到具体的某个
ES2015 特性支持情况这个层面,我们更关心浏览器版本这个层面。我不想关 心 babel 插件的配置,我只希望,
给 babel 一个我想支持 IE 10 的提示,babel 就帮我编译出能在 IE 10 上正常运行的 JavaScript 代码。
!!!欢迎 @babel/preset-env。
我们不妨把 Preset 理解为套餐(预设),每个套餐里打包了不同的插件,
这样安装套餐就等于一次性安装各类 babel 插件。
首先在项目下安装:
$ npm install --save-dev @babel/preset-env
然后修改 .babelrc:
{
"presets": ["@babel/preset-env"]
}
运行 npx babel script.js,输出结果。与前面辛苦配置各种插件后的输出结果几 乎一模一样。
可是,我们还没告诉 babel 我们要支持 IE 10 的,为什么它却 好像预知一切?我们来看 babel-preset-env
的一段文档:
Without any configuration options, babel-preset-env behaves exactly the same as
babel-preset-latest (or babel-preset-es2015,
babel-preset-es2016,
babel-preset-es2017 together).
默认情况下,babel-preset-env 等效于三个套餐,而不巧我们前面安装的 几个插件已经囊括在babel-preset-es2015 中。
那么,如果我只想支持最新版本的 Chrome 呢?
这时我们可以调整 .babelrc 的配置:
{
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": ["last 1 Chrome versions"]
}
}]
]
}
最新版本的 Chrome 已经支持箭头函数、class、const,所以 babel 在编 译过程中,不会编译它们。
Babel includes a polyfill that includes a custom regenerator runtime and core-js.
基本上,babel-polyfill 就是 regenerator runtime 加 core-js。
可是,为什么需要 polyfill 这所谓的垫片?前面聊到 @babel/preset-env 时,
不是说只要定义好我想支持的目标浏览器,babel 就能编译出能运行在目标浏览器上的代码吗?
我们暂时从 polyfill 说起。拿 findIndex 来说,IE 11 仍不支持该方法,假如你的代码里写了 findIndex,
IE 11 浏览器会报如下错误:
Object doesn't support property or method 'findIndex'
怎么办,这时我们就可以写个 polyfill
什么是polyfill?
如果目标环境中已经存在 findIndex,我们什么都不做,如果没有,
我们就在 Array 的原型中定义一个。这便是 polyfill 的意义。 babel-polyfill 同理。
babel-polyfill 的用法。
安装 babel-polyfill
npm install --save @babel/polyfill
使用 babel-polyfill
我们需要在程序入口文件的顶部引用 @babel-polyfill:
require('@babel/polyfill')
[].findIndex('babel')
或者使用 ES6 的写法:
import '@babel/polyfill'
[].findIndex('babel')