开发时很经常会遇到一种情况:刚接手了一个现有的已发布至生产项目,就会被甩下三个问题
这里的慢一般指的是,页面的白屏(FP)到首屏完整(FCP)加载加起来的时长。
先问清楚问题的复现过程,偶现还是必现,偶现的话有没有什么具体的时间段或者网络是否正常。有无特定的复现机型、系统、浏览器。了解更详细的信息有助于我们排查问题
若没有解决的话就得从前端开始入手,导致慢的原因有很多种可能,用chrome打开页面,按f12,转到network,勾上Disable cache
network相关功能说明及使用技巧可以参照这篇文章。Chrome教程(一)NetWork面板分析网络请求
首先我们需要知道的是,浏览器加载的触发流程大致是根据以下步骤进行的:
接着我们就可以看是什么内容的time占用时长最多,根据不同文件、接口的加载时间进行相对应的优化。
//max-age=31536000 等同于页面在31536000时间戳之内。
cache-control: max-age=31536000
//expires
expires: Wed, 11 Sep 2019 16:12:18 GMT
//href只需要填你需要请求的域名//+你请求的域名,如下例
<link rel="dns-prefetch" href="//code.jquery.com" />
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
>script>
首先我们先使用chrome的performance对页面的性能进行解析,具体使用教程可以参照这篇文章
得到上面的图片之后,我们就可以针对占用时间较长的模块进行优化。底部的饼图有总的耗时时间,一般都是scripting和rendering占用的时间较多。
Scripting:Javascript执行
Rendering:样式计算和布局,即重排
Painting:重绘 对应的详细事件
首先我们需要知道,一般的显示器是刷新率是 60 HZ,一个流畅的网页动画的要求就是 1 秒 60 帧,即一秒重新渲染页面 60 次,一次渲染出来的页面叫一帧,动画的本质就是帧的切换。在一次事件循环里,一个宏任务被执行后,js 修改了样式,浏览器也不一定会重新渲染,浏览器可能等到下一次事件循环再一起渲染,而中间没有渲染的那一次,就不会再被渲染出来了,这就叫 “丢帧”。
页面重新渲染间隔大于 16.67 毫秒,动画就会产生卡顿;
减少会引起回流和重绘的操作
尽量使用CSS3动画,CSS3动画在大部分浏览器都开启了硬件加速,比如:在css属性上加上transform:translateZ(0)开启硬件加速
减少js访问dom的次数,部分如window对象的resize,scroll事件,还有document.mousemove等事件,频繁执行dom操作,资源加载等重行为,导致UI停顿甚至浏览器奔溃。建议对函数里面需要执行的函数进行节流操作
如果是使用了setTimeout 或 setInterval 函数来执行动画导致页面卡顿了的话,可能是丢帧的原因导致的,可以看看window.requestAnimationFrame() 方法,将需要用js改变样式代码统一放到下一次重新渲染时执行。
优化可能会引起阻塞的js代码,看看有没有什么死循环或者时间复杂度较高的函数,优化掉
webpack-bundle-analyzer是webpack的一款可视化工具插件
它可以直观分析打包出的文件包含哪些,大小占比如何,模块包含关系,依赖项,文件是否重复,压缩后大小如何,针对这些,我们可以进行文件分割等操作。
vue-cli创建的项目里,在package.json里的script里加上–report,然后运行,旧的版本是会直接让你打开一个地址,新的版本会在打包后的目录里生成一个report.html,打开即可。
"scripts": {
...
"build": "vue-cli-service build --report",
...
}
其他使用webpack创建的目录可以用
npm install --save-dev webpack-bundle-analyzer
然后在webpack的plugins配置的地方引入即可
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
关于webpack对包大小的优化,在webpack4的版本几乎都合并到optimization选项里了,可以逐个翻阅
逐个检查webpack的插件,去除可能会导致包体积增加的没必要的插件,例如部分只需用于本地调试用的插件HotModuleReplacementPlugin等
使用webpack的压缩代码的插件uglifyjs-webpack-plugin,或者使用在线压缩代码的网页对代码进行压缩。
//webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
optimization: {
minimizer: [new UglifyJsPlugin()],
},
webpack4以上的话不用以上的写法,直接使用optimization.minimize,详情可以翻阅文档。
TreeShaking可以在打包的时候帮我们移除javascript上下文中的未引用代码。TreeShaking在webpack2已经支持了。但是旧的项目如果用的是webpack3或之前的版本,考虑升级至webpack4/5,webpack4以上的版本对TreeShaking的检测能力进行进一步的优化。
webpack4之前的SplitChunksPlugin以及webpack4之后的optimization.splitChunks,可以抽取页面的公用模块,避免不同页面之间的重复依赖。下面是optimization.splitChunks的示例代码。
module.exports = {
optimization: {
splitChunks: {
chunks: "async",// all async initial
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: "~",
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
}
引用例如lodash、moment等常用工具库的时候,优先考虑按需引入,或将需要使用到的部分代码,直接拷贝至项目常用的工具函数文件中,自己维护一套常用的工具库,减少包的体积。
假如要引用现成的内存较大的库,比如echarts、jquery等,优先考虑CDN引入从而减少包的体积。建议是公司自己维护一套CDN服务器,以免外网服务的CDN突然下线导致服务崩溃。
给CDN地址加上dns-prefetch,可以对DNS进行预解析。尽量把预解析的代码写到页面的最开始的部分,能尽快加载
//href只需要填你需要请求的域名//+你请求的域名,如下例
<link rel="dns-prefetch" href="//code.jquery.com" />
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
>script>
使用了CDN之后,webpack的项目还可以搭配external,设置import引入的包的名字,之后你就可以像使用npm安装的包一样直接import相对应的名字就可以使用。而且webpack打包时会不从node_modules把external中的包打包进去,从而减少打包的体积
//index.html,引入CDN服务器的Jquery
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
></script>
//webpack.config.js,然后再webpack中设置externals
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
//引入的时候就和npm安装的包一样,用import引入即可
import $ from 'jquery';
$('.my-element').animate(/* ... */);
import { debounce } from 'lodash'
import { throttle } from 'lodash'
// 改成如下写法,找到相对应的库的地址即可
import debounce from 'lodash/debounce'
import throttle from 'lodash/throttle'
以上仅为本人整理的常见的优化思路,欢迎大家补充,实在不行就花钱买更好的服务器配置!重构整个项目!包治百病!看完的话麻烦点个赞啦谢谢