前端性能优化
本文最后更新于:2022年4月4日 晚上
【打包时间】
- 缩小loader的作用范围
include
,exclude
- 缩小模块搜索范围
module.exports = { //... resolve: { modules: [path.resolve(__dirname, 'src'), 'node_modules'], // 搜索位置 extensions:[".js",".jsx"] // 搜索的后缀名,虽然写的时候可以省略,但会增加搜索的性能损耗 } };
【打包大小】
资源压缩
css/js/图片压缩代码分割
分离第三方库去设置长缓存或者CDNmodule.exports = { chainWebpack: config => { config.optimizationsplitChunks({ chunks: 'all', cacheGroups: { vendors: { name: 'chunk-vendors', test: /[\\/]node_module[\\/]/, priority: 10, chunks: 'initial' }, echarts: { name: 'chunk-echarts', priority: 20, test: /[\\/]node_module[\\/]_?echarts(.*)/ } } }) } }
babel转译优化
babel会在在每个需要编译的地方都加上helper函数,造成冗余// .babelrc "plugins": [ "@babel/plugin-transform-runtime" ] // 自动移除语法转换后内联的辅助函数(inline Babel helpers),使用@babel/runtime/helpers里的辅助函数来替代;
按需打包组件(如element-ui)和
tree shaking
gzip
压缩【加载阶段】
白屏loading提示或者骨架屏
路由懒加载/异步组件
// router export default new Router({ routes: [ { path: '/home', component: () => import('@/components/home'), }, { path: '/about', component: () => import('@/components/home'), }, ], }) // async component import {defineAsyncComponent} from 'vue'
图片懒加载
// 主要逻辑
const imgList = [...document.querySelectAll('img')]
function lazyLoad(imgList){
let count = 0
const len = imgList.length
return function(){
let doneList = []
imgList.forEach((img,index)=>{
const react = img.getBoundingClientRect()
// Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置
if(react.top<window.innerHeight){
img.src = img.dataset.src // 在这里赋值图片src
doneList.push(index)
count++
if(count === len){
document.removeEventListener('scroll',lazyLoad)
}
}
})
imgList = imgList.filter((img,index)=>!doneList.includes(i))
}
}
document.addEventListener('scroll',_.debounce(lazyLoad))
//
虚拟列表
动态渲染长列表
图片占位符
优化文档结构
css资源请求放在文档最前
脚本放在最后且非核心代码异步加载
js脚本组织DOM解析,而css资源又回阻塞js执行
异步加载的方式
- 动态创建script标签
- script
defer
- script
async
- 普通的
script
,html解析暂停,立即下载和执行这个脚本 <script async>
,html解析和该脚本的加载同时进行,下载完后执行脚本时暂停html解析<script defer>
,html解析和该脚本的加载同时进行,脚本在页面解析完成后才执行—>DOMContentLoaded
之前执行
使用CDN
安装
html-webpack-plugin
修改
public/index.html
<!DOCTYPE html> <html lang="en"> <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,user-scalable=no" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <!-- 使用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的CSS文件 --> </head> <body> <noscript> <!-- --> </noscript> <div id="app"></div> <!-- built files will be auto injected --> <!-- 使用CDN的JS文件 --> <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> <% } %> <!-- 使用CDN的JS文件 --> </body> </html>
配置
vue.config.js
// 判断生产环境 const isProduction = process.env.NODE_ENV === "production"; const cdn = { // 随便抓几个 js: [ "https://cdn.bootcdn.net/ajax/libs/vue/2.6.0/vue.runtime.esm.js", "https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.3/vue-router.esm.js" ], }; module.exports = { configureWebpack: (config) => { if (isProduction) { config.externals = { // 排除的依赖,不从bundle中引入的依赖 "vue": "Vue", "vue-router": "VueRouter" }; } }, chainWebpack: (config) => { if (isProduction) { config.plugin("html").tap((args) => { // 传递给 html-webpack-plugin's 构造函数的新参数 args[0].cdn = cdn; return args; }); } }, };
预解析DNS
DNS Prefetch
是一种DNS 预解析技术,当浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样在单击当前网页中的连接时就无需进行DNS的解析,减少用户等待时间,提高用户体验。
HTTP
中浏览器会对<a>标签
自动开启dns预解析对于https页面,大部分浏览器是关闭a标签的dns预解析的
HTTPS
用meta信息来告知浏览器, 当前页面要做DNS预解析:<meta http-equiv="x-dns-prefetch-control" content="on" />
在页面
<head>
中使用<link>标签
来强制对DNS预解析:
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
预渲染和服务端渲染
SSR:server side render
prerender-spa-plugin
不适用于个性化的,内容变化大,多路由的页面,可以用来优化单页应用。
【运行阶段】
JS
- 合理使用浏览器缓存策略
- 不频繁操作DOM,使用虚拟DOM或
fragment
批量操作 - 读取元素的位置大小属性(如
scrollTop
,offsetHeight
…)浏览器会立即重排,更不能将这些属性放入循环中 - 使用事件委托
- 使用
Web Worker
- 使用
requestAnimationFrame
代替有高性能需求的计时器CSS
- 避免
css
选择器嵌套过深 - 避免大量使用
id
选择器,每个id
都会变为一个单独的全局变量 - 避免使用
table
,table
会频繁触发重排 - 更换
class
代替直接修改style
- 合理使用GPU加速
-
css3
动画transform
代替修改几何信息left,top...
-
opacity
代替visibility
- 复杂动画元素脱离文档流
position:absolute/fixed
- 加上
will-change
属性的元素会交给合成层渲染(GPU
-
【资源优化】
图片资源
高效图片格式
- WebP
- 矢量图SVG
- Base64位图 base64
响应式图片
<picture> <source srcset="banner_w1000.jpg" media="(min-width: 801px)"> <source srcset="banner_w800.jpg" media="(max-width: 800px)"> <img src="banner_w800.jpg" alt=""> </picture>
图片压缩
- tinypng
- webpack插件
小图合并
- 合并雪碧图
- 使用
icon-font
字体图标svg-sprite-loader
性能检测报告
Performance
Lighthouse
关键时间点
FCP
页面上呈现第一DOM元素的时间,之前都是白屏TTI
页面可以交互的时间LCP
视口内最大面积内容渲染时间
const timing = window.performance && window.performance.timing
const navigation = window.performance && window.performance.navigation
// DNS
const DNS = timing.domainLookupEnd - timing.domainLookupStart
// Network
const NetWork = timing.responseEnd - timing.navigationStart
// 渲染
const Processing = (timing.domComplete || timing.domLoading) - timing.domLoading
// 可交互
const Active = timing.domInteractive = timing.navigationStart
前端性能优化
http://yoursite.com/2022/02/24/前端性能优化/