webpack 4 配置实践

本文主要介绍基于 webpack 4的前端开发环境构建,主要讲解以下内容

为什么要升级 webpack 4

webpack 4 新特性介绍

webpack 4 配置过程讲解

webpack 4 公共依赖拆分解决方案

代码检查机制配置

目前的构建系统的局限性

目前已有项目的构建环境, 各种工具的版本都比较低, 基础框架不统一, 在提高页面性能、技术新特性等方面有明显不足之处,一定程度上影响前端开发体验和开发效率,当前webpack使用是 1.x 版本, 项目中同时使用了 vue 1.x 和 2.x 版本, 所以 vue-loader的版本也比较低, 存在下面的一些问题

对node_modules支持不友好, 目前使用的方案是将第三方库文件放到项目的 lib 目录下提交到git并手动管理

项目中混合了 Vue 1.x 和 2.x 版本, 因为新版本 vue-loader 不支持 Vue 1.x 版本, 所以无法升级 vue-loader

现有打包环境中由于 vue-loader 版本低, 无法在编译期生成 render 方法

由于无法编译时生成 render 函数, 所以加载的 Vue 除运行时依赖外, 还有 compiler 部分, 加载代码体积增大, 还需要 compiler 在运行时将字符串模板进行编译, 效率不高

不能很好的支持code splitting 以及异步加载. 由于webpack output 路径是固定的, 而项目下有多个子目录, 原来是通过在 entry name 中包括子路径将打包后的文件输出到不同的目录, 如果使用 code split 的话, 生成的文件就会输出到一个文件夹下, 无法分目录部署

不支持拆分公共代码, 公共库文件会和项目文件打包成一个文件, 生成文件体积大, 项目迭代后, 会重复打包下载第三方库

由于升级webpack 需要也升级 vue-loader, 而新版 vue-loader 只支持 vue 2.x 版本, 这对于项目中存在的 vue 1.x 项目是不行的. 所以更好的办法是去掉包袱, 重新建一个新仓库, 从零开始进行配置, 当有新需求, 或者原来项目有迭代的时候将旧的项目迁移到新仓库中.

新构建系统支持特性

既然是从零开始, 所以希望尽可能的支持比较多的特性, 提升开发体验. 目前支持的特性包括:

support node_modules import

support TypeScript

support dynamic import and code splitting via

support / compiled to ES5

support scss/sass files

support webpack-dev-server livereload

support ESLint for and files

support TSLint for files

support Stylelint for , and files

support git hook for ESLint/TSLint/Stylelint

support split node_modules vendors to separate js and css files automatically

support split multi entry shared node_modules vendors to separate js and css files automatically

webpack 4 和原项目中 webpack 1 配置对比比较大的不同

原来项目中是使用的是 webpack 1, 所以在配置 webpack 4的过程中, 感觉有不少和原来一些不同的地方, 这里写几个比较明显的

简化了部分配置

一些大部分人都会用的配置被作为默认配置, 并且加了 参数, 可以设置为 和 来快速切换默认配置. 所以在新配置中通过 来切换.

对 long-term-cache 支持更好

除了file-loader, url-loader(实际用的也是file-loader的占位符)之外, 使用 , 的地方都可以使用 来代替. 只和生成的文件的最终内容有关, 不会轻易变化

去掉了 CommonsChunkPlugin, 使用 splitChunksPlugin 来替代.

默认情况下, splitChunks 只会对按需加载(on-demand)也即异步加载模块起作用, 因为初始模块(initial)需要通过 html 中的 script 标签进行加载.

webpack 会按如下条件自动拆分代码块.

关于 splitChunks 的详细内容可以看官方介绍 webpack 4: Code Splitting, chunk graph and the splitChunks optimization

如果不希望使用默认配置, 可以通过 进行配置. 所以在新项目中通过覆盖默认配置, 抽取所有的 initial node_modules 模块到 文件. 配置代码如下.

代码块可以被共享, 或者代码块来自于 node_modules 文件夹

代码块在 min+gzip 之前体积超过 30KB

按需加载的代码块,最大数量应该小于或等于5

初始加载的代码块,最大数量应该小于或等于3

原来使用的 extract-text-webpack-plugin 不再保证兼容 webpack 4

官方出了 mini-css-extract-plugin 使用 webpack 4 的新特性来完成原来 extract-text-webpack-plugin 的功能, 并且和原来的 extract-text-webpack-plugin 相比有以下优点:

异步加载, 当 js 文件被异步加载时, 需要的 css 文件也会自动加载

没有重复编译, 性能比原来要好

易用性好

因为只针对 css 文件, 所以自动带了一些优化, 比如在 时自动 minify

vue-loader v15 和之前版本的使用不同

