vue项目中使用webpack打包优化

437 阅读8分钟

vue项目中使用webpack打包优化

项目的优化是为了去除项目冗余,使项目加载渲染更快,此篇文章只会说对我目前项目有用的几个优化的点,后续会再更新

————————————————此次更新 2022/10/17

这个标题百度的话会有多种方案,因为我目前的项目比较小,打包完不到4MB,所以有些优化可能用不上

以我目前的这个vue-cli4的项目来讲,配置都是在 根目录下的 vue.config.js 这个文件中进行的

对我项目有用的

项目打包分析的可视化工具

下载到项目中

# NPM 
npm install --save-dev webpack-bundle-analyzer 

# Yarn 
yarn add -D webpack-bundle-analyzer

在vue.config.js文件中配置

    const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;// 引入
    module.exports = {
        // 。。。。省略其他配置
            chainWebpack:config=>{// 使用
            config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
        }
    }
    
    

这样你每次打包、启动项目都会给你打开这样的一个页面,就是你项目中用到的所有依赖,可以针对某一块进行优化。

每一个大块都是一个js文件

image.png

1、打包优化moment.js包,无需下载任何插件,有webpack即可

通过上面的可视化分析,我发现我项目中的moment.js比较大,希望针对他进行一个优化

优化前:

image.png 可以看到只是moment.js就占用了六百多kb,这对我的项目来说绝对是需要优化的点了

方案:

vue.config.js


    const webpack = require('webpack');// 引入webpack
configureWebpack: config =>{
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /^\.\/(zh-cn|vi)$/i), //减小moment语言包
    // 这段配置大概意思就是去除了moment.js包中的其他语言,只留下了中文部分,从而缩减了体积
}

优化后

image.png 可以明显看到moment的体积大大缩小了

有用!

2、注释与console的消除

我们的线上项目一般是不需要注释和控制台输出的,将其处理掉也可以缩小我们项目的部分冗余

直接引入即可,无需下载

const UglifyJsPlugin = require('uglifyjs-webpack-plugin') //引入插件
configureWebpack: config =>{
    config.plugins.push(
        new UglifyJsPlugin({  // 使用插件
          uglifyOptions: {
            // 删除注释
            output: {
              comments: false
            },
            // 删除console debugger 删除警告
            compress: {
              drop_console: true, //console
              drop_debugger: false,
              pure_funcs: ['console.log'] //移除console,这是个数组,也可以后面继续扩展你想要清除的,比如console.error等
            }
          }
        }),
    )
}

这个优化对于项目中注释和console比较多的效果会更好,可以对比下配置前与配置后打包体积的大小

3、图片的压缩

因为我们项目中使用到的图片还是比较多的,所以这也是一个优化的大点,图片少的可以忽略~~~

下载这两个loader,处理对图片的压缩

#NPM 
npm i url-loader
npm i file-loader

vue-cli项目在vue.config.js的配置

module.exports = {
  chainWebpack:config=>{
        config.module.rule('images')
      .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)// 针对这些图片类型压缩
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
    .end()
    .use('url-loader')
    .loader('file-loader')
    .options({
      name: 'assets/[name].[hash:8].[ext]'
    }).end()
    config.module.rule('svg')
      .test(/\.(svg)(\?.*)?$/)// 针对svg类型图片压缩
      .use('file-loader')
      .loader('file-loader')
      .options({
        name: 'assets/[name].[hash:8].[ext]'
      })
  },
}

这个对于我目前这个项目的体积优化还是蛮大的,从4M多到3M吧,这个跟项目中图片较多有关系,另外压缩图片方法还有很多其他的配置,这里只是我项目中用到的一种

这里重点提一下tinypng-webpack-plugin这个插件

它是收费的,你申请一个key每个月有一个免费额度,使用完就不能压缩了

我接手这个项目的时候,使用的就是这个,然后某一天,项目突然运行不起来了,终端一直显示图片正在压缩,折腾了一下午,最终发现是当月的免费额度用完了,重新申请了key就可以了,太无语了

需要先下载,这里不推荐使用,所以感兴趣的可以自己去搜索

const tinyPngWebpackPlugin = require('tinypng-webpack-plugin'); // 引用
new tinyPngWebpackPlugin({
      // key: "4cQVfjVm5MWKNpvgmTgpGnXg0DlRP6Kd",
      /* 项目启动时如果一直出现 【tinyPNG is compressing imgs】 提示,请重新申请key
      /* key的申请地址 https://tinypng.com/developers
      */
      key: "5y1gdmrpdx3MRpJxBq5Zx9tTH7bTmVQl"
})

3、使用splitChunks拆分第三方库

技术要求:js文件在浏览器中加载要求size不得大于244kb,遂将大文件进一步拆分

