使用 Webpack 4 和 Babel 7 配置 Vue.js 工程模板

团队最近开始一个新项目,技术栈采用 Vue.js 2 框架。由于是一个较复杂的大型单页应用,决定使用官方推荐的 webpack 工程作为应用工程脚手架。该工程模板中使用了 webpack 3 和 babel 6,由于 webpack 4 已发布,Babel 7(beta) 也出来挺长一段时间了,所以想对工程模板进行升级改造,以尝试工程化开发工具的最新特性。

关键调整

webpack工程模板本身是为大型应用设计的,构建脚本和配置文件的结构相比一般项目稍显复杂,添加了一些开发辅助工具,并且做了一定程度的封装。本次升级也是沿用了此套结构,只是对升级后的几个工具配置项做了调整。几个关键调整:除了 webpack 4 和 Babel 7,配套的 vue-loader 也升级到了 v15(官方的文档已经是v15了);另外为了配套 webpack 4,extract-text-webpack-plugin 插件也被替换成了mini-css-extract-plugin,并且配合 css-hot-loader
可实现提取css文件的热替换。主要配置项调整请看下文,前后配置代码差异可以见这里,完整工程模板库地址:https://github.com/xsbear/vue-webpack4-babel7,有需要的同学可以直接 clone。

基础配置 build/webpack.base.conf.js

这里的主要变化是新的 vue-loader 配置,由于新版配置选项发生变更,原来vue-loader.conf.js里的配置项基本已废弃,本方案直接将transformAssetUrls选项配置在了 vue-loader 的 rule options 里;另外需引入vue-loader/lib/plugin,以确保正确解析 .vue文件 <script>块中的js代码。

开发模式配置 webpack.dev.conf.js

webpack 4 的一个主要变化: 添加 mode 参数,值为 development,移除已废弃的NamedModulesPluginNoEmitOnErrorsPlugin 等插件,另外引入 mini-css-extract-plugin 作为css文件提取插件。当然,如果你不打算在开发模式下使用css提取(使用 style-loader 替代),也可以不引入此插件,且在调用utils.styleLoaders方法配置css模块规则时,extract 参数传 false

#build/utils.js

对 utils里的 generateLoaders 方法做了调整,替换css提取插件,增加 hotReload 参数,供开发、生产模式选择是否要执行css模块热加载。此方法可以根据传入的 usePostCSS 参数或 loader 参数组装成自定义的css预处理或后处理的 loader 组合,结合 styleLoaders 方法再组装完整的css模块rules,如 .css, .postcss, .sass 等。cssLoaders 方法返回中,枚举了多种css预处理格式,实际项目中可根据自己采用的预处理方案进行选择,无需全部配置,如只使用postcss,后面几个预处理格式都可以注释。

生产模式配置 build/webpack.prod.conf.js

这里的主要调整是为适配 webpack 4 的新配置。同样的,新增参数 mode: ‘production’;比较大的调整是 optimization 配置,替代了之前的 CommonsChunkPlugin, UglifyJsPlugin 等插件。另外如 HashedModuleIdsPlugin 等插件也已废弃,无需再配置。这里的重点是替代 CommonsChunkPlugin 插件的 splitChunks 配置项,目前给出的配置是可以满足 vendor 库和业务公用代码库分离的典型场景,更多分离优化的配置,有需要的同学可查找相关教程,由于不是本文重点,这里就不展开了。

Babel 7 配置 .babelrc

Babel 7 的相关依赖包需要加上 @babel scope。一个主要变化是 presets 设置由原来的 env 换成了 @babel/preset-env, 可以配置 targets, useBuiltIns 等选项用于编译出兼容目标环境的代码。其中useBuiltIns如果设为 "usage",Babel 会根据实际代码中使用的ES6/ES7代码,以及与你指定的targets,按需引入对应的 polyfill,而无需在代码中直接引入 import '@babel/polyfill',避免输出的包过大,同时又可以放心使用各种新语法特性。

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
["@babel/preset-env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"useBuiltIns": "usage"
}]
],
"plugins": ["transform-vue-jsx"]
}

transform-runtime

对于开发应用来说,直接使用上述的按需 polyfill 方案是比较方便的,但如果是开发工具、库的话,这种方案未必适合(babel-polyfill 是通过向全局对象和内置对象的prototype上添加方法实现的,会造成全局变量污染)。Babel 提供了另外一种方案 transform-runtime,它在编译过程中只是将需要polyfill的代码引入了一个指向 core-js 中对应模块的链接(alias)。关于这两个方案的具体差异和选择,可以自行搜索相关教程,这里不再展开,下面提供一个 transform-runtime的参考配置方案。

  • 首先安装 runtime 相关依赖

    1
    2
    npm install --save-dev @babel/plugin-transform-runtime
    npm install --save @babel/runtime
  • plugin-transform-runtime 的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "presets": [
    ["@babel/preset-env", {
    "modules": false,
    "targets": {
    "browsers": ["chrome 65"]
    }
    }]
    ],
    "plugins": ["transform-vue-jsx", ["@babel/plugin-transform-runtime", {
    "useBuiltIns": true
    // "polyfill": false
    }]
    ]
    }

这里解释一下 useBuiltIns(默认为 false) 和 polyfill(默认为 true) 两个选项,如果不配置或使用默认值,那 Babel 是会自动 polyfill 的(引入core-js polyfill 的alias),哪怕你指定的 targets 只有最新版chrome,最终编译出来的包会比较大,这不是我们所希望的。解决办法是通过配置 polyfillfalseuseBuiltInstrue (二选一即可),从而不引入 core-js 的 polyfill 。如果目标环境是较新的浏览器,可以按此配置,但是有一些最新的特性可能还需要单独 polyfill。个人认为,如果开发应用可以直接使用 babel-polyfill 方案,相对来说简单方便,编译输出的包大小也是可控的。

其它

原工程中有测试模块,引入了相关依赖包。此次升级,为适配 webpack 4 和 babel 7 也调整了相关依赖及配置。如果你项目中不需要引入测试模块,可以考虑将以下依赖包移除:

1
2
3
4
5
6
7
8
9
10
11
12
"@babel/register"
// 这个是 jest 为兼容 Babel 7 的一个依赖,注意不是 @babel/core
"babel-core": "^7.0.0-bridge.0"
"babel-jest"
"regenerator-runtime"
"chromedriver"
"selenium-server"
"jest"
"jest-serializer-vue"
"nightwatch"
"selenium-server"
"vue-jest"

其他的一些调整如 postcss.config.js 等,可自行查看相关差异,不再赘述。

总结

webpack 工程作为一个针对大型应用设计的官方通用模板,为了兼容并包及开箱即用对工程配置做了一定程度的封装及开发辅助工具的整合,加快了项目开发的上手速度,但也对编译、构建工具的升级调整带来了一些不便。具体到实际项目中,可以根据情况对 webpack 的相关配置进行简化,比如只保留 webpack.base.conf, webpack.dev.confwebpack.prod.conf 3个配置文件的结构即可,便于维护。

本人对于 webpack,Babel 等工具的掌握也仅限于应用层面,只是想把此次摸索的成果与大家进行分享,如描述有误或不到位的地方,欢迎指正交流。

参考:
Setting up a Vue.js Project with webpack 4 and Babel 7
Upgrade to Babel 7