现在JavaScript
代码主要采用ES6
编写,但并不是所有浏览器都支持ES6
语法,因此,在其中需要某种转化机制,也就是将ES6+
语法转化为ES5
语法,babel7
可以帮助我们做到这件事。
1、babel介绍
babel-loader
是一个webpack
的加载器,用于将ES6+
转译成ES5
,要开始使用babel-loader
,我们需要安装一些依赖项,以babel7
为例,有这么一些依赖:
- @babel/core
- @babel/preset-env
- @babel/plugin-transform-runtime
- @babel/polyfill
下面细说以下这些依赖具有是干什么的:
@babel/core
这是babel编译库的核心包
@babel/preset-env
这个包是指编译时会自动按照最新的转译规则去编译,除了这个包外还有一些规则包,比如babel-preset-es2015
(将es6
的javascript
代码转化浏览器兼容javascript
代码), babel-preset-es2016
(将es7
的javascript
代码转化浏览器兼容javascript
代码), babel-preset-es2017
(将es8
的javascript
代码转化浏览器兼容javascript
代码)等。
@babel/polyfill
接下来我们继续说babel/polyfill
是个什么东东。babel
官网上写了很明确一句话,babel
只负责对语法进行编译。当我们写尖头函数,babel
会帮你把它编译成普通函数,这没有任何问题,但是,比如说我们代码里使用了promise
,babel
打包出来的代码其实还是promise
,在低版本浏览器里,promise
并不支持,但是babel
并不会帮你处理,因为这不是语法编译层面需要做的事情。不转换新的API
包括,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise
等全局对象。
于是,如果我们要让打包出来的代码能兼容低版本浏览器,还要考虑到promise,Set
这样的新语法低版本浏览器不兼容的问题,这时候babel/polyfill
就出场了。你只需要全局安装一下babel/polyfill
:
npm install --save-dev @babel/polyfill
然后在项目中使用一下它,你的代码就不存在刚才提到的兼容性问题了。
import '@babel/polyfill'
但是,直接用babel-polyfill
会有一些坑,第一个坑是污染全局环境,比如说低版本浏览器没有Set
,但是babel-polyfill
会在全局变量里加一个Set
。再一个问题是,会造成代码冗余,举个例子,多个模块用到Promise
,每个模块里都有可能独立存在一个对Promise
做兼容性的代码。所以,使用babel-polyfill
可以解决兼容性问题,但并不是最佳方案,于是,出现了babel-plugin-transform-runtime
,使用这个插件,就可以解决上面的问题了。
@babel/plugin-transform-runtime
为了不污染全局对象和内置的对象原型,但是又想体验使用新鲜语法的快感。就可以配合使用babel-runtime
和babel-plugin-transform-runtime
。 比如当前运行环境不支持promise
,可以通过引入babel-runtime/core-js/promise
来获取promise
, 或者通过babel-plugin-transform-runtime
自动重写你的promise
。也许有人会奇怪,为什么会有两个runtime
插件,其实是有历史原因的:刚开始开始只有babel-runtime
插件,但是用起来很不方便,在代码中直接引入helper
函数,意味着不能共享,造成最终打包出来的文件里有很多重复的helper
代码。所以,Babel
又开发了babel-plugin-transform-runtime
,这个模块会将我们的代码重写,如将Promise
重写成_Promise
(只是打比方),然后引入_Promise helper
函数。这样就避免了重复打包代码和手动引入模块的痛苦。
在使用@babel/plugin-transform-runtime
之前需要安装@babel/runtime
,因为前者依赖后者。
2、webpack中配置babel
① 安装依赖
npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime @babel/polyfill @babel/runtime--save-dev
② 在项目的根目录中创建名为 .babelrc 的新文件来配置 Babel:
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
③ webpack 配置 loader(加载器)
module: {
rules: [
{
test: /\.js$/, // 使用正则来匹配 js 文件
exclude: /node_modules/, // 排除依赖包文件夹
use: {
loader: 'babel-loader' // 使用 babel-loader
}
}
]
}
webpack.config.js 最终配置:
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './babel7转义ES6.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
publicPath: __dirname + '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new CleanWebpackPlugin() // 会删除上次构建的文件,然后重新构建
]
}
④ 在 app.js 全局引入 @babel/polyfill 并写入 ES6 语法,并执行 npm run build 打包
// 测试 ES6 语法是否通过 babel 转译
import '@babel/polyfill'
const array = [1, 2, 3]
const isES6 = () => console.log(...array)
isES6()
const arr = [new Promise(() => {}), new Promise(() => {})]
arr.map(item => {
console.log(item)
})
全局引入 @babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大
更改 .babelrc,只转译我们使用到的
/**
* babel配置文件
*/
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",// 按需引入
"corejs": "3.3.2"
}
]
],
"plugins": ["@babel/plugin-transform-runtime"]
}
同时,将全局引入这段代码注释掉,再次打包
// 全局引入
// import '@babel/polyfill'
参考文章:
https://zhuanlan.zhihu.com/p/35378233
https://juejin.im/post/5a96859a6fb9a063523e2591
https://www.jianshu.com/p/7bc7b0fadfc2
https://github.com/SunshowerC/blog/issues/4