vue项目的webpack打包优化

  • Post author:
  • Post category:vue



前言

我第一次从搭建项目环境,到打包准备上线测试,vue对开发者来说学习成本不高,带来了很多的便利。没有经过如何优化,直接打包出来,打开页面时就看到无尽的加载中。。。

啥???是网络不好了???

等了半分钟时间页面终于出现了。我第一次打包,所以百度做功课,原来是vue打包出来文件体积太大,而且会影响到首屏加载。

那么,如何做到优化?


分析打包结果

借助webpack-bundle-analyzer插件,可以看到打包后每个文件的内容的大小。

第一步:安装webpack-bundle-analyzer

npm install webpack-bundle-analyzer –save-dev

第二步:在vue.config.js文件中引入

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

第三步:在webpack配置中使用该插件

module.exports = {
    configureWebpack: config => {
        config.plugins.push(
            new BundleAnalyzerPlugin()
        )
    },
}

第四步:运行

npm run build

自动打开浏览器显示一个项目打包分析图,便于直观各个文件的大小

可以看到最大vendors文件达到了2.37M,下面开始瘦身。

一、关闭生产环境的sourceMap

vue项目打包之后js文件夹中,会自动生成一些map文件,是方便于运行时报错可以准确的输出是哪一行哪一列有错,但map文件占用一部分空间。

关闭生产环境,如下配置:

module.exports = {
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: false,
}

关闭生产环境对比,减少了很大体积

前:dist大小为13.9MB

后:dist大小为3.44MB


二、路由懒加载

在router.js文件中,原来的静态引用方式

import Echarts from '../views/Echarts.vue'

const routes = [
  {
    path: "/echarts",
    name: "Echarts",
    component: Echarts,
  },
];

改为

const routes = [
  {
    path: "/echarts",
    name: "Echarts",
    component: () => import('../views/Echarts.vue')
  },
];

如果没有应用懒加载,打包会非常大,影响首屏页面加载,加载的内容过多,时间过长,会出长时间的白屏。而能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

三、ant-design组件按需加载

首屏需要加载的依赖包,其中ant-design整整占了1.03MB。

首先安装babel-plugin-import

npm install babel-plugin-import –save-dev

然后在babel.config.js文件配置

module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  plugins: [
    [ "import", {
      "libraryName": "ant-design-vue",
      "libraryDirectory": "es",
      "style": "css"
    } ]
  ]
};

然后在main.js中引入需要的部分组件

import { Button } from 'ant-design-vue';

const app = createApp(App);
app.use(Button).mount('#app');

注意:有一些组件的引入方式有所不同,具体的参照ant-design官网介绍。

import { message } from 'ant-design-vue';
const app = createApp(App);
app.config.globalProperties.$message = message;

再运行npm run build,查看项目打包的分析图

可以看到vendors减少到了1.31MB。


四、外部引入模块(CDN)

我们可以把第三方库通过cdn的方式引入项目,这样vendor.js会减少,并且大大提升首屏加载速度。

在vue.config.js配置

const isProduction = process.env.NODE_ENV === 'production';

const cdn = {
    css: [
        'https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.8/antd.min.css'
    ],
    js: [
        'https://unpkg.com/vue@3.2.20/dist/vue.runtime.global.prod.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.0/vue-router.global.prod.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.0/vuex.global.prod.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.8/antd.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.8.0/echarts.min.js'
    ]
}

module.exports = {
    chainWebpack: (config) => {
        // 生产环境配置
        if (isProduction || devNeedCdn) {
            // 生产环境注入cdn
            config.plugin('html')
                .tap(args => {
                    args[0].cdn = cdn;
                    return args;
                });
        }
    },
    configureWebpack: config => {
        // 用cdn方式引入,则构建时要忽略相关资源
        if (isProduction || devNeedCdn) config.externals = cdn.externals;

        if (isProduction) {
            config.externals = {
                'vue': 'Vue',
                'vue-router': 'VueRouter',
                'vuex': 'Vuex',
                'ant-design-vue': 'antd',
                'echarts': 'echarts'
            }
        }
    },
}



说明

这里配置externals字段,是将第三方库抽取出来。

config.externals = {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'ant-design-vue': 'antd',
    'echarts': 'echarts'
}

以打包ant-design-vue为例,”ant-design-vue” 指的是你import语句中import Antd from ‘ant-design-vue’中的’ant-design-vue’, “antd”是ant-design-vue这个库暴露出来的全局对象。这个对象你可以从cdn资源的源码中找到或者根据需要引用的文件去node_modules文件夹下去寻找。下面我从node_modules文件夹下的源码找一下ant-design-vue,vue-router和vuex暴露出的全局对象,以下是各源码的截图:

