一、Lighthouse
Lighthouse是 Google Chrome 推出的一款开源自动化工具,谷歌浏览器中已经集成,它可以搜集多个现代网页性能指标,分析 Web 应用的性能并生成报告,为开发人员进行性能优化的提供了参考方向。
Lighthouse会生成一份报告,会给你的页面跑出一个分数来。 分别是页面性能(performance)、Progressive(渐进式 Web 应用)、Accessibility(可访问性)、Best Practices(最佳实践)、SEO 五项指标的跑分。甚至针对我们的性能问题给出了可行的建议、以及每一项优化操作预期会帮我们节省的时间。
Lighthouse 几个比较重要的性能指标项:
首次内容绘制(First Contentful Paint) :即浏览器首次将任意内容(如文字、图像、canvas 等)绘制到屏幕上的时间点。简单来说就是用户导航到访问页面后浏览器呈现第一段 DOM 内容所需的时间。1.8 秒内达到快速级别。
可交互时间(Time to Interactive) :指的是所有的页面内容都已经成功加载,且能够快速地对用户的操作做出反应的时间点。
速度指标(Speed Index) :衡量了首屏可见内容绘制在屏幕上的速度。在首次加载页面的过程中尽量展现更多的内容,往往能给用户带来更好的体验,所以速度指标的值约小越好。
总阻塞时间(Total Blocking Time) :指First Contentful Paint 首次内容绘制 (FCP)与Time to Interactive 可交互时间 (TTI)之间的总时间
最大内容绘制(Largest Contentful Paint) :度量标准报告视口内可见的最大图像或文本块的呈现时间
累积布局偏移(Cumulative Layout Shift) :衡量的是页面整个生命周期中每次元素发生的非预期布局偏移得分的总和。每次可视元素在两次渲染帧中的起始位置不同时,就说是发生了LS(Layout Shift)、
二、speed-measure-webpack-plugin
2.1 使用
借助 speed-measure-webpack-plugin 插件,分析 webpack 的总打包耗时以及每个 plugin 和 loader 的打包耗时,从而让我们对打包时间较长的部分进行针对性优化。
安装
bash
yarn add speed-measure-webpack-plugin -D
vue.config.js配置
bash
// 导入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
module.exports = {
configureWebpack: (config) => {
config.plugins.push(new SpeedMeasurePlugin())
...
}
}
配置完成之后,执行打包指令的时候会看到详细的每个loaders和plugins的构建时间,这样就能够对构建时间进行一个量化。
2.2 优化
2.2.1 缩小文件的搜索范围(配置include 、exclude、alias)
通过配置include exclude也可以减少webpack loader的搜索转换时间。
bash
module.exports = {
chainWebpack: (config) => {
config.module
.rule('js')
.test(/\.js[x]?$/)
.include.add(path.resolve(__dirname, 'src'))
.end()
config.resolve.alias.set('@/', resolve('src/'))
config.resolve.alias.set('@test', resolve('src/views/test/'))
}
}
2.2.2 Happypack多进程构建
在使用 Webpack 对项目进行构建时,会对大量文件进行解析和处理。当文件数量变多之后,Webpack 构件速度就会变慢。由于运行在 Node.js 之上的 Webpack 是单线程模型的,所以 Webpack 需要处理的任务要一个一个进行操作。
而 Happypack 的作用就是将文件解析任务分解成多个子进程并发执行。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度。
HappyPack的原理就是每次webpack解析一个模块,HappyPack会将它及它的依赖分配给worker进程中; HappyPack会将模块进行一个划分,比如我们有多个模块,这些模块交给HappyPack,首先在webpack compiler(钩子)的run方法之后,进程就会到达HappyPack,HappyPack会做一些初始化,初始化之后会创建一个线程池,线程池会将构建任务里面的模块进行一个分配,比如会将某个模块以及它的一些依赖分配给其中的一个HappyPack线程,以此类推,那么一个HappyPack的一个线程池会包括多个线程,这时候线程池的这些线程会各自去处理其中的模块以及它的依赖,处理完成之后会有一个通信的过程,会将处理好的资源传输给HappyPack的一个主进程,完成整个的一个构建过程。
安装
bash
yarn add happypack -D
vue.config.js配置
bash
const Happypack = require('happypack')
module.exports = {
configureWebpack: (config) => {
// 多线程优化构建速度
config.plugins.push(
new Happypack({
loaders: ['babel-loader', 'vue-loader', 'url-loader'],
cache: true,
threads: 3 // 线程数取决于你电脑性能的好坏,好的电脑建议开更多线程
})
)
}
}
2.2.3 通过缓存提升二次打包速度(HardSourceWebpackPlugin)
HardSourceWebpackPlugin是 webpack 的插件,用于为模块提供中间缓存步骤。,缓存默认的存放路径是: node_modules/.cache/hard-source。配置 HardSourceWebpackPlugin,首次构建时间没有太大变化,但是第二次开始,构建时间大约可以节约 80%。
安装
bash
yarn add hard-source-webpack-plugin -D
vue.config.js配置
bash
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
chainWebpack: (config) => {
// 打包缓存加速
config.plugin('hardSource').use(HardSourceWebpackPlugin)
}
}
3. 分析并优化打包体积
3.1 分析打包体积
webpack-bundle-analyzer,这个插件在可视化网页包中输出文件的大小,并且提供了交互式可缩放的树形图。
安装
bash
yarn add webpack-bundle-analyzer -D
vue.config.js配置
bash
在vue.config.js中配置
module.exports = {
...,
configureWebpack: () => {
config.plugins.push(
new BundleAnalyzerPlugin()
)
}
}, // vue-cli3已内置,在package里面的serve代码命令里面加--report即可
3.2 优化打包体积
3.2.1 js文件最小化处理
代码的最优化, 我们会通过chainWebpak来处理,这里使用的是webpack配置中的optimization来处理的。
在vue.config.js中配置
bash
module.exports = {
chainWebpack: config => {
config.optimization.minimize(true);
}
}
3.2.2 splitChunks 分割代码
代码分割,顾名思义就是将某一些相关的文件放入到相应的文件中, 为了优化性能,比如快速打开首屏,利用缓存等,我们需要对bundle进行拆分。
在vue.config.js中配置
bash
module.exports = {
chainWebpack: config => {
config.optimization.minimize(true);
config.optimization.splitChunks({
chunks: 'all'// 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
})
}
}
splitChunks 插件配置
3.2.3 公用代码提取,使用 CDN 加载
cdn是一套内容分发服务,其本质是将一些公共资源缓存到离用户最近的服务器上,减少直接从数据源服务器获取资源的响应时间。 我们使用vue, vuex, vue-router, element-ui这些公共的cnd资源,可以极大的减轻对服务器的压力,实质是减少了对我们自己服务器的http请求。
在vue.config.js中配置
bash
module.exports = {
chainWebpack: config => {
config.optimization.minimize(true);
config.optimization.splitChunks({
chunks: 'all'
})
config.externals({
vue: 'vue',
vuex: 'vuex',
'vue-router': "'vue-router'",
'element-ui': "'element-ui'"
})
}
}
main.js配置
bash
import Vue from 'vue'
import App from './App.vue'
import ELEMENT from 'element-ui'
// 注释,这个已经通过CDN 加载了
// import "element-ui/lib/theme-chalk/index.css";
import axios from 'axios'
import * as echarts from 'echarts';
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import router from './router'
Vue.use(ELEMENT)
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.prototype.$http = axios
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
index.html文件配置
bash
<head>
...
<!-- element-ui 组件引入样式 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.5.4/theme-chalk/index.css">
</head>
<body>
<!-- 引入vue -->
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
<!-- 引入vuex -->
<script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
<!-- 引入vue-router -->
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<!-- 引入element-ui组件库 -->
<script src="https://cdn.bootcss.com/element-ui/2.5.4/index.js"></script>
<!-- 引入mint-ui组件库 -->
<script src="https://cdn.bootcss.com/mint-ui/2.2.13/index.js"></script>
<div id="app"></div>
</body>