浅析VUE中的异步渲染机制nextTick原理及如何改为同步渲染古兰精

这个问题应该先要做一个前提补充,我们知道当数据在同步变化的时候,页面订阅的响应操作为什么不会与数据变化完全对应,而是在所有的数据变化操作做完之后,页面才会得到响应,完成页面渲染。

从一个例子体验一下异步渲染机制。

importVuefrom'Vue'newVue({el:'#app',template:'

{{val}}
',data(){return{val:'init'}},mounted(){this.val='我是第一次页面渲染'//debuggerthis.val='我是第二次页面渲染'constst=Date.now()while(Date.now()-st<3000){}}})上面这一段代码中,在mounted里给val属性进行了两次赋值,如果页面渲染与数据的变化完全同步的话,页面应该是在mounted里有两次渲染。

然而由于Vue内部的异步渲染机制,实际上页面只会渲染一次,把第一次的赋值所带来的的响应与第二次的赋值所带来的的响应进行一次合并,将最终的val只做一次页面渲染,而且页面是在执行所有的同步代码执行完之后才能得到渲染。

在上述例子里的while阻塞代码之后,页面才会得到渲染,就像在熟悉的setTimeout里的回调函数的执行一样,这就是的异步渲染。熟悉React的同学,应该很快能想到多次执行setState函数时,页面render的渲染触发,实际上与上面所说的Vue的异步渲染有异曲同工之妙。

我们可以从用户和性能两个角度来探讨这个问题。

从用户体验角度,从上面例子里便也可以看出,实际上我们的页面只需要展示第二次的值变化,第一次只是一个中间值,如果渲染后给用户展示,页面会有闪烁效果,反而会造成不好的用户体验。

从性能角度,例子里最终的需要展示的数据其实就是第二次给val赋的值,如果第一次赋值也需要页面渲染则意味着在第二次最终的结果渲染之前页面还需要渲染一次无用的渲染,无疑增加了性能的消耗。

对于浏览器来说,在数据变化下,无论是引起的重绘渲染还是重排渲染,都有可能会在性能消耗之下造成低效的页面性能,甚至造成加载卡顿问题。

异步渲染和熟悉的节流函数最终目的是一致的,将多次数据变化所引起的响应变化收集后合并成一次页面渲染,从而更合理的利用机器资源,提升性能与用户体验。

先总结一下原理,在Vue中异步渲染实际上是在数据每次变化时,将其所要引起页面变化的部分都放到一个异步API的回调函数里,直到同步代码执行完之后,异步回调开始执行,最终将同步代码里所有的需要渲染变化的部分合并起来,最终执行一次渲染操作。

拿上面例子来说,当val第一次赋值时,页面会渲染出对应的文字,但是实际这个渲染变化会暂存,val第二次赋值时,再次暂存将要引起的变化,这些变化操作会被丢到异步API,Promise.then的回调函数中,等到所有同步代码执行完后,then函数的回调函数得到执行,然后将遍历存储着数据变化的全局数组,将所有数组里数据确定先后优先级,最终合并成一套需要展示到页面上的数据,执行页面渲染操作操作。

异步队列执行后,存储页面变化的全局数组得到遍历执行,执行的时候会进行一些筛查操作,将重复操作过的数据进行处理,实际就是先赋值的丢弃不渲染,最终按照优先级最终组合成一套数据渲染。

这里触发渲染的异步API优先考虑Promise,其次MutationObserver,如果没有MutationObserver的话,会考虑setImmediate,没有setImmediate的话最后考虑是setTimeout。

接下来在源码层面梳理一下的Vue的异步渲染过程。

接下来从源码角度一步一分析一下:

1、当我们使用this.val='343'赋值的时候,val属性所绑定的Object.defineProperty的setter函数触发,setter函数将所订阅的notify函数触发执行。

defineReactive(){...set:functionreactiveSetter(newVal){...dep.notify();...}...}2、notify函数中,将所有的订阅组件watcher中的update方法执行一遍。

Dep.prototype.notify=functionnotify(){//拷贝所有组件的watchervarsubs=this.subs.slice();...for(vari=0,l=subs.length;i

Watcher.prototype.update=functionupdate(){if(this.lazy){this.dirty=true;}elseif(this.sync){this.run();}else{queueWatcher(this);}};4、queueWatcher函数里,会先将组件的watcher存进全局数组变量queue里。

默认情况下config.async是true,直接进入nextTick的函数执行,nextTick是一个浏览器异步API实现的方法,它的回调函数是flushSchedulerQueue函数。

functionqueueWatcher(watcher){...//在全局队列里存储将要响应的变化update函数queue.push(watcher);...//当async配置是false的时候,页面更新是同步的if(!config.async){flushSchedulerQueue();return}//将页面更新函数放进异步API里执行,同步代码执行完开始执行更新页面函数nextTick(flushSchedulerQueue);}5、nextTick函数的执行后,传入的flushSchedulerQueue函数又一次push进callbacks全局数组里,pending在初始情况下是false,这时候将触发timerFunc。

functionnextTick(cb,ctx){var_resolve;callbacks.push(function(){if(cb){try{cb.call(ctx);}catch(e){handleError(e,ctx,'nextTick');}}elseif(_resolve){_resolve(ctx);}});if(!pending){pending=true;timerFunc();}//$flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(function(resolve){_resolve=resolve;})}}6、timerFunc函数是由浏览器的Promise、MutationObserver、setImmediate、setTimeout这些异步API实现的,异步API的回调函数是flushCallbacks函数。

//将nextTick里push进去的flushSchedulerQueue函数进行for循环依次调用functionflushCallbacks(){pending=false;varcopies=callbacks.slice(0);callbacks.length=0;for(vari=0;i

functionflushSchedulerQueue(){varwatcher,id;//安装id从小到大开始排序,越小的越前触发的updatequeue.sort(function(a,b){returna.id-b.id;});//queue是全局数组,它在queueWatcher函数里,每次update触发的时候将当时的watcher,push进去for(index=0;index

Watcher.prototype.run=functionrun(){if(this.active){varvalue=this.get();...}};10、get函数中,将实例watcher对象push到全局数组中,开始调用实例的getter方法,执行完毕后,将watcher对象从全局数组弹出,并且清除已经渲染过的依赖实例。

Watcher.prototype.get=functionget(){pushTarget(this);//将实例push到全局数组targetStackvarvm=this.vm;value=this.getter.call(vm,vm);...}11、实例的getter方法实际是在实例化的时候传入的函数,也就是下面vm的真正更新函数_update。

function(){vm._update(vm._render(),hydrating);};12、实例的_update函数执行后,将会把两次的虚拟节点传入传入vm的patch方法执行渲染操作。

Vue.prototype._update=function(vnode,hydrating){varvm=this;...varprevVnode=vm._vnode;vm._vnode=vnode;if(!prevVnode){//initialrendervm.$el=vm.__patch__(vm.$el,vnode,hydrating,false/*removeOnly*/);}else{//updatesvm.$el=vm.__patch__(prevVnode,vnode);}...};四、nextTick实现原理首先nextTick并不是浏览器本身提供的一个异步API,而是Vue中使用由浏览器本身提供的原生异步API封装而成的一个异步封装方法,上面第5第6段是它的实现源码。它对于浏览器异步API的选用规则如下,Promise存在取由Promise.then,不存在Promise则取MutationObserver,MutationObserver不存在setImmediate,setImmediate不存在最后取setTimeout来实现。

从上面的取用规则也可以看出来,nextTick即有可能是微任务,也有可能是宏任务,从优先去Promise和MutationObserver可以看出nextTick优先微任务,其次是setImmediate和setTimeout宏任务。

对于微任务与宏任务的区别这里不深入,只要记得同步代码执行完毕之后,优先执行微任务,其次才会执行宏任务。

1、Vue.config.async=false

当然是可以的,在第四段源码里,我们能看到如下一段,当config里的async的值为为false的情况下,并没有将flushSchedulerQueue加到nextTick里,而是直接执行了flushSchedulerQueue,就相当于把本次data里的值变化时,页面做了同步渲染。

functionqueueWatcher(watcher){...//在全局队列里存储将要响应的变化update函数queue.push(watcher);...//当async配置是false的时候,页面更新是同步的if(!config.async){flushSchedulerQueue();return}//将页面更新函数放进异步API里执行,同步代码执行完开始执行更新页面函数nextTick(flushSchedulerQueue);}在我们的开发代码里,只需要加入下一句即可让你的页面渲染同步进行。

importVuefrom'Vue'Vue.config.async=false2、this._watcher.sync=true

在Watch的update方法执行源码里,可以看到当this.sync为true时,这时候的渲染也是同步的。

Watcher.prototype.update=functionupdate(){if(this.lazy){this.dirty=true;}elseif(this.sync){this.run();}else{queueWatcher(this);}};在开发代码中,需要将本次watcher的sync属性修改为true,对于watcher的sync属性变化只需要在需要同步渲染的数据变化操作前执行this._watcher.sync=true,这时候则会同步执行页面渲染动作。

像下面的写法中,页面会渲染出val为1,而不会渲染出2,最终渲染的结果是3,但是官网未推荐该用法,请慎用。

newVue({el:'#app',sync:true,template:'

{{val}}
',data(){return{val:0}},mounted(){this._watcher.sync=truethis.val=1debuggerthis._watcher.sync=falsethis.val=2this.val=3}})至此我们了解了Vue中为什么采用异步渲染页面的原因,并且从源码的角度深入剖析了整个渲染前的操作链路,同时剖析出Vue中的异步方法nextTick的实现与原生的异步API直接的联系。最后也从源码角度下了解到,Vue并非不能同步渲染,当我们的页面中需要同步渲染时,做适当的配置即可满足。

异步渲染机制我们其实都知道,也很容易碰到,但是可能并不清楚这个概念及其原理。正好今天看到一个大厂面试题,提到异步渲染机制,好奇是个什么概念,发现其实也不是什么新东西,我们在写代码时也经常遇到,也是个很基础的东西吧,只是不大清楚这个概念,所以找了一些资料看了下。发现这篇开篇讲的挺通俗易懂,中间源码讲的也挺清晰的,所以转载过来共同学习。

THE END
1.古兰姬塔 古兰(十天极皮) 简介 年龄15 身高170cm 声优小野友树 兴趣- 喜好- 不擅长- 国内称呼- 图鉴 和搭档碧一起在偏僻的赞克汀泽岛上生活的少年。想要找到不知所踪的父亲在信中提到的“星之岛伊斯塔鲁西亚”。以和露莉亚命运般的邂逅为契机,踏上了前往广阔天空的旅途。性格善良,乐于助人。精通各式武艺,每天都https://gbf.huijiwiki.com/wiki/%E8%88%B9%E5%91%98%E6%89%8B%E5%86%8C/%E5%8F%A4%E5%85%B0
2.古兰书名字打分古兰书姓名免费测试生辰八字五格分析名字古兰书名字打分五格 外格 11 (木) 1 古 兰 书 1 5 23 10 天格 6 (土) 人格 28 (金) 地格 33 (火) 总格 38(金) 五格指的是姓名学中天格、地格、人格、总格、外格。五格全称为五格剖象法,是目前较有影响的一种取名法。 天格:乃祖先留下来的,其数理对人影响不大。 人格:又称"主运",是整个姓名https://m.mzi8.com/ceshi/xm_%E5%8F%A4%E5%85%B0%E4%B9%A6.html
3.《四谷怪谈阿岩的亡灵》高清电影【志怪奇谭】仲代达矢版【四谷怪谈】才是最经典的(高清修复), 38:07 【Levn的讲故事系列(第六季)】四谷怪谈07.又市献计休阿岩 06:34 【怪谈·四谷怪谈】阿岩果断拒绝了宅悦的帮助 06:59 日本民间古老故事173集-四谷怪谈(萌萌的人物,熟悉的故事) 01:58 四谷怪谈-阿岩 08:48 几分钟看完日http://4g.v.sogou.com/movie/mzuwy3k7hazdknzqhee73iolys44roowzs4p3ivbugykfuoswxcm35wb5e.html
4.古兰个人资料简介及身高古兰身价多少古兰转会记录N. Gulan 全称:尼古拉·古兰 国家:塞尔维亚 球队: 位置:中场 球衣:- 生日:1989-03-23 身高:183cm 体重:73kg 合同到期:1719676800 球员数据球员动态 全部 全部 赛事时间主队比分客队进球点球黄牌红牌上场时间 塞尔甲04-26 22:00拉德尼基诺维贝尔格莱德1-1帕拉辛000046 https://data.qtx.com/qiuyuan/29W8eM37vb.html
5.古兰简介所属图书:《古兰经》 出版日期:2013-01 字数:5031 所属分类: 哲学、宗教>宗教>伊斯兰教(回教)>古兰经(可兰经) 关键词: 古兰经阿拉伯人民族 换肤 字号 小中大 当前显示为试读部分,购买后可阅读全文× 古兰简介(节录) 《古兰经》是阿拉伯语文中首先加以记载的典籍,换言之,在《古兰经》之前,阿拉伯人并没有什么https://www.sklib.cn/booklib/databasedetail?SiteID=122&Type=literature&ID=7252003
6.古兰经翻译APP下载安装2024最新正版手机免费下载7. 简便操作:用户只需点击右上角的下载按钮即可下载每个MP3文件,操作简单方便。 8. 设备内播放:如果用户选择下载文件,可以直接从设备中播放下载的MP3文件。 通过以上功能,《古兰经翻译》APP为用户提供了高品质的古兰经翻译音频文件,同时提供了在线收听和离线下载的选择,方便用户随时随地学习和理解古兰经的含义。 https://www.25pp.com/xiazai/7404525/
7.走进伊斯兰教论读古兰经英语演讲古兰经完整记录了上帝对默罕默德的话语--仅仅是另一本畅销书榜单上的作者。 Yet, the fact that so few people do actually read the Koran is precisely why it's so easy to quote -- that is, to misquote. 然而事实上,只有极少数人真正研读古兰经,这就是为什么古兰经这么容易被引用--或者说,错误地被https://www.kekenet.com/Article/201712/535697.shtml
8.《古兰》中文译文版20230314.doc目录TOC\o"1-3"\h\z第一本古兰经译文 1第一章开端章 1第二章黄牛(巴格勒) 1第二本古兰经译文 10第三本古兰经译文 19第三章伊姆兰的家属 22第四本古兰经译文 28第四章妇女(娓萨) 35第五本古兰经译文 37第六本古兰经译文 46第五章筵席(餐桌,马以代) 48第七本古兰经译文 54第六章牲畜(哀诺安)https://www.renrendoc.com/paper/250340183.html
9.古兰经每日一页282页(海木宰阿訇)伟大的真主说:? 你应当诵读《古兰经》。[:4] 贵圣(愿主福安之)说:“ ”你们以优美的声音装https://kxion.com/article/2581.html
10.《古兰》诠释的历史《古兰》所诠释的历史观,是其全部哲学思想的理论基础,是其宗教思想、人生理想、认识观点、伦理观念、生活方式、法律规范和行为准则的根基、核心和出发点。 《古兰》诠释的历史主要涵盖了三个部分:历史创造,历史发展,历史动力。 独一神的理念就是信仰唯一的主宰安拉,是《古兰》的核心,从而形成了伊斯兰独特的历史观。http://www.duost.com/20080624/16442.html
11.《古兰》中文译文版《古兰》中文范译 译:穆·哈布林拉 中国宣扬伊斯兰 目录 第一本古兰经译文1 第一章 开端章1 第二章 黄牛(巴格勒)1 第二本古兰经译文10 第三本古兰经译文19 第三章 伊姆兰的家属22 第四本古兰经译文28 第四章 妇女(娓萨)34 第五本古兰经译文37 第六本古兰经译文45 第五章 筵席(餐桌,马以代)48 第https://www.mayiwenku.com/p-21184298.html
12.揭秘欧洲首个女“肉弹”:从不读《古兰经》,嗜烟酒男友多全球速报一个26岁、从未读过《古兰经》的“女牛仔”,成为了欧洲第一名自杀式炸弹的袭击者。 在11月18日法国警方在巴黎北郊圣但尼(Saint-Denis)的反恐突袭行动中,被疑为巴黎恐怖袭击主谋的阿卜杜勒-哈米德·阿巴乌德(Abdelhamid Abaaoud)被警方击毙。 据法新社报道,在警方此次围捕行动中,阿巴乌德的表妹哈斯娜·艾特·阿布拉森https://www.thepaper.cn/newsDetail_forward_1399102/
13.撒拉族千年《古兰经》首次向公众展示聚居在青藏高原黄河之滨的撒拉族,是我国人口较少民族。这个民族有一个稀世珍宝,它就是被学界普遍认为有着近千年历史,目前世界现存最古老手抄本《古兰经》之一的撒拉族手抄本《古兰经》。今年春节期间,它在青海循化撒拉族自治县街子镇清真寺的藏经楼首次向公众展示。引起了广大游客的浓厚兴趣。 http://wenhui.whb.cn/zhuzhan/jjl/20190220/243428.html
14.真古兰森游戏视频游戏视频:黑洞炮 缩退炮https://tv.sohu.com/v/cGwvNTY3Mjc4Mi8xODE4MjUxNi5zaHRtbA==.html
15.诵读《古兰经》每章的好处经训要把宗教书籍读好,特别是经典恐更难,穆斯林诵读《古兰经》更非易事,读懂它知道每章的意思就更难,从中受益通晓每章给世人的好处,恐智知者甚少。我给你推荐《嘎最》古兰经注又名《光明的启示》(丁秉全 译),《嘎最》古兰经注是广大中国穆斯林群众及清真寺通用的传统经注教材,是伊玛目们普学、通讲的经典.,每http://www.muslimwww.com/html/2014/jinxun_0219/22209.html
16.glj381413362(古兰经)/Starred·GitHub古兰经glj381413362 Follow 3followers·7following Achievements Achievements Block or Report Stars Search Showing results Molunerfinn /PicGo A simple & beautiful tool for pictures uploading built by vue-cli-electron-builder TypeScript24,3182,245UpdatedDec 11, 2024 https://github.com/glj381413362?direction=desc&sort=updated&tab=stars
17.古兰贡酒古兰贡酒哪款好?看实拍,买好货!京东优评,为您推荐的“古兰贡酒”相关产品的购买用户评价 包装用心(3944) , 高端大气(3195) , 醇厚浓香(968) , 口感醇厚(564) , 好喝不腻(357) , 省心快捷(79) , 细腻爽口(24) , 劲儿十足(16) , 赠品多多(7) . 优评TOP5精选好评商品,看实拍图,放心买好货! https://yp.jd.com/1225956acfe0257c697b4.html
18.古德古兰最新章节古德古兰无弹窗全文阅读> 古德古兰最新章节列表 古德古兰 作者:顾天伦 状态: 连载, 【加入书架】, 【直达底部】 最后更新:2024-04-04 13:07:00 最新章节: 1658:百年(大结局) 阿斯兰再次重生了,重生到五十年前被家族赶到边境种地的日子。他曾经在这里崛起,将王国最为贫瘠的梅洛地区发展成为大陆最富有的商业城市。这一世重来,他不https://www.x81zw.co/book/143/143594/