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壓縮
配置環境需耐心