记一次 webpack3.x + vue-cli2 老项目的打包文件分割优化实战

586 阅读2分钟

前言:优化打包就是不断的分析包大小,然后不断改打包配置,然后再继续不断分析包大小的过程

先分析

  • webpack.prod.conf.js 文件的 output 配置:
output: {
  // 我看之前脚手架没有追加 [name] 配置,加上后分析图中会显示chunk名方便看哪个文件大了
  chunkFilename: utils.assetsPath('js/[id].[name].[chunkhash].js')
}
  • package.json 文件配置分析命令,不断执行查看 chunk 大小分布:"report": "npm run build --report"

webpack.base.conf.js 中做了啥

{
    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 1, 
      name: utils.assetsPath('media/[name].[hash:7].[ext]')
    }
  },
  {
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 1, // 我把这里调成了 1,为了避免资源转成 base64 占据文件大小,原来是 10000,
      name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
    }
  }

webpack.prod.conf.js 中做了啥

  • 简单处理部分
// 这一步和后端商量,可以先不做
module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 1, // 10000,
          // 为了图片放静态服务器先把 hash 去掉了 原来是.[hash:7] 
          name: utils.assetsPath('img/[name].[ext]'),
          // 配置了云服务访问图片资源的地址,减轻一个服务器的访问压力
          publicPath: 'https://xxxx.oss-cn-xxx.aliyuncs.com/'
        }
      },
    ]
},

// 利用 CSSSplitWebpackPlugin 插件分割了 app.css 成多个模块的子 css 减少单个文件大小
new CSSSplitWebpackPlugin({
  size: 4000,
  filename: `${config.rootPath}static/css/[name]-[part].[ext]`
})

new webpack.optimize.CommonsChunkPlugin({
  name: 'app',
  async: 'vendor-async', 
  children: true,
  minChunks: Infinity, // 我把这里调整成了无限大,这样异步块没达到无限大,就只会一个异步块一个包,不会合到一起形成大文件,原来是3
}),
  • 复杂处理部分,就是通过不断分析包大小,然后不断分割块
// 从 vendor 中提取出 echarts
new webpack.optimize.CommonsChunkPlugin({
  name: 'echarts',
  chunks: ['vendor'],
  minChunks: function(module) {
    return module.resource && /echarts/.test(module.resource)
  }
}),
// 从 echarts 中提取出 echarts-component
new webpack.optimize.CommonsChunkPlugin({
  name: 'echarts-component',
  chunks: ['echarts'],
  minChunks: function(module) {
    return module.resource && /component/.test(module.resource)
  }
}),
// 从 ['vendor', 'app'] 中提取出 element-ui
new webpack.optimize.CommonsChunkPlugin({
  name: 'element-ui',
  chunks: ['vendor', 'app'],
  minChunks: function(module) {
    return module.resource && /element-ui/.test(module.resource)
  }
}),
// 从 app 中提取出 app-assets
new webpack.optimize.CommonsChunkPlugin({
  name: 'app-assets',
  chunks: ['app'],
  minChunks: function(module) {
    return module.resource && /assets/.test(module.resource)
  }
}),
// 从 app 中提取出 app-components
new webpack.optimize.CommonsChunkPlugin({
  name: 'app-components',
  chunks: ['app'],
  minChunks: function(module) {
    return module.resource && /components/.test(module.resource)
  }
}),

// 千万记得调整这个插件,因为上面手动分割了很多包
new HtmlWebpackPlugin({
  // 这里要调整成手动排序 chunk
  chunksSortMode: 'manual',
  // 把我们自己分割好的包的 chunk 名一个个按顺序记录在这里,不然打包会报错
  chunks: [
    'manifest',
    'vendor',
    'echarts',
    'echarts-component',
    'element-ui', 
    'app-assets',
    'app-components',
    'app'
  ]
}),

最后是异步组件和路由懒加载的使用

// 如果发现多个路由文件合成了一个非常大的文件,想拆分路由文件懒加载,可以配置路由
{
    path: '/pathname',
    // 利用 () => import(/* webpackChunkName: "chunkname" */ '') 语法,
    // vue-router 文档有写,不同的 webpackChunkName 会分开打包成各自的路由文件
    component: () => import(/* webpackChunkName: "chunkname" */ '@/pages/pagename.vue'),
    name: 'routename'
},

// 如果发现了一个文件特别大,可以看看是不是引入的组件太大,然后利用下面的方式做成异步组件,就会拆分出一个文件
components: {
  DailyView: () => import(/* webpackChunkName: "daily" */ '@/pages/daily.vue')
},

后端帮忙处理部分

  • 后端配置服务器支持 gzip 压缩后,文件会更小
  • 后端把前端图片资源放到了另一个服务器cdn,说是不同域名的异步加载资源数量会更多