webpack打包性能优化

533 阅读2分钟

首先得知道为什么这个文件比较大,chunk-vendors.xxx.js是一个保存着各种相关依赖的关系的文件,比如导入的elmentUI、echarts、vue、vue-router等各种依赖

1.使用cdn加速

// 修改vue.config.js 分离不常用代码库
module.exports = {
 configureWebpack: config => {
  if (process.env.NODE_ENV === "production") {
   config.externals = { // 不会被打包的库
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'moment': 'moment'
   }
  }
 }
}
// 然后在public文件夹的index.html 使用cdn加载
<!-- CDN -->
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.runtime.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
 

2.开启gzip压缩

一、安装compression-webpack-plugin插件。前端将文件打包成.gz文件,然后通过nginx的配置,让浏览器直接解析.gz文件,可以大大提升文件加载的速度。 1.npm使用下面命令安装

npm install --save-dev compression-webpack-plugin

修改vue.config.js配置

const webpack = require('webpack')
const CompressionWebpackPlugin = require('compression-webpack-plugin')//这个插件可以看官网还是很好用的
const productionGzipExtensions = ['js', 'css']//对什么文件进行压缩
const Timestamp = new Date().getTime();//防止http的缓存每次打包用时间戳进行区分
module.exports = {
	//这里是自己的配置
    configureWebpack: {
      output: { // 输出重构  打包编译后的 文件名称 
          filename: `js/[name]${Timestamp}.js`,
          chunkFilename: `js/[name]${Timestamp}.js`
      },
      plugins: [
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),//该插件能够使得指定目录被忽略,从而使得打包变快,文件变小
        // 下面是下载的插件的配置
        new CompressionWebpackPlugin({
          algorithm: 'gzip',//压缩算法
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),//处理所有匹配此 {RegExp} 的资源
          threshold: 10240,//只处理比这个值大的资源。按字节计算
          minRatio: 0.8//只有压缩率比这个值小的资源才会被处理
        }),
        
        //另一种方法是解决打包后chunk.js文件过多问题。
       //  LimitChunkCountPlugin可以通过合并块来对块进行后期处理
        new webpack.optimize.LimitChunkCountPlugin({
          maxChunks: 5, //控制打包生成js的个数  必须大于或等于 1,此处设置成最多生成5个chunk.js文件
          minChunkSize: 100
        })
      ]
    },
}
 

3.移除prefetch插件和preload

vue 脚手架默认开启了 preloadprefetch,当我们项目很大时,这个就成了首屏加载的最大元凶了 preloadprefetch 都是一种资源预加载机制; preload 是预先加载资源,但并不执行,只有需要时才执行它; prefetch 是意图预获取一些资源,以备下一个导航/页面使用; preload 的优先级高于 prefetch

vue项目目前用的是路由懒加载的模式,在每一次刷新都会重新加载很多js文件,由于路由都没有自定义webpackChunkName,所以就是默认的11(数字).js这种。

打包后默认是chunk-xxx.js

一种方法是移除prefetch,此插件是用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。具体内容官网

vue.config.js配置

  //关闭关闭 preload 与 prefetch
  chainWebpack: config => {
    // 移除 preload(预载) 插件
    config.plugins.delete('preload')
    // 移除 prefetch(预取) 插件
    config.plugins.delete('prefetch')
  }

4.webpack分包

Webapck4.x 版本之前,使用 CommonsChunkPlugin去做分离,webpack 4 最大的改动就是废除了 CommonsChunkPlugin 引入了 optimization.splitChunks。如果你的 mode 是 production,那么 webpack4 就会自动开启 Code Splitting。

chainWebpack:config => {
     config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          // cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'),
            minChunks: 3, //  被至少用三次以上打包分离
            priority: 5, // 优先级
            reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
          },
          node_vendors: {
            name: 'chunk-libs',
            chunks: 'initial', // 只打包初始时依赖的第三方
            test: /[\\/]node_modules[\\/]/,
            priority: 10
          },
          vantUI: {
            name: 'chunk-vantUI', // 单独将 vantUI 拆包
            priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
            test: /[\\/]node_modules[\\/]_?vant(.*)/
          },
            elementUI: {
              name: 'chunk-elementUI', 
              priority: 20,
              test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
                },
        }
      })
      config.optimization.runtimeChunk('single')
}

