天天看点

vue项目打包优化(简介)

1. 我们为什么要进行打包优化呢?

1、打包优化的目的

1、优化项目启动速度,和性能

2、必要的清理数据

3、减少打包后的体积

第一点是核心,第二点呢其实主要是清理console

2、性能优化的主要方向

1、去重.map文件

2、开启CDN加速

3、代码压缩

4、图片压缩 (下方跳过)

5、公共代码抽离

6、首屏骨架屏优化

7、开启Gzip压缩

// 生产环境是否生成 sourceMap 文件
  productionSourceMap: false,     //不输出map文件
           

2. 打包步骤详解代码演示:

vue.config.js

中添加

打包前的配置:

module.exports = {
  publicPath: "./", // 静态资源路径(默认/,打包后会白屏)
  outputDir: "dist", // 打包后文件的目录 (默认为dist)
};
           

1. 去除.map文件

// 生产环境是否生成 sourceMap 文件
  productionSourceMap: false,     //不输出map文件
           

2. 开启CDN加速

快速查找对应CDN

// 是否为生产环境
const isProduction = process.env.NODE_ENV !== 'development';

// ==================== 注入cdn start ====================
// 本地环境是否需要使用cdn
const devNeedCdn = false;
// cdn链接
const cdn = {
  // cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
  externals: {
    vue: "Vue",
    vuex: "Vuex",
    "vue-router": "VueRouter",
    axios: "axios",
    nprogress: "NProgress",
    "element-ui": "Element"
  },
  // cdn的css链接
  css: [
    "https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/theme-chalk/index.min.css",
    "https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css"
  ],
  // cdn的js链接
  js: [
    "https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js",
    "https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js",
    "https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js",
    "https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js",
    "https://cdn.bootcdn.net/ajax/libs/core-js/3.6.5/minified.min.js",
    "https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/locale/zh-CN.min.js",
    "https://cdn.bootcdn.net/ajax/libs/echarts/5.0.0-rc.1/echarts.common.min.js",
    "https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"
  ]
};
// ==================== 注入cdn end ====================

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

public / index.html

 中的 

head

 标签中

<!-- 使用CDN的CSS文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" target="_blank" rel="external nofollow"  rel="stylesheet" />
    <% } %>
      <!-- 使用CDN的CSS文件 -->


      <!-- 使用CDN的JS文件 -->
      <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
        <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
        <% } %>
          <!-- 使用CDN的JS文件 -->
           

3. 代码压缩

安装插件 

npm i -D uglifyjs-webpack-plugin

// 代码压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

    // ==================== 代码压缩 start ====================
    //在configureWebpack中加入
    // 代码压缩
    config.plugins.push(
      new UglifyJsPlugin({
        uglifyOptions: {
          output: {
            comments: false // 去掉注释
          },
          //生产环境自动删除console
          compress: {
            drop_debugger: true,
            drop_console: true, //注释console
            pure_funcs: ["console.log"] // 移除console
          }
        },
        sourceMap: false,
        parallel: true
      })
    );
    // ==================== 代码压缩 end ====================
           

4. 图片压缩(会报错,等待更新)

安装插件 

npm install image-webpack-loader --save-dev

// ==================== 压缩图片 start ====================
    // 在chainWebpack中新增以下代码
    // config.plugins.delete('prefetch')
    // config.module
    //   .rule('images')
    //   .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
    //   .use('image-webpack-loader')
    //   .loader('image-webpack-loader')
    //   .options({ bypassOnDebug: true })
    // ==================== 压缩图片 end ====================
           

图片生成在线地址

5. 公共代码抽离

// ==================== 公共代码抽离 start ====================
    // 公共代码抽离
    config.optimization = {
      splitChunks: {
        // 分割代码块
        cacheGroups: {
          vendor: {
            //第三方库抽离
            chunks: "all",
            test: /node_modules/,
            name: "vendor",
            minChunks: 1, //在分割之前,这个代码块最小应该被引用的次数
            maxInitialRequests: 5,
            minSize: 0, //大于0个字节
            priority: 100 //权重
          },
          common: {
            //公用模块抽离
            chunks: "all",
            test: /[\\/]src[\\/]js[\\/]/,
            name: "common",
            minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数
            maxInitialRequests: 5,
            minSize: 0, //大于0个字节
            priority: 60
          },
          styles: {
            //样式抽离
            name: "styles",
            test: /\.(sa|sc|c)ss$/,
            chunks: "all",
            enforce: true
          },
          runtimeChunk: {
            name: "manifest"
          }
        }
      }
    };
    // ==================== 公共代码抽离 end ====================
           