webpack打包时,默认的最大资源大小限制为什么是244KB? - 知乎 (zhihu.com)

splitChunks配置介绍:webpack 中文文档 (docschina.org) 文档中有详细说明各个配置项的作用。

拆分第三方库之前,app.js文件达到了1M,没有达到要求 image.png

拆分后app.js中的vue、vue-router、core-js被拆分出去,app文件减小 image.png

在浏览器中运行的体积,js文件体积均在244kb以下

image.png

以下面为例,vue-cli项目中在vue.config.js中将vue、vue-router、core-js 单独拆分为一个js文件

module.exports = {
   configureWebpack: config =>{
      config.optimization = {
        splitChunks: {   // 使用splitChunks将第三方库拆分
          chunks: 'all',
          cacheGroups: {
            'vue': {
              name: 'vue',
              test: /[\\/]node_modules[\\/]vue[\\/]/,
              priority: 10
            },
            'vue-router': {
              name: 'vue-router',
              test: /[\\/]node_modules[\\/]vue-router[\\/]/,
              priority: -10
            },
            'core-js': {
              name: 'core-js',
              test: /[\\/]node_modules[\\/]core-js[\\/]/,
              priority: -10
            },
          }
        }
      },
   }
}

webpack创建的vue项目配置:在build/webpack.prod.conf.js中配置

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
    entry: { // 想要拆分的第三方插件
    // key可以自己定义,value可以参考package.json中的插件名
    //3rd party libraries
    'vue-qrcode-reader': 'vue-qrcode-reader',
    'vue': 'vue',
    'moment': 'moment',
    // 'vant': 'vant',
    // 'vue-router': 'vue-router',
  },
  plugins: [
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
          drop_console: true, //console
          drop_debugger: false,
          pure_funcs: ['console.log'] //移除console
        },
        // 删除注释
        output: {
          comments: false
        },
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
   
    new ExtractTextPlugin({  // css压缩
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      allChunks: true,
    }),
     // 这里将上面entry中定义的key放在names的数组中
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vue-qrcode-reader', 'vue', 'moment' ],
      minChunks: Infinity
    }),
  ]

第三方插件分离时可以多打包看一下可视化打包结果,是否为最优

4、使用externals将部分第三方库剔除在打包外

Less 快速入门 | Less.js 中文文档 - Less 中文网 (bootcss.com)

less作为css的预处理器,可以更高效、更直观的写出优雅的css

但浏览器最终还是接收的还是css文件,less只是更方便我们书写。

所以我们是否可以打包的时候将less转为css,打包时将less.js包排除在外,这样既享受到了less带来的方便,还没有增加我们的打包体积。

试一下吧

首先确保你没在页面中的任何地方引入less,比如在main.js文件中这样

#  import less from "less"

#  Vue.use(less)

我们使用less,只需要安装依赖,然后就可以直接使用,并不需要在main.js文件中再引入

不然打包时将less排除,代码中又用到,会报undefined错误

配置如下

外部扩展(externals) | webpack 中文网 (webpackjs.com)

module.exports = {
  chainWebpack:config=>{
    config.externals({  // 将这些包 排除在build之外
      less: "less"
    })
  }
}

webpack创建的vue项目配置 ./build/webpack.base.conf.js

externals: {  // 排除在打包外
    'vant': 'vant', // 将vant-ui排除在打包外
},

这样,就没有less,本地查看或者部署到测试,样式都是没问题的

其他

浏览器console输出

performance.timing.loadEventEnd-performance.timing.navigationStart

查看页面加载时间,可以优化前后做一个对比。详情可以搜索 performance

本次更新到这里的优化部分其实就没有(捂脸哭),第一次去研究项目优化这个东西

大家通过可视化工具也看到了,除了moment,还有html2canvas的包也很大,后续将会针对这些进一步减小项目的打包体积,大家如果有什么可行的方案,欢迎在评论区讨论呀

对我项目没用的

在寻找优化的过程中,不可避免的会将可行的方案都执行一遍,然后通过前后对比来确认哪些有用。

这里说的可能是因为某些原因,只是对我的项目没用的,大家也可以在自己项目中尝试看一下有没有效果

1、使用compression-webpack-plugin压缩文件

使用这个插件的目的是为了将我们打包好的文件进一步压缩,在访问服务端传输数据的时候,将压缩好的文件进行传输,达到一个更快的效果

前端 - compression-webpack-plugin实践_个人文章 - SegmentFault 思否

这里是有关插件的一个介绍和一些踩坑注意

说下在我的项目中的配置

安装插件

 #NPM  npm i compression-webpack-plugin

vue.config.js中配置

