前言
当前需要向页面插入十万个div元素,如果使用普通的渲染方式,会造成延迟。这时候就需要通过分时函数来实现渲染了。
html
<div class="w_680 d_f jc_sb">
<div class="d_f fd_c ai_c">
<button onclick="handleInsert()">插入1万个元素(原始写法)</button>
<div id="idOld"></div>
</div>
<div class="d_f fd_c ai_c">
<button onclick="handleOptimize()">插入1万个元素(优化后的写法)</button>
<div id="idOptimize"></div>
</div>
</div>
原始写法
function handleInsert() {
let idOld = document.querySelector('#idOld'),
datas = Array.from({ length: 100000 }, (_, i) => i + 1);
for (const item of datas) {
const div = document.createElement('div');
div.textContent = item;
idOld.appendChild(div);
}
}
优化方式一(参数归一化)
function handleOptimize() {
let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
idOptimize = document.querySelector('#idOptimize'),
taskHandler = (item, i) => {
const div = document.createElement('div');
div.textContent = item;
idOptimize.appendChild(div);
};
performChunk(datas, taskHandler);
// performChunk(100000, taskHandler);
}
function performChunk(datas, taskHandler) {
// 参数归一化
if (typeof datas === 'number') datas = Array.from({ length: datas }, (_, i) => i + 1);
if (datas.length === 0) return false;
let i = 0;
function _run() {
if (i >= datas.length) return false;
requestIdleCallback((idle) => {
while (idle.timeRemaining() > 0 && i < datas.length) {
taskHandler(datas[i], i);
i++;
}
_run();
});
}
_run();
}
优化方式二(当浏览器不支持requestIdleCallback方法的时候)
function handleOptimize() {
let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
idOptimize = document.querySelector('#idOptimize'),
taskHandler = (item, i) => {
const div = document.createElement('div');
div.textContent = item;
idOptimize.appendChild(div);
},
scheduler = (task) => {
setTimeout(() => {
const start = Date.now();
task(() => Date.now() - start < 50);
}, 100);
};
performChunk(datas, taskHandler, scheduler);
}
function performChunk(datas, taskHandler, scheduler) {
if (typeof datas === 'number') datas = Array.from({ length: datas }, (_, i) => i + 1);
if (datas.length === 0) return false;
let i = 0;
function _run() {
if (i >= datas.length) return false;
scheduler((isGoOn) => {
while (isGoOn() > 0 && i < datas.length) {
taskHandler(datas[i], i);
i++;
}
_run();
});
}
_run();
}
优化方式三(判断环境)
function handleOptimize() {
let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
idOptimize = document.querySelector('#idOptimize'),
taskHandler = (item, i) => {
const div = document.createElement('div');
div.textContent = item;
idOptimize.appendChild(div);
}
browserPerformChunk(datas, taskHandler);
}
function browserPerformChunk(datas, taskHandler) {
const scheduler = (task) => {
requestIdleCallback((idle) => {
task(() => idle.timeRemaining() > 0);
})
};
performChunk(datas, taskHandler, scheduler);
}
function performChunk(datas, taskHandler, scheduler) {
if (typeof datas === 'number') datas = Array.from({ length: datas }, (_, i) => i + 1);
if (datas.length === 0) return false;
let i = 0;
function _run() {
if (i >= datas.length) return false;
scheduler((isGoOn) => {
while (isGoOn() > 0 && i < datas.length) {
taskHandler(datas[i], i);
i++;
}
_run();
});
}
_run();
}
解析
待定…
html
<div id="menuBox">div>
JavaScript
mainInit();
// 数据处理
// 扁平数组结构转换成JSON树形结构
function mainInit() {
// 源数据
let data = [
{ id: 5, parentId: 2, name: "目录1-1-1", },
{ id: 1, parentId: 0, name: "目录1", },
{ id: 13, parentId: 9, name: "目录2-2-3", },
{ id: 3, parentId: 1, name: "目录1-2", },
{ id: 2, parentId: 1, name: "目录1-1", },
{ id: 12, parentId: 9, name: "目录2-2-2", },
{ id: 4, parentId: 1, name: "目录1-3", },
{ id: 7, parentId: 0, name: "目录2", },
{ id: 9, parentId: 7, name: "目录2-2", },
{ id: 6, parentId: 2, name: "目录1-1-2", },
{ id: 11, parentId: 9, name: "目录2-2-1", },
{ id: 8, parentId: 7, name: "目录2-1", },
{ id: 10, parentId: 7, name: "目录2-3", },
];
// 构造一个新对象
// 对象的属性名是id值
// 对象的属性值是数组对应项的值
let map = {};
data.forEach(item => {
map[item.id] = item;
});
const result = [];
data.forEach(item => {
const parent = map[item.parentId];
if (parent) {
// 如果有children则push数据到children中
// 如果没有就新建一个空的children数组
// 然后再push数据
parent.children = parent.children || [];
parent.children.push(item);
} else {
// 在本例中
// else只会进入两次
// 也是最外层目录
result.push(item);
}
});
const root = document.getElementById('menuBox');
createMenu(result, root);
}
// 生成标签元素
function createMenu(data, root) {
if (!data.length) throw new Error('出错啦');
data.forEach(item => {
const detailsEle = document.createElement('details');
const summaryEle = document.createElement('summary');
const span = document.createElement('span');
span.innerHTML = item.name;
summaryEle.appendChild(span);
detailsEle.appendChild(summaryEle);
root.appendChild(detailsEle);
if (item.children?.length) createMenu(item.children, detailsEle);
});
}
CSS
null