是否可以指示浏览器在异步 DOM 操作期间不要渲染/重排/更新视口?
Posted
技术标签:
【中文标题】是否可以指示浏览器在异步 DOM 操作期间不要渲染/重排/更新视口?【英文标题】:Is it possible to instruct the browser not to render/reflow/update the viewport during asynchronous DOM manipulations? 【发布时间】:2019-05-16 19:12:58 【问题描述】:我有这样的事情:
async function async_DOM_manipulations()
await DOM_manipulation_1() ;
await DOM_manipulation_2() ;
await DOM_manipulation_3() ;
浏览器(我使用的是 Chrome)坚持在异步 DOM_manipulation_x
函数之间呈现页面。
但这看起来不太好。用户所看到的只是 GUI 在几分之一秒内如何在几个步骤中发生变化。
是否可以在这个过程中以某种方式指示浏览器停止渲染页面?
【问题讨论】:
@Andy,很难给出这个问题的工作示例。我将不得不发布太多代码。问题很简单。浏览器是否提供了一种机制来控制何时呈现页面? 我只是将子元素添加到父容器并设置一些类名。 是的。在新的断开/隐藏元素中执行 DOM 操作,然后用新元素替换旧元素。从而避免中间回流。但我要问的问题不同而且简单得多。浏览器是否允许开发人员决定何时更新视口,或者我们被浏览器自动做出的决定所困扰,我们必须解决它们。我隐约记得读过一些关于 API 的东西来做到这一点。这就是我问这个问题的原因。 【参考方案1】:我认为,根据我的测试,这是不可能的。我能看到的唯一一种方法是为某个内部结构准备一批更新,并在所有异步操作结束时立即更新 DOM。
所以(TypeScript)代码看起来像
interface DOMOperation
node: Node,
operation: (...args: any) => void;
args: any[];
export class DOMBatch
private queue: DOMOperation[] = [];
private updateRunning: boolean = false;
public async beginUpdate(): Promise<void>
return new Promise(
(resolve) =>
const checkUpdateRunning = () =>
if (this.updateRunning)
setTimeout(() => checkUpdateRunning() , 0); // each new javascript loop check if the update is running
else
this.queue = [];
resolve();
checkUpdateRunning();
)
public endUpdate()
for(const op of this.queue)
op.operation.apply(op.node, op.args);
this.updateRunning = false;
public appendChild(target: Node, childNode: Node)
this.queue.push( node: target, operation: target.appendChild, args: [ childNode ] );
public removeChild(target: Node, childNode: Node)
this.queue.push( node: target, operation: target.removeChild, args: [ childNode ]);
public insertBefore(target: Node, toInsert: Node, before: Node)
this.queue.push( node: target, operation: target.insertBefore, args: [ toInsert, before ] );
public setAttribute(target: Element, name: string, value: string)
this.queue.push( node: target, operation: target.setAttribute, args: [ name, value] );
public removeAttribute(target: Element, name: string)
this.queue.push( node: target, operation: target.attributes.removeNamedItem, args: [ name ] );
...
const domBatch = new DOMBatch();
...
async function async_DOM_manipulations(): Promise<void>
await domBatch.beginUpdate(); // cleanup the queue and wait for other possible renders running
await prepare_DOM_manipulation_1(); // fill the batch with updates
await prepare_DOM_manipulation_2(); // fill the batch with updates
await prepare_DOM_manipulation_3(); // fill the batch with updates
domBatch.endUpdate(); // render the batch
必须确保在异步任务和最终 updateDOM 之间没有进行另一个异步 DOM 操作,因此对 beginUpdate() 的调用应该返回承诺
【讨论】:
以上是关于是否可以指示浏览器在异步 DOM 操作期间不要渲染/重排/更新视口?的主要内容,如果未能解决你的问题,请参考以下文章
script标签的async属性是用来异步加载,异步加载的作用是否同时下载,执行html代码和js代码