对图片压缩
针对一些对图片像素没有很高要求的图片资源进行压缩,这里可以通过 image-webpack-plugin
或 image-minimizer-webpack-plugin
进行图片资源的压缩,具体配置可以直接点链接进行查阅.
外部扩展 —— externals & cdn
externals 选项就是用于 防止 将某些 import
的包(package) 打包到 bundle 中,而是在运行时(runtime) 再去从外部获取这些 扩展依赖(external dependencies),具体可见
简单来说就是原本应该要被打包到 bundle 中的 js,现在通过配置 externals 选项,将它做外 bundle 之外的资源,即 cdn 资源,在代码运行时再去请求这个资源。
例如,下面就是在项目关于 externals 的配置:
chainWebpack: (config) => { ... // 通过 CDN 方式引入资源 config.externals({ echarts: 'echarts', nprogress: 'NProgress', }); } 复制代码
DllPlugin 插件 —— 优化打包时间
PS: webpack4或以上的版本,vue官方 和 react 官方 都已不推荐使用此插件DllPlugin 插件负责将那些比较稳定(例如 vue/react 全家桶)的库进行打包拆分 bundles,下次在打包时就不需要重复进行打包,因此大幅度提升了构建的速度。
webpack4 版本中,已经集成 DllPlugin 插件,我们只需要进行配置即可,具体可见
- 创建
dll.js
文件,进行简单配置
const path = require('path'); const webpack = require('webpack'); module.exports = { entry: { vendor: ['echarts', 'element-ui', 'vue/dist/vue.esm.js', 'vue-router', 'vuex'], }, output: { path: path.join(__dirname, 'target'), filename: '[name].js', library: '[name]_[hash]', }, plugins: [ new webpack.DllPlugin({ // DllPlugin的name属性需要和libary保持一致 name: '[name]_[hash]', //指定当前目录 path: path.join(__dirname, '.', '[name]-manifest.json'), // context需要和webpack.config.js保持一致 context: __dirname, }), ], }; 复制代码
- 在
package.json
文件中配置script
脚本:"dll": "webpack --config ./dll.js"
- 安装
webpack-cli
,因为原本依赖中并没有安装过这个依赖,而运行这个脚本命令需要webpack-cli
- 执行脚本命令
npm run dll
,生成vendor-manifest.json
文件,这个文件是用于让DllReferencePlugin
能够映射到相应的依赖上
- 在
vue.config.js
中配置DllReferencePlugin
插件,链接到已被打包的依赖上
const { pathResolve } = require('./build/utils.js'); // eslint-disable-line const devConfig = require('./build/webpack.dev.conf.js'); // eslint-disable-line const buildConfig = require('./build/webpack.prod.conf.js'); // 分析工具 const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 资源缓存 const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); // 抽离稳定的第三方库,避免重复打包 const DllReferencePlugin = require('webpack').DllReferencePlugin; // 公共函数 const { versionSet } = require('./build/utils'); // eslint-disable-line // 是否为开发环境 const isDevelopment = process.env.NODE_ENV == 'development'; const vueWebpackConfig = () => { let envConfig = {}; if (isDevelopment) { // 开发 envConfig = devConfig; } else { // 构建 versionSet(); envConfig = buildConfig; } const vueConfig = { // 环境配置 ...envConfig, productionSourceMap: isDevelopment, // 是否在构建生产包时生成sourcdeMap // 拓展webpack配置 chainWebpack: (config) => { // ============ 配置别名 ============ config.resolve.alias .set('@build', pathResolve('../build')) // 构建目录 .set('@', pathResolve('../src')) .set('@api', pathResolve('../src/api')) .set('@utils', pathResolve('../src/utils')) .set('@views', pathResolve('../src/views')); // ============ svg处理 ============ const svgRule = config.module.rule('svg'); // 清除已有的所有 loader。 // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。 svgRule.uses.clear(); // 添加要替换的 loader svgRule.use('svg-sprite-loader').loader('svg-sprite-loader').options({ symbolId: 'icon-[name]', }); // ============ 压缩图片 ============ config.module .rule('images') .use('image-webpack-loader') .loader('image-webpack-loader') .options({ bypassOnDebug: true }) .end(); // ============ 打包分析工具 ============ if (!isDevelopment) { if (process.env.npm_config_report) { config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin).end(); config.plugins.delete('prefetch'); } } // ============ CDN资源引入 ============ config.externals({ // echarts: 'echarts', nprogress: 'NProgress', }); }, configureWebpack: (config) => { // 尽量保证项目中文件后缀的精确 config.resolve.extensions = ['.ts', '.js', '.vue', '.json']; // 处理 babel-loader config.module.rules[12].use.unshift({ loader: 'thread-loader', }); config.plugins.push( // 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source new HardSourceWebpackPlugin({ root: process.cwd(), directories: [], environmentHash: { root: process.cwd(), directories: [], // 配置了files 的主要原因是解决配置更新,cache 不生效了的问题,配置后有包的变化,plugin 会重新构建一部分cache files: ['package.json', 'yarn.lock'], }, }), // DllReferencePlugin 插件 new DllReferencePlugin({ context: __dirname, // manifest就是我们第 2 步中打包出来的 json 文件 manifest: require('./vendor-manifest.json'), }), // 分析工具 new SpeedMeasurePlugin(), new BundleAnalyzerPlugin(), ); }, }; return vueConfig; }; module.exports = vueWebpackConfig(); 复制代码
其他优化
resolve.alias & resolve.extensions
resolve.alias 是用于创建 import
或 require
的别名,来确保模块引入变得更简单
例如,下面是项目中定义的一些别名:
chainWebpack: (config) => { // 配置别名 config.resolve.alias .set('@build', pathResolve('../build')) // 构建目录 .set('@', pathResolve('../src')) .set('@api', pathResolve('../src/api')) .set('@utils', pathResolve('../src/utils')) .set('@views', pathResolve('../src/views')); } 复制代码
resolve.extensions 指定为对应的文件后缀,保证在查找模块时的无用查找和递归等,即保证这个配置里的文件后缀要尽可能少。
减少不必要的解析 —— module.noParse
module.noParse 是防止 webpack 解析那些任何与给定正则表达式相匹配的文件,忽略的文件中 不应该含有import
, require
, define
的调用,或任何其他导入机制,通过忽略大型的 library 可以提高构建性能。
例如,vue-cli 中关于 module.noParse 的配置如下:
代码层面优化
从 webpack-bundle-analyzer 反应的内容,可以发现某些 js 和 css 模块体积相对来说比较大,这个时候可以找到对应的文件梳理逻辑并进行代码优化,如封装 js 逻辑、抽离 css 样式等,即最基本的优化就是少书写重复的样式和逻辑,这样也能避免一些无效的重复编译.
最后
欢迎关注同名公众号《熊的猫》,文章会同步更新!
以上就是一些基于 vue-cli(基于 webapck) 进行的一些优化,如果你之前没有进行过相应优化,那么你可以试试。
最后来看看优化前后,生产打包构建时间的变化:
对 webpack 转 vite 感兴趣的可食用
:基于 webpack 项目接入 vite 你可能需要注意的点