全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2025-01-12_nextTick用过吗?讲一讲实现思路吧

您的位置:首页 >> 新闻 >> 行业资讯

nextTick用过吗?讲一讲实现思路吧 点击关注公众号,“技术干货”及时达! 源码实现思路(面试高分回答) ??面试官问我 Vue 的 nextTick 原理是怎么实现的,我这样回答: 在调用 this.$nextTick(cb) 之前: 存在一个 callbacks 数组,用于存放所有的 cb 回调函数。存在一个 flushCallbacks 函数,用于执行 callbacks 数组中的所有回调函数。存在一个 timerFunc 函数,用于将 flushCallbacks 函数添加到任务队列中。当调用 this.nextTick(cb) 时: nextTick 会将 cb 回调函数添加到 callbacks 数组中。判断在当前事件循环中是否是第一次调用 nextTick:如果是第一次调用,将执行 timerFunc 函数,添加 flushCallbacks 到任务队列。如果不是第一次调用,直接下一步。如果没有传递 cb 回调函数,则返回一个 Promise 实例。 根据上述描述,对应的`流程图`如下: graph TD A["this.$nextTick(callback)"] -- B[将回调函数 callback 放入到数组 callbacks 中] B -- C[判断是否是第一次调用 nextTick] C --|是| D[执行 timerFunc, 将 flushCallbacks 添加到任务队列] C --|否| F[如果没有 cb, 则retrun Promise] D -- F F -- 结束 如果上面的描述没有很理解。没关系,花几分钟跟着我下面来,看完下面的源码逐行讲解,你一定能够清晰地向别人讲出你的思路! nextTick思路详解 ???♂??1. 核心代码 ??下面用十几行代码,就已经可以基本实现「nextTick」的功能(默认浏览器支持「Promise」) //存储所有的cb回调函数 constcallbacks= /*类似于节流的标记位,标记是否处于节流状态。防止重复推送任务*/ letpending=false; /*遍历执行数组callbacks中的所有存储的cb回调函数*/ functionflushCallbacks(){ //重置标记,允许下一个nextTick调用 pending=false; /*执行所有cb回调函数*/ for(leti=0;icallbacks.length;i++){ callbacks[i](); } //清空回调数组,为下一次调用做准备 callbacks.length=0; } functionnextTick(cb){ //将回调函数cb添加到callbacks数组中 callbacks.push(()={ cb(); //第一次使用nextTick时,pending为false,下面的代码才会执行 if(!pending){ //改变标记位的值,如果有flushCallbacks被推送到任务队列中去则不需要重复推送 pending=true; //使用Promise机制,将flushCallbacks推送到任务队列 Promise.resolve().then(flushCallbacks); } } 「测试一下:」 letmessage='初始消息'; nextTick(()={ message='更新后的消息'; console.log('回调:',message);//输出2:更新后的消息 }); console.log('测试开始:',message);//输出1:初始消息 如果你想要应付面试官,能手写这部分核心原理就已经差不多啦。 如果你想彻底掌握它,请继续跟着我来!!!?????♂ 2. nextTick() 返回promise ??我们在开发中,会使用await this.$nextTick();让其下面的代码全部变成异步代码。 比如写成这样: awaitthis.$nextTick(); ...... ...... //或者 this.$nextTick().then(()={ ...... }) 核心就是nextTick()如果没有参数,则返回一个promise constcallbacks= letpending=false; functionflushCallbacks(){ pending=false; for(leti=0;icallbacks.length;i++){ callbacks[i](); } callbacks.length=0; } functionnextTick(cb){ //用于存储Promise的resolve函数 let_resolve; callbacks.push(()={ /*------------------新增start------------------*/ //如果有cb回调函数,将cb存储到callbacks if(cb){ cb(); }elseif(_resolve){ //如果参数cb不存在,则保存promise的的成功回调resolve _resolve(); } /*------------------新增end------------------*/ if(!pending){ pending=true; Promise.resolve().then(flushCallbacks); } /*------------------新增start------------------*/ if(!cb){ returnnewPromise((resolve,reject)={ //保存resolve到callbacks数组中 _resolve=resolve; } /*------------------新增end------------------*/ } 「测试一下:」 asyncfunctiontestNextTick(){ letmessage="初始消息"; nextTick(()={ message="更新后的消息"; console.log("传入回调:",message);//输出1:初始消息 //不传入回调的情况 awaitnextTick();//nextTick返回Promise console.log("未传入回调后:",message);//输出2:更新后的消息 } //运行测试 testNextTick(); 3. 判断浏览器环境 ??为了防止浏览器不支持 「Promise」,「Vue」 选择了多种 API 来实现兼容 「nextTick」: Promise -- MutationObserver -- setImmediate -- setTimeout 「Promise」 (微任务): 如果当前环境支持 「Promise」,「Vue」 会使用 Promise.resolve().then(flushCallbacks) 「MutationObserver」 (微任务): 如果不支持 「Promise」,支持 「MutationObserver」。「Vue」 会创建一个 「MutationObserver」 实例,通过监听文本节点的变化来触发执行回调函数。 「setImmediate」 (宏任务): 如果前两者都不支持,支持 「setImmediate」。则:setImmediate(flushCallbacks) 注意:「setImmediate」 在绝大多数浏览器中不被支持,但在 「Node.js」 中是可用的。 「setTimeout」 (宏任务): 如果前面所有的都不支持,那你的浏览器一定支持 「setTimeout」!!! 终极方案:setTimeout(flushCallbacks, 0) //存储所有的回调函数 constcallbacks= /*类似于节流的标记位,标记是否处于节流状态。防止重复推送任务*/ letpending=false; /*遍历执行数组callbacks中的所有存储的cb回调函数*/ functionflushCallbacks(){ //重置标记,允许下一个nextTick调用 pending=false; /*执行所有cb回调函数*/ for(leti=0;icallbacks.length;i++){ callbacks[i]();//依次调用存储的回调函数 } //清空回调数组,为下一次调用做准备 callbacks.length=0; } //判断最终支持的API:Promise/MutationObserver/setImmediate/setTimeout lettimerFunc; if(typeofPromise!=="undefined"){ //创建一个已resolve的Promise实例 varp=Promise.resolve(); //定义timerFunc为使用Promise的方式调度flushCallbacks timerFunc=()={ //使用p.then方法将flushCallbacks推送到微任务队列 p.then(flushCallbacks); }elseif( typeofMutationObserver!=="undefined"&& MutationObserver.toString()==="[objectMutationObserverConstructor]" ){ /*新建一个textNode的DOM对象,用MutationObserver绑定该DOM并指定回调函数。 在DOM变化的时候则会触发回调,该回调会进入主线程(比任务队列优先执行), 即textNode.data=String(counter)时便会加入该回调*/ varcounter=1;//用于切换文本节点的值 varobserver=newMutationObserver(flushCallbacks);//创建MutationObserver实例 vartextNode=document.createTextNode(String(counter));//创建文本节点 observer.observe(textNode,{ characterData:true,//监听文本节点的变化 //定义timerFunc为使用MutationObserver的方式调度flushCallbacks timerFunc=()={ counter=(counter+1)%2;//切换counter的值(0或1) textNode.data=String(counter);//更新文本节点以触发观察者 }elseif(typeofsetImmediate!=="undefined"){ /*使用setImmediate将回调推入任务队列尾部*/ timerFunc=()={ setImmediate(flushCallbacks);//将flushCallbacks推送到宏任务队列 }else{ /*使用setTimeout将回调推入任务队列尾部*/ timerFunc=()={ setTimeout(flushCallbacks,0);//将flushCallbacks推送到宏任务队列 } functionnextTick(cb){ //用于存储Promise的解析函数 let_resolve; //将回调函数cb添加到callbacks数组中 callbacks.push(()={ //如果有cb回调函数,将cb存储到callbacks if(cb){ cb(); }elseif(_resolve){ //如果参数cb不存在,则保存Promise的成功回调resolve _resolve(); } //第一次使用nextTick时,pending为false,下面的代码才会执行 if(!pending){ //改变标记位的值,如果有nextTickHandler被推送到任务队列中去则不需要重复推送 pending=true; //调用timerFunc,将flushCallbacks推送到合适的任务队列 timerFunc(flushCallbacks); } //如果没有cb且环境支持Promise,则返回一个Promise if(!cbtypeofPromise!=="undefined"){ returnnewPromise((resolve)={ //保存resolve到callbacks数组中 _resolve=resolve; } } 你真的太牛了,居然几乎全部看完了! Vue纯源码上面的代码实现,对于 「nextTick」 功能已经非常完整了,接下来我将给你展示出 「Vue」 中实现 「nextTick」 的完整源码。无非是加了一些判断变量是否存在的判断。看完上面的讲解,我相信聪明的你一定能理解 「Vue」 实现 「nextTick」 的源码了吧!?? //存储所有的cb回调函数 constcallbacks= /*类似于节流的标记位,标记是否处于节流状态。防止重复推送任务*/ letpending=false; /*遍历执行数组callbacks中的所有存储的cb回调函数*/ functionflushCallbacks(){ pending=false;//重置标记,允许下一个nextTick调用 constcopies=callbacks.slice(0);//复制当前的callbacks数组 callbacks.length=0;//清空callbacks数组 for(leti=0;icopies.length;i++){ copies[i]();//执行每一个存储的回调函数 } } //判断是否为原生实现的函数 functionisNative(Ctor){ //如Promise.toString()为'functionPromise(){[nativecode]}' returntypeofCtor==="function"/nativecode/.test(Ctor.toString()); } //判断最终支持的API:Promise/MutationObserver/setImmediate/setTimeout lettimerFunc; if(typeofPromise!=="undefined"isNative(Promise)){ constp=Promise.resolve();//创建一个已解决的Promise实例 timerFunc=()={ p.then(flushCallbacks);//使用p.then将flushCallbacks推送到微任务队列 //在某些有问题的UIWebView中,Promise.then并不会完全失效, //但可能会陷入一种奇怪的状态:回调函数被添加到微任务队列中, //但队列并没有被执行,直到浏览器需要处理其他工作,比如定时器。 //因此,我们可以通过添加一个空的定时器来“强制”执行微任务队列。 if(isIOS)setTimeout(()=//解决iOS的bug,推迟空函数的执行(如果不理解,建议忽略) }elseif( typeofMutationObserver!=="undefined"&& (isNative(MutationObserver)|| MutationObserver.toString()==="[objectMutationObserverConstructor]") ){ letcounter=1;//用于切换文本节点的值 constobserver=newMutationObserver(flushCallbacks);//创建MutationObserver实例 consttextNode=document.createTextNode(String(counter));//创建文本节点 observer.observe(textNode,{ characterData:true,//监听文本节点的变化 //定义timerFunc为使用MutationObserver的方式调度flushCallbacks timerFunc=()={ counter=(counter+1)%2;//切换counter的值(0或1) textNode.data=String(counter);//更新文本节点以触发观察者 }elseif(typeofsetImmediate!=="undefined"isNative(setImmediate)){ timerFunc=()={ setImmediate(flushCallbacks);//使用setImmediate推送到任务队列 }else{ timerFunc=()={ setTimeout(flushCallbacks,0);//使用setTimeout推送到宏任务队列 } functionnextTick(cb,ctx){ let_resolve;//用于存储Promise的解析函数 //将回调函数cb添加到callbacks数组中 callbacks.push(()={ if(cb){ try{ cb.call(ctx);//执行传入的回调函数 }catch(e){ handleError(e,ctx,"nextTick");//错误处理 } }elseif(_resolve){ _resolve(ctx);//解析Promise } //第一次使用nextTick时,pending为false,下面的代码才会执行 if(!pending){ pending=true;//改变标记位的值 timerFunc();//调用timerFunc,调度flushCallbacks } //如果没有cb且环境支持Promise,则返回一个Promise if(!cbtypeofPromise!=="undefined"){ returnnewPromise((resolve)={ _resolve=resolve;//存储解析函数 } } 总结通过这样分成三步、循序渐进的方式,我们深入探讨了 「nextTick」 的原理和实现机制。希望这篇文章能够对你有所帮助,让你在前端开发的道路上更加得心应手!?? 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2020-10-15_清华首次提出「类脑计算完备性」及计算系统层次结构,登上Nature 下一篇:2019-06-25_所有城市街头,都在发生相同的故事

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价