天天看點

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壓縮
           

配置環境需耐心

繼續閱讀