1、分析工具
Coverage:查看代码的使用状况
Coverage 是chrome开发者工具的一个新功能,从字面意思上可以知道它是可以用来检测代码在网站运行时有哪些js和css是已经在运行,而哪些js和css是还没有用到的,如图,这是csdn所显示的已运行和尚未运行的代码情况。
上图所示,最右边显示的是我们加载的css和js文件数量,红色区域表示已运行的代码,而青色表示已加载但未运行的代码。可用来发现页面中尚未用到的js 和 css代码,你可以为用户只提供必要的代码,这样就可以提升页面的性能。这对于找出可以进行拆分的脚本以及延迟加载非关键脚本来说非常有用。
上面录制的数据中,最大的文件是 vendor.js,其中 55% 的代码都没有执行过,约 80 KB,这已经相当于一张典型图片的文件大小了。
如果某个文件覆盖率低(即未使用代码比例很高),通常意味着用户加载了太多不必要的代码(要么真的是无用代码,要么是当前时点还没执行到的代码),有性能常识的同学不难推断出,这会导致页面的完全加载时间、或单页应用的启动时间变慢,在慢速网络下的性能损耗会尤其明显;此外,更多代码的解析、编译也就意味着更多的硬件资源消耗,在低端设备上也会存在明显的性能问题。
在笔者看来,Coverage 数据至少能从下面 2 个方面指导我们进行 WEB 应用的优化:
移除死代码
以 Coverage 数据为参考,我们能了解页面重无用代码的比例到底有多大。现实世界中,很多工程师可能是在遗留代码库上工作,并且遗留代码库存在的时间还很长,那么很可能这个代码库中存在大量的无用代码,但是谁也不敢删除他们,因为 JS 这门语言的动态性,你不能粗暴的把哪些看起来“没有被使用”的代码直接删掉,除非你很清楚所有的代码执行路径,很显然这对于大型应用或者遗留代码库来说是不现实的。
怎么移除死代码呢?我们可以依赖打包工具,比如 UglifyJS 在压缩代码时支持直接删除死代码的配置项。而 Webpack 2 中引入了 Tree Shaking 的特性,能够自动把项目中没有用到的代码从打包中去掉,但是这种优化仅限于被 export 的代码。总而言之,死代码要尽可能想办法去掉,Coverage 工具能提供一个判断基准。
懒加载代码
如果能删的死代码都删了,但是 Coverage 数据还是居高不下,那么你应该换个角度思考。就像前文所说,JS 是动态语言,可能部分代码在页面加载时并没有用到,但是用户后来的操作会触发这些代码的执行,为什么不让这些代码在需要的时候再加载呢?聪明的你可能已经想到了,这就是懒加载的技术。
使用 Webpack 打包且没有对配置做特别调优的话,它默认会把所有依赖打包成一个巨大的文件,很容易出现首次加载覆盖率很低的情况,在 Webpack 中实现懒加载可以参考 Code Splitting 和 bundle-loader,具体的配置细节这里不展开讲。使用懒加载之后可以极大的减少页面初次下载的代码,从而提高性能。需要注意的是,懒加载优化需要在模块数量和模块大小之间把握一个平衡,否则过多的模块懒加载反而对性能不利,因为每个 HTTP 请求也是有额外开销的。
webpack-bundle-analyzer:查看资源树
我使用的是 vue-cli 3,需要先安装插件webpack-bundle-analyzer (使用npm或者yarn)
# 这里不要安装最新版,新的版本有坑,后面会讲到,在这里我安装的是6.1.1
yarn add webpack-bundle-analyzer@6.1.1 --dev
在vue.config.js中添加分析工具的配置:
module.exports = {
chainWebpack: (config) => {
if (process.env.NODE_ENV === "production") {
config
.plugin("webpack-bundle-analyzer")
.use(require("webpack-bundle-analyzer").BundleAnalyzerPlugin)
.end();
config.plugins.delete("prefetch");
}
}
}
再运行
npm run build --report
会在浏览器打开一个项目打包的情况图,便于直观地比较各个bundle文件的大小。
瘦身开始
1、productionSourceMap:false
修改vue.config.js中的配置
module.exports = {
outputDir: `${srcFile}`, // 在npm run build时 生成文件的目录 type:string, default:'dist'
productionSourceMap: false, // 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度
}
把productionSourceMap改为false。不然在最终打包的文件中会出现一些map文件,map文件的作用在于:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。
有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。
如果不关掉,生产环境是可以通过map文件看到源码的。
2、路由懒加载
在router.js文件中,原来的静态引用方式
import Login from '../views/Login'
routes:[ { path: '/login', name: 'Login', component: Login } ]
改为
routes:[
{ path: '/login',name: 'Login',component: () => import("../views/Login.vue") }
]
以函数的形式动态引入,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会下载路由组件。
3、关闭Prefetch
因为vuecli 3默认开启prefetch(预先加载模块),提前获取用户未来可能会访问的内容
在首屏会把这十几个路由文件,都一口气下载了
所以我们要关闭这个功能,在vue.config.js中设置
参考官网的做法:

