webpack5 的使用(五):babel 转译 js 代码

7,681 阅读3分钟

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

前言

在前面的文章里,我们已经可以通过 js 进行 import 各种资源。现在还有个问题,就是需要将 es6+ 代码需要转译为 es5 和添加 polyfill,解决浏览器兼容性问题。而 babel 就是专门解决这个问题。

babel

由于 babel 是分为多个包,初学者一般都会被搞混,在这里,先解释各个常用的 babel 包的作用。

  1. @babel/core: 只会将 es6+ 语法转换为 es5 语法,但是没办法转换新 api,如:promise。
  2. @babel/preset-env: 智能预设,包含了一组插件,控制如何添加 polyfill(兼容新 api),可以按需添加 polyfill,不会污染全局环境和原型。
  3. @babel/plugin-transform-runtime: 需要和 @babel/runtime (这玩意我也不清楚是什么东东)配合使用,使辅助代码作为一个独立模块引入,可以减少编译后代码的体积。(这一块,官方文档也没有解释清楚,仅是我个人观点)
  4. @babel/cli: 提供 cli 命令行工具,用来通过命令行编译文件,适合安装到本地项目里(注意:该篇文章没有介绍如何使用该包)。

在一些 babel7.4+ 之前,还有一个重要的插件 @babel/polyfill,该插件已经被弃用了,在这里提醒一下大家,不需要再使用了,以免被一些旧博客误导。

  1. @babel/polyfill: 包括 core-js 和 regenerator runtime 模块,添加各种 es5 没有的新 api 的 polyfill,由于默认没有按需添加,因此会变得很大,这是源码之前运行的插件,需要 -S 安装。

而这些 babel 包如果需要在 webpack 项目上使用,则需要使用到 babel-loader。

使用 babel

安装 babel-loader、@babel/core、@babel/preset-env,用来将 es6+ 语法转换为 es5,并按需添加 polyfill

npm i -D babel-loader @babel/core @babel/preset-env

在 webpack 里配置以下代码,注意需要添加 exclude,防止转换 node_modules 和 bower_components 的代码,包管理下载下来的代码是不需要转换的。

webapck.config.js

module.exports = {
    ...
    module: {
     rules: [
           ...
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env']
              }
            }
          }
      ]
    }
}

如果需要添加插件,比如:@babel/plugin-proposal-object-rest-spread,可以在 options 里添加 plugins 属性引入相应的插件。

备注:该插件已经被 @babel/preset-env 包含,不需要额外安装,该插件用于对 es6 的 reset 参数和 spread 扩展运算符做处理

webpack.config.js

module.exports = {
    ...
    module: {
     rules: [
           ...
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env'],
                plugins: ['@babel/plugin-proposal-object-rest-spread']
              }
            }
          }
      ]
    }
}

提升 babel 转译速度

在 options 里配置 cacheDirectory 为 true,开启缓存 babel-loader 执行的结果,之后的 webpack 每次构建,都会尝试读取缓存。缓存的目录默认为项目的 node_modules/.cache/babel-loader,如果 node_modules 没有找到,则缓存到系统默认的临时文件夹(不清楚怎么更换缓存目录)。

webpack.config.js

module.exports = {
    ...
    module: {
     rules: [
           ...
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env'],
                plugins: ['@babel/plugin-proposal-object-rest-spread'],
                cacheDirectory: true,
              }
            }
          }
      ]
    }
}

执行 npm run build,node_modules 里面生成了一些 babel 转译相关的缓存文件

image.png

减少 babel 编译后的代码体积

babel 对一些公共方法使用了一些辅助代码(如:_extend),它会默认添加到每一个需要辅助代码的文件中。需要安装 @babel/plugin-transform-runtime 和 @babel/runtime 让辅助代码作为一个独立模块引入。

npm i -D @babel/plugin-transform-runtime @babel/runtime

在 plugins 里添加 @babel/plugin-transform-runtime

webpack.config.js

{
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: ['@babel/plugin-proposal-object-rest-spread', 
          '@babel/plugin-transform-runtime'],
        cacheDirectory: true,
      }
    }
}

完整代码

目录

image.png

webpack.config.js

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

console.log('环境变量:', process.env.NODE_ENV)

module.exports = {
  // entry: path.resolve(__dirname, '../src/js/index.js'),
  entry: {
    main: path.resolve(__dirname, '../src/js/index.js'),
    header: path.resolve(__dirname, '../src/js/header.js'),
    footer: path.resolve(__dirname, '../src/js/footer.js'),
  },
  output: {
    // filename: 'main.js',
    filename: 'js/[name].[fullhash].js',
    path: path.resolve(__dirname, '../dist'),
    // assetModuleFilename: 'img/[hash][ext][query]' // 全局指定资源文件输出位置和文件名
  },
  // devServer: {
  //   port: 3000,
  //   hot: true,
  //   contentBase: '../dist'
  // },
  plugins: [
    // new HtmlWebpackPlugin({
    //   title: '首页'
    // }),
    // 配置多个 HtmlWebpackPlugin,有多少个页面就配置多少个
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../src/html/index.html'),
      filename: 'index.html',
      chunks: ['main'] // 与入口文件对应的模块名(entry 配置),这里可以理解为引入 main.js
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../src/html/header.html'),
      filename: 'header.html',
      chunks: ['header']
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../src/html/footer.html'),
      filename: 'footer.html',
      chunks: ['footer']
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[fullhash].css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          //'style-loader', 'css-loader'
          MiniCssExtractPlugin.loader, 'css-loader'
        ]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'
        ]
      },
      // {
      //   test: /\.(jpe?g|png|svg|gif)/i,
      //   type: 'asset/resource',
      //   generator: {
      //     filename: 'img/[hash][ext][query]' // 局部指定输出位置
      //   }
      // },
      // {
      //   test: /\.(jpe?g|png|svg|gif)/i,
      //   type: 'asset/inline',
      // },
      {
        test: /\.(jpe?g|png|svg|gif)/i,
        type: 'asset',
        generator: {
          filename: 'img/[hash][ext][query]' // 局部指定输出位置
        },
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 限制于 8kb
          }
        }
      },
      {
        test: /\.txt/,
        type: 'asset/source'
      },
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-object-rest-spread', 
              '@babel/plugin-transform-runtime'],
            cacheDirectory: true,
          }
        }
      }
    ]
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src'),
      // 下面可以继续新增别名
    }
  }
}

系列文章

webpack5 的使用(零):概念
webpack5 的使用(一):起步
webpack5 的使用(二):多个环境配置
webpack5 的使用(三):加载 css
webpack5 的使用(四):加载资源文件
webpack5 的使用(五):babel 转译 js 代码
webpack5 的使用(六):优化