由于原来 vue-loader 没有用到太多特性, 所以新仓库的 webpack 配置和原来的 webpack 配置相比, 受影响最大的两点

其它的 break change 可以看 vue-loader 的升级指南 Migrating from v14

使用 v15 需要给 webpack plugins 传入一个插件实例

v15 版本不再对 SFC 文件中的 , 和 部分使用自己单独的 loader 配置, 而是和普通文件共享 webpack 中的配置, 减少了配置成本

构建方案

整体目录结构和原来的旧仓库保持一致, 可以方便复用原来的部署系统, 做很少的修改就可以完成对新仓库的部署

入口文件命名规则和原来相同, 但是支持使用 typescript, 所以每个入口文件的名称应该为 或者 , 两者不同时存在

webpack 配置输出目录为入口文件所在目录, 好处是相对路径引用的图片、字体以及输出的js、css, 输出位置都不会乱.

文件输出时以 , 和 命名, 所以不同入口文件不会出现输出文件重名的情况

给 webpack 传入的是一个配置数组, 每个配置项中只包含一个入口文件. 优点是每个入口文件的 node_modules 模块可以精确拆分到对应的 vendor 文件中. 如果多个入口文件放在一个配置项中, 当使用 splitChunks 拆分公共模块时, 会造成多个入口文件的 node_modules 混在一起, 造成冗余加载

构建方案的问题及解决

问题: 虽然每个入口文件的 node_modules 模块被抽取到单独的文件, 但是仍然会有冗余加载的问题. 比如: 多页面时多个入口文件引用相同的 node_modules, 相同的第三方库被打包到多个 vendor 文件中, 每次进入一个页面, 都要加载此页面对应的 vendor 文件, 造成冗余加载

解决方案: 将多个入口文件共享的 node_modules 模块抽取到单独的文件, 比如 , 每个页面除了各自的 , 文件外, 再引入一个 文件即可. 如果感觉初始请求太多, 或者不希望生成 文件, 可以在入口文件所在目录下放一个 文件, 则此目录下的所有入口文件都不生成

生成文件关系示例

那么如何获取到所有入口文件共享的 node_modules 模块呢? 最开始提出了两种方案

第一种方案, 在入口文件所在目录添加文件 , 内容为一个数组, 数组中维护公共模块. 比如

在 webpack 打包过程中, 读取此文件, 生成 配置. 这种方案的最大问题就是, 手动管理不智能, 由于 import 引用关系复杂, 可能没法准确知道有哪些模块是被共享的, 并且如果忘记修改此文件, 就不能很好的发挥作用.

第二种方案, 自动获取同目录下所有入口文件都依赖的 node_modules 模块作为 中包含的模块, 剩余的模块则放入入口文件对应的 中, 如果没有剩余的模块, 则此文件不生成. 此方案的好处是, 每次 webpack 打包前会自动精确解析,并且不需要人工管理, 出错概率小.

经过分析对比, 我们采用第二种方案打包共用包, 所以需要解析 , 和 , 取出所有的 import 声明, 这里考虑使用 js parser 解析文件, 获取 import 声明, 看上去可能会比使用正则匹配难一些, 但是更精确. 由于项目中存在 ts 文件, 一般的 js parser 不一定支持 ts, 所以最终使用 typescript 的 parser.

在生成 webpack 配置时, 对每个入口文件使用 获取此文件与同目录下其它入口文件共享的 node_modules 模块, 最终得到一个数组, 通过此数组生成 所对应的 配置

调用顺序: --> -->

的作用是找出传入文件所在目录下所有入口文件的 vendors, 然后比较出每个入口文件都依赖的模块, 作为 返回.

的作用是以一个入口文件为起点, 通过分析此文件引入的外部文件, 递归的取出所有 , 和 文件中引用到的 node_modules 模块并去重

parseImports 的作用是分析一个文件引用到的所有外部文件, 包括 node_modules 模块和普通文件.

分析方法为, 通过使用 typescript parser 得到 AST tree, 取出所有的 声明语句, 然后得到 module 名字. 并且由于模块声明语句只会出现在每个文件顶层, 所以只需要通过 typescript 的 遍历一层子节点即可

对普通 和 文件使用的是 typescript parser 直接进行处理, 对 文件则先是通过 vue-template-compiler 取出文件中的 部分, 然后再将此部分交给 typescript parser 进行处理.

为了加速获取过程, 会对每个文件的结果进行缓存. 因为不同的入口文件可能会引用同一文件, 就会进行多次 parseImports.

由于 webpack 支持不加后缀直接引用然后由 webpack 进行解析相应的文件, 所以这里也加了支持, 并且和 webpack 共享 配置

});

returnparseImports.importsCache[file] = {vendors: vendors,files: files};

}

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180601G0T3Z900?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券