4、ant-design-vue组件按需加载
首屏需要加载的依赖包,其中ant-design-vue整整占了568k
原本的引进方式引进了整个包:
import Antd from "ant-design-vue"
Vue.use(ElementUI)
按需引入
借助 babel-plugin-import ,我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-import:
yarn add babel-plugin-import --dev
使用 vue-cli 3 的小伙伴
修改babel.config.js
文件,配置 babel-plugin-import
module.exports = {
presets: ["@vue/app"],
+ plugins: [
+ [
+ "import",
+ { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
+ ]
+ ]
};
接下来,如果你只希望引入部分组件,比如 Button 和 Layout,那么需要在 main.js 中写入以下内容:
import Vue from 'vue';
import {
Layout,
Col,
Row,
Button
} from "ant-design-vue";
import App from './App.vue';
Vue.use(Button);
Vue.use(Layout);
Vue.use(Col);
Vue.use(Row);
new Vue({
el: '#app',
render: h => h(App)
});
# 注意:如果你用到了Layout,则必须要引用Col、Row
5.使用 CDN 外部加载资源-vue, vuex, vue-router,axios
对于vue, vuex, vue-router,axios等我们可以利用wenpack的externals参数来配置,这里我们设定只需要在生产环境中才需要使用:
# 配置vue.config.js
const isProduction = process.env.NODE_ENV === 'production';
const cdn = {
css: [],
js: [
'https://cdn.bootcss.com/vue/2.5.17/vue.runtime.min.js',
'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
]
}
module.exports = {
chainWebpack: config => {
// 生产环境配置
if (isProduction) {
// 生产环境注入cdn
config.plugin('html')
.tap(args => {
args[0].cdn = cdn;
return args;
});
}
},
configureWebpack: config => {
if (isProduction) {
// 用cdn方式引入
config.externals = {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'axios': 'axios'
}
}
},
}
# 修改public文件夹下面的index.html
<!DOCTYPE html>
<html lang="zh">
<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">
<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but eye-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
6、使用 CDN 外部加载资源-比如:echarts
这次优化主要是针对echarts,在其文档里也有提到按需加载,但是这次我们不用按需加载了,我想把echarts彻底干掉。
首先在index.html中引入echarts的外部CDN(如果需要地图组件,也需要一并引入)
# index.html
<script src="https://cdn.bootcss.com/echarts/4.1.0/echarts.min.js"></script>
复制代码然后在webpack.base.config.js中,做如下改动
// module.exports中增加externals对象
module.exports = {
externals: {
"echarts": "echarts" //默认是配置引用的库(这里是echarts)暴露出的全局变量
},
}
7、gzip的安装及配置
拆完包之后,我们再用gzip做一下压缩
# compression-webpack-plugin
yarn add compression-webpack-plugin@6.1.1 --dev
#注意:compression-webpack-plugin不要安装最新版本,有坑!!!!
# 在vue.congig.js中引入并修改webpack配置
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === "production") {
config.mode = "production";
return {
plugins: [
new CompressionPlugin({
test: /\.js$|\.html$|\.css/, //匹配文件名
threshold: 10240, //对超过10k的数据进行压缩
deleteOriginalAssets: false //是否删除原文件
})
]
};
}
}
}
在服务器我们也要做相应的配置
如果发送请求的浏览器支持gzip,就发送给它gzip格式的文件
8、在nginx中开启gzip实例
在nginx.conf中加入
gzip on;
gzip_buffers 32 4K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types application/javascript text/css text/xml;
#配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
保存并重启nginx,刷新页面(为了避免缓存,请强制刷新)就能看到效果了。以谷歌浏览器为例,通过F12看请求的响应头部,如下图:
Content-Encoding:gzip 说明开启了gzip压缩
Transfet-Encoding:chunked 说明压缩后分块传输