module.exports = {
    configureWebpack: config =>{
    // 省略其他配置。。。。
        config.plugins.push(
            new CompressionWebpackPlugin({   // 使用插件
                // filename: info => { // 会为每个js文件生成一个gz压缩包
                //   return `${info.path}${info.base}.gz${info.query}`
                // },
                filename: '[path].gz[query]',  // 只会生成一个gz文件
                algorithm: 'gzip',   // 压缩类型
                threshold: 10240, // 只有大小大于该值的资源会被处理 10240
                test: /\.js$|\.css$|\.html$|\.ttf$|\.eot$|\.woff$/,   // 需要压缩的文件后缀,可更改为适合自己项目的
                minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
                deleteOriginalAssets: false // 是否删除原文件,一般不删除,如果压缩文件访问失败还可以访问原文件
              }),
         )
      }
  }

这里说一下这个filename

我们不删除原文件的话,打包后的体积注定会更大,因为多了压缩后的gz文件,但也应该大不了多少, 这个filename影响了压缩类型,是每个js文件都压缩成一个gz呢,还是一个文件中全部打包只有一个gz文件

我们都应该会选择打包成一个gz文件,这样增加的体积不会太大,还能完成压缩操作,岂不美哉。但是

目前我上面filename的写法: filename:'[path].gz[query]'会在终端中报 waring的错误

image.png

大概意思应该就是命名冲突导致的,不清楚这对这个操作有没有什么影响。如果不想看到 waring 的话推荐使用第一种命名方式 ,当然还有其他的写法,但意思都一样

filename: info => { // 会为每个js文件生成一个gz压缩包
    return `${info.path}${info.base}.gz${info.query}`
},

感觉没用的原因

这个配置其实我感觉应该是有用的

我的思路是:我们访问服务端需要传输的数据, 将它们打成压缩包,这样向服务器传输的速度更快,我们页面响应的也就会更快

但是老大告诉我,在实际的操作中并不会用到,所以否决了

EMMMMM。。这个问题需要我下去再研究一下,才能得到准确的结论

vue.config.js文件的整体配置(供参考)

const webpack = require('webpack');
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') //引入插件

module.exports = {
  publicPath: '/', // 路由路径
  filenameHashing: true, // 文件名hasg
  assetsDir: 'assets', //静态资源打包文件夹
  productionSourceMap: false,
  devServer: {
    open: ,
    port: , //设置端口
    proxy: {
      '/api': {
        target: 'a.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '/'
        }
      },
    },
  },
  configureWebpack: config =>{
      config.optimization = {
        splitChunks: {
          chunks: 'all',
          cacheGroups: {
            'vue': {
              name: 'vue',
              test: /[\\/]node_modules[\\/]vue[\\/]/,
              priority: 10
            },
            'vue-router': {
              name: 'vue-router',
              test: /[\\/]node_modules[\\/]vue-router[\\/]/,
              priority: -10
            },
            'core-js': {
              name: 'core-js',
              test: /[\\/]node_modules[\\/]core-js[\\/]/,
              priority: -10
            },
          }
        }
      },
      config.plugins.push(
        new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /^\.\/(zh-cn|vi)$/i), //减小moment语言包
        new UglifyJsPlugin({
          uglifyOptions: {
            // 删除注释
            output: {
              comments: false
            },
            // 删除console debugger 删除警告
            compress: {
              drop_console: true, //console
              drop_debugger: false,
              pure_funcs: ['console.log'] //移除console
            }
          }
        }),
      )
  },
  css: { // css 每次打包清除缓存配置
    extract: {
      ignoreOrder: true // 处理 “无法满足块组的所需顺序” 警告
    },
    sourceMap: false,
    loaderOptions: {
      sass: { },
    },
  },
  //只加载本页面所需要的js文件 (避免加载多余的js文件)
  chainWebpack:config=>{
    config.externals({  // 将这些包 排除在build之外
      less: "less"
    })
    // 删除无用的prefetch、preload插件,避免加载多余的资源
    config.plugins.delete('prefetch')
    config.plugins.delete('preload')
    config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
    config.module.rule('images')
      .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
    .end()
    .use('url-loader')
    .loader('file-loader')
    .options({
      name: 'assets/[name].[hash:8].[ext]'
    }).end()
    config.module.rule('svg')
      .test(/\.(svg)(\?.*)?$/)
      .use('file-loader')
      .loader('file-loader')
      .options({
        name: 'assets/[name].[hash:8].[ext]'
      })
  },
}

————————结尾

————————————————此次更新 2022/10/17

后续再有项目性能优化相关再来更新,上述中有任何错误或者补充 欢迎大家评论区留言 ~ ~ ~

另外弱弱的问一下,webpack官网是好久没更新了吗,使用插件按照官网上的出现各种意外的问题/(ㄒoㄒ)/~~