6. 首屏添加骨架屏优化

安装插件 npm install vue-skeleton-webpack-plugin

在src下新建Skeleton文件夹,其中新建index.js以及index.vue,在其中写入以下内容,其中,骨架屏的index.vue页面样式请自行编辑

index.js

import Vue from 'vue'
import Skeleton from './index.vue'
export default new Vue({
  components: {
    Skeleton
  },
  template: '<Skeleton />'
})
           

index.vue

<template>
  <div class="skeleton-wrapper">
    <header class="skeleton-header"></header>
    <section class="skeleton-block">
      <img
        src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==">
      <img
        src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==">
    </section>
  </div>
</template>

<script>
export default {
  name: 'Skeleton',
}
</script>

<style scoped>
.skeleton-header {
  height: 40px;
  background: #1976d2;
  padding: 0;
  margin: 0;
  width: 100%;
}
.skeleton-block {
  display: flex;
  flex-direction: column;
  padding-top: 8px;
}
</style>
           

vue.config.js

// path引入
const path = require('path')

//骨架屏渲染
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')

    // ==================== 骨架屏 start ====================
    // configureWebpack模块中写入内容
    // 骨架屏渲染
    config.plugins.push(
      new SkeletonWebpackPlugin({
        webpackConfig: {
          entry: {
            app: path.join(__dirname, "./src/Skeleton/index.js")
          }
        },
        minimize: true,
        quiet: true,
        // 如果不设置那么所有的路由都会共享这个骨架屏组件
        router: {
          mode: "hash",
          // 给对应的路由设置对应的骨架屏组件,skeletonId的值根据组件设置的id
          routes: [{ path: "/list", skeletonId: "skeleton" }]
        }
      })
    );
    // ==================== 骨架屏 end ====================
           

7. 开启Gzip压缩

安装插件 

npm install [email protected] --save-dev

// gzip压缩
const CompressionWebpackPlugin = require("compression-webpack-plugin");

    // ==================== gzip压缩 start ====================
    // 生产环境相关配置
    if (isProduction) {
      //gzip压缩
      const productionGzipExtensions = ["html", "js", "css"];
      config.plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path].gz[query]",
          algorithm: "gzip",
          test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
          threshold: 10240, // 只有大小大于该值的资源会被处理 10240
          minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
          deleteOriginalAssets: false // 删除原文件
        })
      );
    }
    // ==================== gzip压缩 end ====================
           

安装

nginx

的文件夹中:

conf

 / 

nginx.conf

# 开启gzip
    gzip on;

    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;

    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    gzip_comp_level 2;

    # 进行压缩的文件类型。javascript有多种形式,后面的图片压缩不需要的可以自行删除
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;

    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;

    # 设置压缩所需要的缓冲区大小     
    gzip_buffers 4 16k;
           

完整代码压压惊

// path引入
const path = require("path");

// 是否为生产环境
const isProduction = process.env.NODE_ENV !== "development";

// 代码压缩
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

// gzip压缩
const CompressionWebpackPlugin = require("compression-webpack-plugin");

//骨架屏渲染
const SkeletonWebpackPlugin = require("vue-skeleton-webpack-plugin");

// ==================== 注入cdn start ====================
// 本地环境是否需要使用cdn
const devNeedCdn = false;
// cdn链接
const cdn = {
  // cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
  externals: {
    vue: "Vue",
    vuex: "Vuex",
    "vue-router": "VueRouter",
    axios: "axios",
    nprogress: "NProgress",
    "element-ui": "Element"
  },
  // cdn的css链接
  css: [
    "https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/theme-chalk/index.min.css",
    "https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css"
  ],
  // cdn的js链接
  js: [
    "https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js",
    "https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js",
    "https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js",
    "https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js",
    "https://cdn.bootcdn.net/ajax/libs/core-js/3.6.5/minified.min.js",
    "https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/locale/zh-CN.min.js",
    "https://cdn.bootcdn.net/ajax/libs/echarts/5.0.0-rc.1/echarts.common.min.js",
    "https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"
  ]
};
// ==================== 注入cdn end ====================

