前言
开发过程中,我们会用到很多 ES6+ 的新特性,但是部分浏览器对这些新特性的支持并不是很好,所以我们需要使用 Babel 对代码进行转换,使得我们的项目可以兼容更多的浏览器版本。
Babel 默认只转换新的 JavaScript 语法,而不转换新的 API,比如 Iterator
、Generator
、Set
、Map
、Proxy
、Reflect
、Symbol
、Promise
等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转换。
举例来说,ES6 在 Array
对象上新增了 Array.from
方法。Babel 就不会转换这个方法。如果想让这个方法运行,必须使用 @babel-polyfill
,为当前环境提供一个垫片。
babel-polyfill
主要包含了core-js
和regenerator-runtime
两部分。
- core.js:提供了如ES5、ES6、ES7等规范中新定义的各种对象、方法的模拟实现。
- regenerator-runtime:提供 generator 支持,如果应用代码中用到 generator、async 函数的话需要用到。
**babel-polyfill**
实现的 Promise 对象扩展了 finally 方法,引用后不需要再使用 es6-promise 扩展**Promise.finally**
(在 ES2018 中 finally 方法已经成为了标准)。
实际开发的过程中,大家可能会对 Babel 的相关包和配置感到困惑,不清楚究竟是用来干什么的,这篇文章会帮助大家梳理清楚 Babel 的相关知识。
初始化项目
1、创建文件夹和文件:
mkdir babel-demo
cd babel-demo
# 项目结构如下
├── README.md
├── dist
│ ├── index.js // 转换之后的文件
├── src
│ ├── index.js // 源文件
└── package.json
2、初始化 package.json
npm init
3、安装依赖并配置 scripts 和 .babelrc
yarn add @babel/cli @babel/core @babel/preset-env -D
# 配置 scripts
"scripts": {
"build": "babel src --out-dir dist"
}
4、运行测试
执行 yarn run build
,结果如下:
从上面的结果我们可以看出,cosnt
被转换成了 var
,但 includes
方法没有被处理。
安装
在 7.x 的版本中,babel 的依赖都放到了 @babel 命名空间下
Babel 默认只转换新的 JavaScript 语法,而不转换新的 API,比如 Iterator
、Generator
、Set
、Map
、Proxy
、Reflect
、Symbol
、Promise
等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转换。
举例来说,ES6 在 Array
对象上新增了 Array.from
方法。Babel 就不会转换这个方法。如果想让这个方法运行,必须使用 @babel-polyfill
,为当前环境提供一个垫片。
babel-polyfill
主要包含了core-js
和regenerator-runtime
两部分。
- core.js:提供了如ES5、ES6、ES7等规范中新定义的各种对象、方法的模拟实现。
- regenerator-runtime:提供 generator 支持,如果应用代码中用到 generator、async 函数的话需要用到。
**babel-polyfill**
实现的 Promise 对象扩展了 finally 方法,引用后不需要再使用 es6-promise 扩展**Promise.finally**
(在 ES2018 中 finally 方法已经成为了标准)。
@babel/core 的作用是把 js 代码转换成 AST(抽象语法树),方便各个插件分析语法进行相应的处理
配置
// 可以把 options 抽离到 .babelrc 文件中
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [ "@babel/preset-env" ]
}
}
]
}
// index.js 引入 @babel/polyfill
import "@babel/polyfill";
经过上面的配置,我们的项目就可以在大部分的浏览器上运行,但是我们会发现打包之后文件的体积大了很多,因为 polyfill 默认会把所有的 ES6+ 的特性注入进来,如果想要按需注入,可以设置 useBuiltIns 参数。
useBuiltIns 选项是 babel 7 的新功能,这个选项告诉 babel 如何配置 @babel/polyfill 。
它有三个参数可以使⽤:
- entry:需要在 webpack 的⼊⼝⽂件⾥手动 import “@babel/polyfill” 。 babel 会根据你的使⽤情况导⼊垫⽚,没有使⽤的功能不会被导⼊相应的垫⽚。
- usage::不需要 import ,全⾃动检测,但是要安装 @babel/polyfill 。(试验阶段)
- false::如果你 import “@babel/polyfill” ,它不会排除掉没有使⽤的垫⽚,文件的体积会很大。 (不推荐)
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"corejs": {
"version": 2,
"proposals": true
},
"useBuiltIns": "usage" // 按需注⼊
}
]
]
}
@babel/preset-env 在升级到 7.4.0 以上的版本以后,既支持core-js@2,也支持core-js@3。所以增加了
corejs
的配置,来指定所需的版本。如果没有配置,默认使用 core-js@2, 并且会提示我们指定一个版本。
core-js@3
当 core-js 升级到 3 .0 的版本后,我们就不需要再使用 @babel/polyfill,因为它只包含 core-js 2.0 的版本。所以在 @babel/prest-env 升级到 7.4.0 及以上版本并且使用 core-js@3,需要做如下的替换工作:
// 安装 core-js@3.0 和 regenerator-runtime
yarn add core-js@3 regenerator-runtime
// .babelrc
presets: [
["@babel/preset-env", {
useBuiltIns: "entry", // or "usage"
corejs: 3,
}]
]
// 入口文件index.js
// 如果配置了 "useBuiltIns": "usage",下面的操作就不需要了
// before
import "@babel/polyfill";
// after
import "core-js/stable";
import "regenerator-runtime/runtime";
@babel/plugin-transform-runtime
当我们开发的是组件库、⼯具库的时候, @babel-polyfill 就不适合了,因为 ployfill 模拟的对象和方法是注⼊到全局变量 window 对象下的,会污染全局环境。所以推荐闭包方式:@babel/plugin-transform-runtime 。
// 安装
yarn add @babel/plugin-transform-runtime @babel/runtime-corejs3 -D
// 配置
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
注意:在之前的版本中,@babel/runtime-corejs2 最大的问题就是无法模拟实例上的方法,比如数组的 includes 方法就无法被 polyfill。但是在 core-js@3 的版本中,所有的实例方法都可以被polyfill了。
配置 React 打包环境
// 安装依赖
yarn add @babel/preset-react -D
yarn add react react-dom
// 配置
{
"presets": [
[
"@babel/preset-env"
{
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"corejs": 3,
"useBuiltIns": "usage" // 按需注⼊
}
]
"@babel/preset-react"
]
}
// react 代码
import React, { Component } from "react";
import ReactDom from "react-dom";
class App extends Component {
render() {
return <div>hello world</div>;
}
}
ReactDom.render(<App />, document.getElementById("app"));