最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    正文概述 掘金(魔术师卡颂)   2021-11-16   678

    大家好,我卡颂。

    前端框架中经常有将多个自变量变化触发的更新合并为一次执行的批处理场景,框架的类型不同,批处理的时机也不同。

    比如如下Svelte代码,点击H1后执行onClick回调函数,触发三次更新。由于批处理,三次更新会合并为一次。

    接着分别以同步、微任务、宏任务的形式打印渲染结果:

    <script>
      let count = 0;
      let dom;
      const onClick = () => {
        // 三次更新合并为一次
        count++;
        count++;
        count++;
      
        console.log("同步结果:", dom.innerText);
      
        Promise.resolve().then(() => {
          console.log("微任务结果:", dom.innerText);
        });
      
        setTimeout(() => {
          console.log("宏任务结果:", dom.innerText);
        });
      }
    </script>
    
    <h1 bind:this={dom} on:click={onClick}>{count}</h1>
    

    同样的逻辑用不同框架实现,打印结果如下:

    • Vue3:同步结果:0 微任务结果:3 宏任务结果:3
    • Svelte:同步结果:0 微任务结果:3 宏任务结果:3
    • Legacy React:同步结果:0 微任务结果:3 宏任务结果:3
    • Concurrent React:同步结果:0 微任务结果:0 宏任务结果:3

    Vue3 Svelte

    本质原因在于:有的框架使用宏任务实现批处理,有的框架使用微任务实现批处理。

    本文接下来会讲解宏任务微任务的起源,以及他们与批处理的关系。

    欢迎加入人类高质量前端框架研究群,带飞

    如何调度任务

    先放上完整流程图,方便有个整体印象:

    为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    默认情况下,浏览器(以Chrome为例)中每个Tab页对应一个渲染进程,渲染进程包含主线程、合成线程、IO线程等多个线程。

    主线程的工作非常繁忙,要处理DOM、计算样式、处理布局、处理事件响应、执行JS等。

    为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    这里有两个问题需要解决:

    1. 这些任务不仅来自线程内部,也可能来自外部,如何调度这些任务?

    2. 主线程在工作过程中,新任务如何参与调度?

    第一个问题的答案是:消息队列

    所有参与调度的任务会加入任务队列中。根据队列先进先出的特性,最早入队的任务会被最先处理。用伪代码描述如下:

    // 从任务队列中取出任务
    const task = taskQueue.takeTask();
    // 执行任务
    processTask(task);
    

    其他进程通过IPC将任务发送给渲染进程的IO线程,IO线程再将任务发送给主线程的任务队列,比如:

    • 鼠标点击后,浏览器进程通过IPC将“点击事件”发送给IO线程,IO线程将其发送给任务队列

    • 资源加载完成后,网络进程通过IPC将“加载完成事件”发送给IO线程,IO线程将其发送给任务队列

    如何调度新任务

    第二个问题的答案是:事件循环

    主线程会在循环语句中执行任务。随着循环一直进行下去,新加入的任务会插入队列末尾,老任务会被取出执行。用伪代码描述如下:

    // 退出事件循环的标识
    let keepRunning = true;
    
    // 主线程
    function MainThread() {
      // 循环执行任务
      while(true) {
        // 从任务队列中取出任务
        const task = taskQueue.takeTask();
        // 执行任务
        processTask(task);
    
        if (!keepRunning) {
          break;
        }
      }
    }
    

    延迟任务

    除了任务队列,浏览器还根据WHATWG标准,实现了延迟队列,用于存放需要被延迟执行的任务(如setTimeout),伪代码如下:

    function MainThread() {
      while(true) {
        const task = taskQueue.takeTask();
        processTask(task);
    
        //执行延迟队列中的任务 
        processDelayTask()
    
        if (!keepRunning) {
          break;
        }
      }
    }
    

    当本轮循环任务执行完后(即执行完processTask后),会执行processDelayTask检查是否有延迟任务到期,如果有任务过期则执行他。

    为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    介于processDelayTask的执行时机在processTask之后,所以当任务的执行时间比较长,可能会导致延迟任务无法按期执行。考虑如下代码:

    function sayHello() { console.log('hello') }
    
    function test() { 
      setTimeout(sayHello, 0); 
      for (let i = 0; i < 5000; i++) {
        console.log(i);
      }
    }
    test()
    

    即使将延迟任务sayHello的延迟时间设为0,也需要等待test所在任务执行完后才能执行,所以sayHello最终的延迟时间是大于设定时间的。

    宏任务与微任务

    加入任务队列的新任务需要等待队列中其他任务都执行完后才能执行,这对于突发情况下需要优先执行的任务是不利的。

    为了解决时效性问题,任务队列中的任务被称为宏任务,在宏任务执行过程中可以产生微任务,保存在该任务执行上下文中的微任务队列中。

    即流程图中右边的部分:

    为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    宏任务执行结束前会遍历其微任务队列,将该宏任务执行过程中产生的微任务批量执行。

    MutationObserver

    微任务是如何解决时效性问题同时又兼顾性能呢?

    考虑用于监控DOM变化的微任务API —— MutationObserver

    当同一个宏任务中发生多次DOM变化,会产生多个MutationObserver微任务,其执行时机是该宏任务执行结束前,相比于作为新的宏任务进入队列等待执行,保证了时效性。

    同时,由于微任务队列内的微任务被批量执行,相比于每次DOM变化都同步执行回调,性能更佳。

    为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    总结

    框架中批处理的实现本质和MutationObserver非常类似。利用了宏任务微任务异步执行的特性,将更新打包后执行。

    只不过不同框架由于更新粒度不同,比如Vue3Svelte更新粒度很细,所以使用微任务实现批处理。

    React更新粒度比较粗,但内部实现比较复杂,即有宏任务的场景也有微任务的场景。


    下载网 » 为啥同样的逻辑在不同前端框架中效果不同 - 掘金

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元