module.exports = {
  publicPath: "./", // 静态资源路径(默认/,打包后会白屏)
  outputDir: "dist", // 打包后文件的目录 (默认为dist)

  // 生产环境是否生成 sourceMap 文件
  productionSourceMap: false, //不输出map文件

  // ==================== 注入cdn start ====================
  chainWebpack: config => {
    config.plugin("html").tap(args => {
      // 生产环境或本地需要cdn时,才注入cdn
      if (isProduction || devNeedCdn) args[0].cdn = cdn;
      return args;
    });
  },
  // ==================== 注入cdn end ====================

  configureWebpack: config => {
    // 用cdn方式引入,则构建时要忽略相关资源
    if (isProduction || devNeedCdn) config.externals = cdn.externals;

    // ==================== gzip压缩 start ====================
    // 生产环境相关配置
    if (isProduction) {
      //gzip压缩
      const productionGzipExtensions = ["html", "js", "css"];
      config.plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path].gz[query]",
          algorithm: "gzip",
          test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
          threshold: 10240, // 只有大小大于该值的资源会被处理 10240
          minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
          deleteOriginalAssets: false // 删除原文件
        })
      );
    }
    // ==================== gzip压缩 end ====================

    // ==================== 代码压缩 start ====================
    //在configureWebpack中加入
    // 代码压缩
    config.plugins.push(
      new UglifyJsPlugin({
        uglifyOptions: {
          output: {
            comments: false // 去掉注释
          },
          //生产环境自动删除console
          compress: {
            drop_debugger: true,
            drop_console: true, //注释console
            pure_funcs: ["console.log"] // 移除console
          }
        },
        sourceMap: false,
        parallel: true
      })
    );
    // ==================== 代码压缩 end ====================

    // ==================== 压缩图片 start ====================
    // 在chainWebpack中新增以下代码
    // config.plugins.delete('prefetch')
    // config.module
    //   .rule('images')
    //   .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
    //   .use('image-webpack-loader')
    //   .loader('image-webpack-loader')
    //   .options({ bypassOnDebug: true })
    // ==================== 压缩图片 end ====================

    // ==================== 公共代码抽离 start ====================
    // 公共代码抽离
    config.optimization = {
      splitChunks: {
        // 分割代码块
        cacheGroups: {
          vendor: {
            //第三方库抽离
            chunks: "all",
            test: /node_modules/,
            name: "vendor",
            minChunks: 1, //在分割之前,这个代码块最小应该被引用的次数
            maxInitialRequests: 5,
            minSize: 0, //大于0个字节
            priority: 100 //权重
          },
          common: {
            //公用模块抽离
            chunks: "all",
            test: /[\\/]src[\\/]js[\\/]/,
            name: "common",
            minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数
            maxInitialRequests: 5,
            minSize: 0, //大于0个字节
            priority: 60
          },
          styles: {
            //样式抽离
            name: "styles",
            test: /\.(sa|sc|c)ss$/,
            chunks: "all",
            enforce: true
          },
          runtimeChunk: {
            name: "manifest"
          }
        }
      }
    };
    // ==================== 公共代码抽离 end ====================

    // ==================== 骨架屏 start ====================
    // configureWebpack模块中写入内容
    // 骨架屏渲染
    config.plugins.push(
      new SkeletonWebpackPlugin({
        webpackConfig: {
          entry: {
            app: path.join(__dirname, "./src/Skeleton/index.js")
          }
        },
        minimize: true,
        quiet: true,
        // 如果不设置那么所有的路由都会共享这个骨架屏组件
        router: {
          mode: "hash",
          // 给对应的路由设置对应的骨架屏组件,skeletonId的值根据组件设置的id
          routes: [{ path: "/list", skeletonId: "skeleton" }]
        }
      })
    );
    // ==================== 骨架屏 end ====================
  }
};

// 12.6MB   原有
// 2.68MB   去除.map
// 2.56MB   cdn加速
// 2.51MB   代码抽离
// 2.50MB   公共代码抽离
// 2.28MB   gzip压缩
           

配置环境需耐心

继续阅读