5.减少打包文件的体积

productionSourceMap 在通过 vue-cli 脚手架生成的项目中,其值默认为 true 。它的作用是用来显示或定义一些"问题"代码。但是对于生产环境,我们可以进行关闭。这样能够对于打包后文件的体积有很大的减少

 module.exports = { 
     productionSourceMap: false,
     ...
 }

6.tree shaking

// vue.config.js
module.exports = {
  configureWebpack: {
    optimization: {
      usedExports: true, // 启用Tree-Shaking

      sideEffects: true, // 表示整个项目全部都有副作用,不会进行Tree-Shaking
      sideEffects: false  //  全部进行tree shaking

      // 如果想设置某些文件不进行Tree-Shaking
      sideEffects: [
        './src/some-side-effect-file.js' 
      ] 
    }
  }
}

上面通过·optimzation.usedExports启用了Tree Shaking功能。

sideEffects默认是false,表示可以进行Tree Shaking,会剔除无用代码。

如果整个项目中存在有副作用的文件,需要设置sideEffects为true来关闭。

同时可以通过sideEffects字段来指定某些文件不要进行Tree Shaking。

这样通过配置,可以启用Tree Shaking来删除项目中未使用的代码,减少打包后的容量。

sideEffects

这时候仍存在一个问题,如果通过模块化引入另一个js文件,即使没有被使用,useExports 也不会进行 tree shaking

// index.js
import './format.js';

// format.js
export function timeFormat() {
  return '2022-01-01';
}

比如上面这段代码,通过 import 语句引入 format.js,但 format.js 导出的函数没有被使用。 此时仍然对于 import 语句进行了编译,我们希望在引入的文件中也进行 tree shaking,删除无用的代码,这个时候在 package.json 中配置 sideEffects 属性来处理。

// package.json 其他配置省略
{
    "sideEffects": false
}

sideEffects 用于告知 webapck 编译器哪些模块有副作用

定义为 false,表示所有的模块都不存在副作用,不需要用到的时候直接删除 定义为数组,告知有副作用的模块,该模块中有副作用的代码会被保留,没有副作用且没有使用到的代码会被删除。

image.png 这样引入的 js 文件没有被使用,进行了 tree shaking,可是 css 资源通过 import 引入也被删除了,也不会编译生成对应的 css 文件,解决方式可以选择在 sideEffects 属性中定义数组,或者处理 css 文件的 loader 中配置(推荐)。

// package.json
{
    "sideEffects": [
        "**.css"
    ],
}

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        // 其它配置省略
        sideEffects: true,
      },
    ],
  },
};

7.自动刷新只重新构建修改的文件

// vue.config.js

module.exports = {
  configureWebpack: {
    // 其他配置
  },

  devServer: {
    watch:true,
    hot:true,
    watchOptions: {
      poll: 1000, //每秒询问1000次
      aggregateTimeout: 500, //防抖,500毫秒内重复的重新构建请求只会构建一次
      ignored: /node_modules/ //忽略对node_modules的监听
    }
  }
}

通过配置 watchOptions,可以优化开发时的模块热替换体验,避免不必要的重启编译。

需要注意 devServer.watch 也需要设置为 true,watchOptions 才会生效

8.优化Loader配置,仅针对需要处理的文件

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('js')
      .test(/\.js$/)
      .include
        .add(path.resolve(__dirname, 'src'))
        .end()
      .use('babel')
        .loader('babel-loader')
        .options({
          cacheDirectory: true
        })
    
    config.module  
      .rule('images')
      .test(/\.(png|jpe?g|gif)(\?.*)?$/)
      .use('url-loader')
        .loader('url-loader') 
        .options({
          limit: 10000  
        })
  }
}

通过chainWebpack获取规则配置,然后使用include仅针对src目录应用babel-loader。

同时使用cacheDirectory开启babel缓存。

另外针对图片使用url-loader,并设置了大小限制,只有小于10kb的图片会转base64编码。

这样只对需要的资源应用Loader进行转换,可以有效优化构建性能。

可以根据项目实际情况,决定哪些文件需要进行Loader转换,只针对该部分文件应用规则。