【原创】webpack如何优化vue项目

935 阅读1分钟

前言

日常开发中,Vue框架通常都会使用Webpack进行构建,随着项目不断迭代,项目逐渐变得庞大,然而项目的构建速度随之变得缓慢,于是对Webpack构建进行优化变得刻不容缓。通过适当的方法优化后,项目的构建速度提高了50%。现将相关优化方法进行总结分享。

一.优化loader配置

由于Loader对文件的转换操作很耗时,所以需要让尽可能少的文件被Loader处理。我们可以通过以下3方面优化Loader配置:(1)优化正则匹配(2)通过cacheDirectory选项开启缓存(3)通过include、exclude来减少被处理的文件。实践如下:

项目原配置:

{
  test: /\.js$/,
  loader: 'babel-loader',
  include: [resolve('src'), resolve('test')]
},

优化后配置:

{
  // 1、如果项目源码中只有js文件,就不要写成/\.jsx?$/,以提升正则表达式的性能
  test: /\.js$/,
  // 2、babel-loader支持缓存转换出的结果,通过cacheDirectory选项开启
  loader: 'babel-loader?cacheDirectory',
  // 3、只对项目根目录下的src 目录中的文件采用 babel-loader
  include: [resolve('src')]
},

二.优化UglifyJS插件

webpack默认提供了UglifyJS插件来压缩JS代码,但是它使用的是单线程压缩代码,也就是说多个js文件需要被压缩,它需要一个个文件进行压缩。所以说在正式环境打包压缩代码速度非常慢(因为压缩JS代码需要先把代码解析成用Object抽象表示的AST语法树,再去应用各种规则分析和处理AST,导致这个过程耗时非常大)。

因此我们需要可以并行处理多个子任务,多个子任务完成后,再将结果发到主进程中,有了这个思想后,因此 ParallelUglifyPlugin 插件就产生了,当webpack有多个JS文件需要输出和压缩时候,原来会使用UglifyJS去一个个压缩并且输出,但是ParallelUglifyPlugin插件则会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS去压缩代码。无非就是变成了并行处理该压缩了,并行处理多个子任务,效率会更加的提高。

安装 webpack-parallel-uglify-plugin 插件

然后在webpack.config.js 配置代码如下:

// 引入 ParallelUglifyPlugin 插件const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');​module.exports = {  plugins: [    // 使用 ParallelUglifyPlugin 并行压缩输出JS代码    new ParallelUglifyPlugin({      // 传递给 UglifyJS的参数如下:      uglifyJS: {        output: {          /*           是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,           可以设置为false          */          beautify: false,          /*           是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false          */          comments: false        },        compress: {          /*           是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用           不大的警告          */          warnings: false,          /*           是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句          */          drop_console: true,          /*           是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不           转换,为了达到更好的压缩效果,可以设置为false          */          collapse_vars: true,          /*           是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx'  转换成           var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false          */          reduce_vars: true        }      }    }),  ]}

打包对比基本上大小能相差30%!!!!

三.减少冗余代码

babel-plugin-transform-runtime 是Babel官方提供的一个插件,作用是减少冗余的代码 。 Babel在将ES6代码转换成ES5代码时,通常需要一些由ES5编写的辅助函数来完成新语法的实现,例如在转换 class extent 语法时会在转换后的 ES5 代码里注入 extent 辅助函数用于实现继承。babel-plugin-transform-runtime会将相关辅助函数进行替换成导入语句,从而减小babel编译出来的代码的文件大小

四.DllPlugin分包

通过DllPlugin插件分离出第三方包

  • 新建webpack.dll.conf.js

const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require("clean-webpack-plugin");

module.exports = {
  entry: {
    vendor: [
      'vue',
      'vue-router',
      'vuex',
      'axios',
      'element-ui',
      'echarts'
    ]
  },
  output: {
    filename: '[name]_dll_[hash:6].js', // 产生的文件名
    path: path.resolve(__dirname, '../static/dll'),
    library: '[name]_dll_[hash:6]'
  },
  plugins: [
    new CleanWebpackPlugin({
      root: path.resolve(__dirname, '../static/dll'),
      dry: false // 启用删除文件
    }),
    new webpack.DllPlugin({
      name: '[name]_dll_[hash:6]',
      path: path.resolve(__dirname, '../static/dll', '[name].dll.manifest.json')
    })
  ]
};
  • 修改webpack.prod.conf.js

使用add-asset-html-webpack-plugin动态添加dll.jshtml

需要注意

  1. add-asset-html-webpack-plugin要在HtmlWebpackPlugin后引入;

  2. html-webpack-plugin依赖包版本4.0.0-alpha会出个问题,添加上去的路径会变成undefined需要是3.2.0版本

const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
...
plugins: [
  // 插入dll json
  new webpack.DllReferencePlugin({
    context: path.join(__dirname),
    manifest: require('../static/dll/vendor.dll.manifest.json')
  }),
  new HtmlWebpackPlugin(),
  // 插入 dll js
  new AddAssetHtmlPlugin([{
    publicPath: config.build.assetsPublicPath + 'static/dll/',  // 注入到html中的路径
    outputPath: 'static/dll/', // 输出文件目录
    filepath: resolve('static/dll/*.js'), // 文件路径
    includeSourcemap: false,
    typeOfAsset: "js"
  }])
]

五.按需加载代码

通过vue写的单页应用时,可能会有很多的路由引入。当打包构建的时候,javascript包会变得非常大,影响加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
项目中路由按需加载(懒加载)的配置:

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

六.提取公共代码

如果每个页面的代码都将这些公共的部分包含进去,则会造成以下问题 :
• 相同的资源被重复加载,浪费用户的流量和服务器的成本。
• 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
如果将多个页面的公共代码抽离成单独的文件,就能优化以上问题 。Webpack内置了专门用于提取多个Chunk中的公共部分的插件CommonsChunkPlugin。
项目中CommonsChunkPlugin的配置:
// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function(module, count) {
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    );
  }
}),
// 抽取出代码模块的映射关系
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
}),

总结:

在现实项目中,项目上线后无疑就会要进行优化,特别是使用webpack打包后的项目,如果没有进行优化,仅靠一般的脚手架搭建默认的配置,打包出来的项目性能堪忧,最直接体现的就是首屏加载速度,也是前端面试中经常考验的哦,不慌,经过本文以上六种(包括但不限于)优化,打包从30分钟,到2分钟不到,整体还有优化空间,可以使用其他cdn等再进行优化方式,快去试试吧!

------------------------------------------------------------------------------------------------