浏览器渲染问题疑惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Alander
V2EX    前端开发

浏览器渲染问题疑惑

  •  
  •   Alander 2021-07-22 10:27:16 +08:00 1578 次点击
    这是一个创建于 1619 天前的主题,其中的信息可能已经有所发展或是发生改变。
    <body> <div id="root"> </div> </body> <script> const div = document.querySelector('#root') div.innerHTML = '1' const now = performance.now() cnsole.log(div.innerHTML); while (performance.now() - now < 100) { console.log(now) } console.log(div.innerHTML); div.innerHTML = '2' console.log(div.innerHTML); </script> 

    不懂就问:

    这段代码在 chrome 渲染直接渲染成 2,没有 1 的过程,是为什么?这段 while 阻塞不生效? js 上是符合代码的逻辑的,但是渲染上是直接 2,有什么资料可供查看?

    第 1 条附言    2021-07-22 13:36:22 +08:00
    我没表示明白我的想法:确实不应该有 1 这个过程,因为宏任务执行结束后再渲染,此时应该是 2,我的疑惑是上述 while 循环在我的理解里面应该阻塞住浏览器渲染,也就是 100ms 以后再渲染出 2,实际上页面一进入就直接渲染出 2 了。
    第 2 条附言    2021-07-22 13:41:14 +08:00
    是我理解错误了现象,实际上就是如第一次 append 所说的,会先空白等待再渲染出 2,而非直接渲染出 2,我以为的直接渲染出 2 是因为刷新遗留的 2,并非本次渲染,此贴终结,感谢耐心回复的人
    runze
        1
    runze  
       2021-07-22 10:36:24 +08:00
    cyrbuzz
        2
    cyrbuzz  
       2021-07-22 12:05:43 +08:00
    参考下这个: https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

    说下自己的理解,里面提到第 11 步是更新渲染,11 之后还有两步,一个是执行浏览器空闲回调(requestsIdleCallback)一个是 worker 的回调。

    11 步之前是执行 JS,JS 主线程就一个线程,UI 渲染会等待 JS 执行,所以你这个阻塞也阻塞了渲染,

    ```
    const div = document.querySelector('#root')
    div.innerHTML = '1'
    const now = performance.now()
    console.log(div.innerHTML);
    ```
    执行到这里如果没有后续代码,浏览器会尝试执行微任务栈,然后执行 UI 渲染,此时就会渲染 1 。

    但要注意 innerHTML 实时改变了 DOM,但不是触发渲染的条件,改变 DOM != 渲染,走完这个循环才是执行渲染的条件。

    ```
    while (performance.now() - now < 100) {
    console.log(now)
    }
    console.log(div.innerHTML);
    div.innerHTML = '2'
    console.log(div.innerHTML);
    ```

    加上下面这些,只是多阻塞了主线程一会,此时走完前面的 1-10,去执行 UI 渲染,而是不断执行 console.log(now),之后执行到了:

    ```
    div.innerHTML = '2'
    console.log(div.innerHTML);
    ```
    执行渲染时的 innerHTML 已经是 2,所以渲染出来 2 了。

    一般可以用 setTimeout+Promise,这样不会阻塞 UI 渲染,只会阻塞后续代码执行:

    ```
    function sleep(times) {
    return new Promise((resolve) => {
    setTimeout(() => {
    resolve()
    }, times)
    })
    }
    ```

    ```
    async function draw() {
    const div = document.querySelector('#root')
    div.innerHTML = '1'
    //const now = performance.now()
    //console.log(div.innerHTML);
    //while (performance.now() - now < 100) {
    // console.log(now)
    //}
    await sleep(100)
    console.log(div.innerHTML);
    div.innerHTML = '2'
    console.log(div.innerHTML);
    }

    draw()
    ```

    相关扩展可以了解一下 Vue 的$nextTick,曾经某一个版本把它从微任务改到了宏任务,此时用$nextTick 改变 DOM 的 Style 会出现抖动(因为 宏-> 微 -> UI 渲染 -> 宏 2),本来任务应该在 UI 渲染前结果成了 UI 渲染后。
    Alander
        3
    Alander  
    OP
       2021-07-22 12:25:25 +08:00
    @cyrbuzz 我可能没表述明白这个事情,页面的表现是:已进入页面直接显示 2,同时控制台不停在打印 console.log(now);

    我的疑惑是为什么不是。先渲染出 1,100 毫秒后渲染出 2 或者 先是空白,100ms 后直接渲染 2 ?有点不明白,我去看下你提到的规范说明
    cyrbuzz
        4
    cyrbuzz  
       2021-07-22 13:09:10 +08:00
    @Alander

    emm,感觉我有说明= =,我简化一下:

    innerHTML 并非触发渲染的条件,执行完整个循环才是,包裹你写的阻塞在内的所有代码都属于渲染前执行的任务。
    Alander
        5
    Alander  
    OP
       2021-07-22 13:29:53 +08:00
    @cyrbuzz 我理解你的意思,现在的 chrome 表现情况是 js 在执行中,但是页面已经渲染出来了 2,不好意思哈,可能是我没理解你的意思,我只是再确认一下我有没有表述明白:这段 html 在浏览器中执行页面的渲染并没有被阻塞直接渲染出<div id='root'>2</div>,同时 js 代码运行 while 循环,而非先 js 运行完 while 循环再执行渲染。如果说是你明白了我的意思但是我没有明白你的解释真的不好意思哈,我再仔细找下其他资料。
    Alander
        6
    Alander  
    OP
       2021-07-22 13:31:57 +08:00
    @cyrbuzz innerHTML 并非触发渲染的条件,执行完整个循环才是,包裹你写的阻塞在内的所有代码都属于渲染前执行的任务。
    你的这段话也是我的想法,但是实际页面表现与我的预期不一致,按照这个想法是否是先 while 循环结束后再渲染出字符串 2 ?但是实际上是页面一加载就直接渲染出了 2
    Alander
        7
    Alander  
    OP
       2021-07-22 13:39:55 +08:00
    @cyrbuzz 哈哈哈,不好意思,是我理解错浏览器了,我看见的是刷新前渲染的结果误以为是一进来就是 2,不好意思哈,你的解释是正确的
    3dwelcome
        8
    3dwelcome  
       2021-07-22 13:46:55 +08:00
    我测试了一下,阻塞没问题啊。

    div.innerHTML = '1'
    这句正常执行了,但是被后面的 while()语句阻塞了,所以 1 没办法正常显示出来。

    你之所以老是看到页面是 2,是因为上次的页面没刷新。而不是 JS 跳过 while(),自己去执行了后面的 div.innerHTML = '2'
    hazardous
        9
    hazardous  
       2021-07-22 13:54:50 +08:00
    100 毫秒太短所以感觉不明显,改成 3000 或者 5000 再试。
    Alander
        10
    Alander  
    OP
       2021-07-22 14:27:49 +08:00
    @3dwelcome 是的是的,是上次页面没刷新导致的我的错觉
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1343 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 16:57 PVG 00:57 LAX 08:57 JFK 11:57
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86