webpack-babael7转译ES6

Fairfeng 2019-10-21

现在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(将ES6JavaScript代码转化浏览器兼容JavaScript代码), babel-preset-es2016(将es7JavaScript代码转化浏览器兼容JavaScript代码), babel-preset-es2017(将es8JavaScript代码转化浏览器兼容JavaScript代码)等。
@babel/polyfill
接下来我们继续说babel/polyfill是个什么东东。babel官网上写了很明确一句话,babel只负责对语法进行编译。当我们写尖头函数,babel会帮你把它编译成普通函数,这没有任何问题,但是,比如说我们代码里使用了promisebabel打包出来的代码其实还是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-runtimebabel-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)
})

webpack-babael7转译ES6
全局引入 @babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大
更改 .babelrc,只转译我们使用到的

/**
* babel配置文件
*/
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",// 按需引入
        "corejs": "3.3.2"
      }
    ]
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}

同时,将全局引入这段代码注释掉,再次打包

// 全局引入
// import '@babel/polyfill'

webpack-babael7转译ES6
参考文章:
https://zhuanlan.zhihu.com/p/35378233
https://juejin.im/post/5a96859a6fb9a063523e2591
https://www.jianshu.com/p/7bc7b0fadfc2
https://github.com/SunshowerC/blog/issues/4

相关推荐