首先我们得清楚数据量大导致哪些问题
- 页面加载时间过长,导致浏览器无响应
- 数据量大操作渲染时 DOM 操作过长页面卡顿
- 频繁大量数据可能会导致爆内存
- 查看、操作数据乱用户体验差
- 影响 SEO 优化
解决方案
- 虚拟列表
- 分页加载
- 懒加载
- 异步加载
- 线程加载
- 文档碎片
- freeze
- requestAnimationFrame
- 其他资源
虚拟列表
定义:是一种按需加载的实现方式,对可见区域进行渲染,对非可视区域中的数据不渲染或部分渲染,从而提高渲染性能。
如:可见高度为500px,每条列表项50px,那么每次就最多渲染10个列表项。
let clientHeight = 500, itemHeight = 50
const data = Array(10000).fill({}).map((i,idx)=>({content: `${idx}数据`}))
const init = ()=>{
const renderContent = clientHeight / itemHeight
render(Math.floor(renderContent))
}
const render = (height, start=0)=>{
const renderContent = height / itemHeight
const frame = document.createDocumentFragment()
for(let i=start, i<Math.floor(renderContent), i++){
const temDom = Document.createElement("div")
tempDom.innerHTML = data[i].content
frame.appendChild(tempDom)
}
container.innerHTML = ""
container.appendChild(frame)
}
Element.addEventLiistener("scroll", (event)=>{
event = event.target
if(event.scrollTop){
// push操作
render(Math.floor(event.scrollTop + clientHeight))
}
})
init()
分页加载
定义:对数据每次渲染特定条数,减少全部渲染的重排,从而提高性能。
如:分页pageSize: 20, page: 1
const pagination = { pageSize: 20, page: 1 }
// 默认渲染20条
...
// 分页操作
buttom.addEventListener("click", (event)=>{
event = event.target
value = event.innerText
pageination.page = value
container.innerHTML = ""
render(20, (value-1)*pagination.pageSize)
}
懒加载
定义:使用 settimeout 去实现懒加载(不推荐),除非是在静态资源的加载。
建议不要在工作中使用,它只是把加载工作给延迟了。
Vue 代码
// 模板语法
<div v-for="(item,key) in applyList" :key="key"> ... </div>
onMounted(()=>{
setTimeout(()=>{
applyList.value = data
})
})
异步加载
定义:使用 ES6 async 去实现异步加载,不阻塞当前线程(不推荐),推荐使用异步加载配合文档碎片实现。
const rederAll = async()=>{
const batch = 100
const frame = document.createDocumentFragment()
const createEl = ()=>{
for(let i=0; i<100; i++){
const div = document.createElement("div")
div.innerHTML = "xxx"
frame.appendChild(div)
}
}
for(let i=100; i<10000; i+=100){
createEl()
}
}
线程加载
定义:在Web worker处理大量数据操作。
const worker = new Worker(render.js)
worker.addEventListener("message", ({data})=>{
container.appendChild(data)
})
// render.js
self.postMessage(处理之后的数据)
文档碎片
定义:推荐在原生的开发环境下使用,通过创建文档碎片来避免过渡的回流去操作DOM实现渲染。
作用:减少浏览器重排,通过创建一个浏览器容器对象,来添加DOM操作,统一进行添加至DOM树。
let batchSize = 100
let num = batchSize
const body = document.body
const fragment = document.createDocumentFragment()
for (let i = 0; i < num; i++) {
const div = document.createElement('div')
div.innerText = 111111111
fragment.append(div)
}
body.appendChild(fragment)
const fragment1 = document.createDocumentFragment()
const temp = setInterval(() => {
if (num >= len) {
clearInterval(temp)
document.body.appendChild(fragment1)
}
for (let i = 0; i < batchSize; i++) {
const div = document.createElement('div')
div.innerText = 22222
fragment1.append(div)
}
num += batchSize
})
freeze
定义:在Vue或者React中使用导致数据被代理或绑定,会影响性能,使用freeze冻结对象使得对象只读。
场景:使用freeze结对象可以防止触发代理的setter和getter。
// Vue
const list = ref([])
Object.freeze(list.value)
// React
const [list, setList] = useState([])
Object.freeze(list)
requestAnimationFrame
定义:浏览器执行动画的方法,在下次重绘之前调用,不会触发回流。
const myRender = ()=>{
render()
if(条件){
requestAnimation(myRender)
}
}
requestAnimation(myRender)
其他资源
在商城后台管理系统中,列表大概率会出现商品图片,这时就可以结合上面的解决方案来实现。\
- 图片压缩,使用webp压缩图片大小
- lazy加载
- 后端定义响应头缓存图片
- 小图标可以使用精灵图或者字体图表来标识