目录
webpackChunkName: "dialogInfo"
Tree shaking:消除无用的 JS 代码,减少代码体积
requestAnimationFrame 制作动画:刷新频率与显示器的频率保持一致
setTimeout/setInterval 属于 JS引擎
requestAnimationFrame 属于 GUI引擎
JS引擎与GUI引擎是互斥的: GUI 引擎在渲染时会阻塞 JS 引擎的计算
requestAnimationFrame 刷新频率是固定且准确的
setTimeout/setInterval 定时器仍会在后台执行动画任务,
requestAnimationFrame 当页面处于未激活的状态下,屏幕刷新任务会被系统暂停
B版API:IntersectionObserver :回调
懒/动态加载:按需/运动加载
路由懒加载
SPA :一个路由对应一个页面
把所有页面打包成一个文件
如果不做处理,项目打包后,会把所有页面打包成一个文件,当用户打开首页时,会一次性加载所有的资源,造成首页加载很慢,降低用户体验
懒加载前提:ES6动态地加载模块——import()
调用 import() 之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中
——摘自《webpack——模块方法》的import()小节
webpackChunkName:分离到单独的 chunk
要实现懒加载,就得先将进行懒加载的子模块分离出来,打包成一个单独的文件
webpackChunkName 作用是 webpack 在打包的时候,对异步引入的库代码(lodash)进行代码分割时,设置代码块的名字。webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中
// 通过webpackChunkName设置分割后代码块的名字
const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue");
const MetricGroup = () => import(/* webpackChunkName: "metricGroup" */ "@/views/metricGroup/index.vue");
…………
const routes = [
{
path: "/",
name: "home",
component: Home
},
{
path: "/metricGroup",
name: "metricGroup",
component: MetricGroup
},
…………
]
组件懒加载
webpackChunkName: "dialogInfo"
home 页面 和 about 页面,都引入了 dialogInfo 弹框组件,该弹框不是一进入页面就加载,而是需要用户手动触发后才展示出来
<script>
const dialogInfo = () => import(/* webpackChunkName: "dialogInfo" */ '@/components/dialogInfo');
export default {
name: 'homeView',
components: {
dialogInfo
}
}
</script>
适用场景
体积大
该页面的 JS 文件体积大,导致页面打开慢,可以通过组件懒加载进行资源拆分,利用浏览器并行下载资源,提升下载速度(比如首页)
非首屏
该组件不是一进入页面就展示,需要一定条件下才触发(比如弹框组件)
复用性高
该组件复用性高,很多页面都有引入,利用组件懒加载抽离出该组件,一方面可以很好利用缓存,同时也可以减少页面的 JS 文件大小(比如表格组件、图形组件等)
Tree shaking:消除无用的 JS 代码,减少代码体积
项目中只使用了 targetType 方法,但未使用 deepClone 方法,项目打包后,deepClone 方法不会被打包到项目里
// util.js
export function targetType(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
}
export function deepClone(target) {
return JSON.parse(JSON.stringify(target));
}
原理:ES6模块静态分析
静态分析就是不需要执行代码,就可以从字面量上对代码进行分析。ES6之前的模块化,比如 CommonJS 是动态加载,只有执行后才知道引用的什么模块,就不能通过静态分析去做优化
只适用于函数式编程
无法通过静态分析判断出一个对象的哪些变量未被使用,所以 tree-shaking 只对使用 export 导出的变量生效
// util.js
export default {
targetType(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
},
deepClone(target) {
return JSON.parse(JSON.stringify(target));
}
};
// 引入并使用
import util from '../util';
util.targetType(null)
骨架屏优化白屏时长
虚拟列表:大量数据
长列表虚拟滚动:只渲染可视区,非可见区域的不渲染
计算出 totalHeight 列表总高度,并在触发时滚动事件时根据 scrollTop 值不断更新 startIndex 以及 endIndex ,以此从列表数据 listData 中截取对应元素
虚拟滚动插件
虚拟滚动的插件有很多,比如 vue-virtual-scroller、vue-virtual-scroll-list、react-tiny-virtual-list、react-virtualized
Web Worker 优化长任务
由于浏览器 GUI 渲染线程与 JS 引擎线程是互斥的关系,当页面中有很多长任务时,会造成页面 UI 阻塞,出现界面卡顿、掉帧等情况
查看页面的长任务:
打开控制台,选择 Performance 工具,点击 Start 按钮,展开 Main 选项,会发现有很多红色的三角,这些就属于长任务(长任务:执行时间超过50ms的任务)
适用:当任务的运算时长 - 通信时长 > 50ms
Time 是这个资源的通信时长(也叫加载时长)
requestAnimationFrame 制作动画:刷新频率与显示器的频率保持一致
可以解决用 setTimeout/setInterval 制作动画卡顿的情况
优先级
setTimeout/setInterval 属于 JS引擎
requestAnimationFrame 属于 GUI引擎
JS引擎与GUI引擎
是互斥的: GUI 引擎在渲染时会阻塞 JS 引擎的计算
时间
requestAnimationFrame 刷新频率是固定且准确的
setTimeout/setInterval 是宏任务
根据事件轮询机制,其他任务会阻塞或延迟js任务的执行,会出现定时器不准的情况
性能
当页面被隐藏或最小化时,
setTimeout/setInterval 定时器仍会在后台执行动画任务,
requestAnimationFrame 当页面处于未激活的状态下,屏幕刷新任务会被系统暂停
图片
动态裁剪:在图片url地址上动态添加参数
懒加载
(Load On Demand)延迟加载、按需加载
可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,减少了服务器的负载。懒加载适用于图片较多,页面列表较长(长列表)的场景中。
src 属性发送请求并下载图片
data-xxx 先暂存 src 的值
由于浏览器会自动对页面中的 img 标签的 src 属性发送请求并下载图片,可以通过 html5 自定义属性 data-xxx 先暂存 src 的值,然后在图片出现在屏幕可视区域的时候,再将 data-xxx 的值重新赋值到 img 的 src 属性即可
<img src="" alt="" data-src="./images/1.jpg">
<img src="" alt="" data-src="./images/2.jpg">
vue-lazyload
插件
// 安装
npm install vue-lazyload
// main.js 注册
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// 配置项
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png', // 图片加载失败时的占位图
loading: 'dist/loading.gif', // 图片加载中时的占位图
attempt: 1
})
// 通过 v-lazy 指令使用
<ul>
<li v-for="img in list">
<img v-lazy="img.src" :key="img.src" >
</li>
</ul>
兼容版:window监听scroll事件+节流
- 图片的 src 属性设为默认图片
- 图片的真实路径则设置在data-src属性中,
- 绑定 window 的
scroll
事件,对其进行事件监听。 -
//节流 window.addEventListener('scroll', throttle(lazyload, 200))
- 在scroll事件的回调中,判断我们的懒加载的图片是否进入可视区域,
- 如果图片在可视区内将图片的 src 属性设置为data-src的值
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;//一定记得设置图片高度
}
</style>
</head>
<body>
<img src="./img/default.png" data-src="./img/1.jpg" />
...
<img src="./img/default.png" data-src="./img/10.jpg" />
<script>
function lazyload() {
let viewHeight = document.body.clientHeight //获取可视区高度
//用属性选择器返回属性名为data-src的img元素列表
let imgs = document.querySelectorAll('img[data-src]')
imgs.forEach((item, index) => {
if (item.dataset.src === '') return
// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
let rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src
item.removeAttribute('data-src')//移除属性,下次不再遍历
}
})
}
lazyload()//刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
document.addEventListener("scroll",lazyload)
</script>
</body>
</html>
B
版API:IntersectionObserver
:回调
IntersectionObserver
"交叉观察器"。可见(visible)本质是,目标元素与视口产生一个交叉区
callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)
//callback 是可见性变化时的回调函数,option 是配置对象(该参数可选)
var io = new IntersectionObserver(callback, option)
// 开始观察
io.observe(document.getElementById('example'))
// 停止观察
io.unobserve(element)
// 关闭观察器
io.disconnect()
callback 函数的参数(entries)
是一个数组,每个成员都是一个 IntersectionObserverEntry
对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries
数组就会有两个成员。
- time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
- target:被观察的目标元素,是一个 DOM 节点对象
- isIntersecting: 目标是否可见
- rootBounds:根元素的矩形区域的信息,
getBoundingClientRect()
方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null - boundingClientRect:目标元素的矩形区域的信息
- intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
- intersectionRatio:目标元素的可见比例,即
intersectionRect
占boundingClientRect
的比例,完全可见时为 1,完全不可见时小于等于 0
const config = {
rootMargin: '0px',
threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let img = entry.target
let src = img.dataset.src
if (src) {
img.src = src
img.removeAttribute('data-src')
}
// 解除观察
self.unobserve(entry.target)
}
})
}, config)
imgs.forEach((image) => {
observer.observe(image)
})
图片转 base64 编码字符串
并写入 HTML 或者 CSS 中,减少 http 请求
将小图片转换为 base64 编码字符串,并写入 HTML 或者 CSS 中,减少 http 请求
适用于:小图片、图片大小会膨胀为原文件的 4/3
转 base64 格式的优缺点:
1)它处理的往往是非常小的图片,因为 Base64 编码后,图片大小会膨胀为原文件的 4/3,如果对大图也使用 Base64 编码,后者的体积会明显增加,即便减少了 http 请求,也无法弥补这庞大的体积带来的性能开销,得不偿失
2)在传输非常小的图片的时候,Base64 带来的文件体积膨胀、以及浏览器解析 Base64 的时间开销,与它节省掉的 http 请求开销相比,可以忽略不计,这时候才能真正体现出它在性能方面的优势
url-loader
将图片转 base64:
// 安装
npm install url-loader --save-dev
// 配置
module.exports = {
module: {
rules: [{
test: /.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
// 小于 10kb 的图片转化为 base64
limit: 1024 * 10
}
}]
}]
}
};