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加速
// 是否为生产环境
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] %>" 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 compression-webpack-plugin@6.1.1 --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压缩
配置环境需耐心