如果没有按需加载,以echarts为例,import * as echarts from “echarts”的echarts是全局对象,如下:

做完这些之后,在public/index.html文件中引入cdn链接,如下:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <!-- 使用CDN的CSS文件 -->
    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
      <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
    <% } %>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
      <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>
  </body>
</html>

再运行npm run build,查看项目打包的分析图

可以看到vendors减少到了400kb。

五、gzip

安装compression-webpack-plugin

npm install compression-webpack-plugin –save-dev

如果打包时候出现Cannot read property ‘tapPromise’ of undefined 这个报错,是compression-webpack-plugin版本问题,通过降低版本就可以解决此类问题。

在 vue.congig.js中引入并修改webpack配置,如下:

const CompressionWebpackPlugin = require('compression-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = ['js', 'css'];

module.exports = {
    configureWebpack: (config) => {
        if (isProduction) {
            config.plugins.push(
                new CompressionWebpackPlugin({
                    algorithm: 'gzip',
                    test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
                    threshold: 10240,
                    minRatio: 0.8
                }),

                new CompressionPlugin({
                    test: /\.(js|css)(\?.*)?$/i,//需要压缩的文件正则
                    threshold: 10240,//文件大小大于这个值时启用压缩
                    deleteOriginalAssets: false//压缩后保留原文件
                })
            );
        };
    },
}

服务器也要开gzip优化性能,请自行百度,我不做演示了。


项目开启gzip压缩和性能优化【亲测可用】

六、图片的压缩

请自行百度:图好快。

七、使用uglifyjs-webpack-plugin插件去除console.log打印以及注释

npm install uglifyjs-webpack-plugin –save-dev

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const isProduction = process.env.NODE_ENV === 'production';

configureWebpack: config => {
   const plugins = [];
   if (isProduction) {
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            output: {
              comments: false, // 去掉注释
            },
            warnings: false,
            compress: {
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ['console.log']//移除console
            }
          }
        })
      )
   }
},

虽然congsole.log()以及注释不会占用太多体积(也就几kb,甚至几十kb)。

八、完整配置

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin")
const productionGzipExtensions = ['js', 'css'];
const isProduction = process.env.NODE_ENV === 'production';

const cdn = {
    css: [
        'https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.8/antd.min.css'
    ],
    js: [
        'https://unpkg.com/vue@3.2.20/dist/vue.runtime.global.prod.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.0/vue-router.global.prod.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.0/vuex.global.prod.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.8/antd.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.8.0/echarts.min.js'
    ]
}

// 本地环境是否需要使用cdn
const devNeedCdn = false

module.exports = {
    // 基本路径
    publicPath: './',
    // 输出文件目录
    outputDir: 'dist',
    // eslint-loader 是否在保存的时候检查
    lintOnSave: false,
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: false,
    chainWebpack: (config) => {
        // 生产环境配置
        if (isProduction || devNeedCdn) {
            // 生产环境注入cdn
            config.plugin('html')
                .tap(args => {
                    args[0].cdn = cdn;
                    return args;
                });
        }
    },
    configureWebpack: config => {
        // 用cdn方式引入,则构建时要忽略相关资源
        if (isProduction || devNeedCdn) config.externals = cdn.externals;

        if (isProduction) {
            config.externals = {
                'vue': 'Vue',
                'vue-router': 'VueRouter',
                'vuex': 'Vuex',
                'ant-design-vue': 'antd',
                'echarts': 'echarts'
            },
            config.plugins.push(
                new CompressionWebpackPlugin({
                    algorithm: 'gzip',
                    test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
                    threshold: 10240,
                    minRatio: 0.8
                }),

                new CompressionPlugin({
                    test: /\.(js|css)(\?.*)?$/i,//需要压缩的文件正则
                    threshold: 10240,//文件大小大于这个值时启用压缩
                    deleteOriginalAssets: false//压缩后保留原文件
                })
            );
        }

        // 关于打包后包资源各部分占比的配置相关
        // config.plugins.push(
        //     new BundleAnalyzerPlugin()
        // )
    },
    devServer: {
        overlay:{
            warning:false,
            errors:false
        },
        open: process.platform === 'darwin',
        host: 'localhost',
        port: 80,
        https: false,
        hotOnly: false,
        proxy: '', 
    },
}



版权声明:本文为qq_33235582原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。