电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

詳解Vue的異步更新實(shí)現(xiàn)原理

瀏覽:24日期:2022-10-17 10:47:08

最近面試總是會(huì)被問到這么一個(gè)問題:在使用vue的時(shí)候,將for循環(huán)中聲明的變量i從1增加到100,然后將i展示到頁面上,頁面上的i是從1跳到100,還是會(huì)怎樣?答案當(dāng)然是只會(huì)顯示100,并不會(huì)有跳轉(zhuǎn)的過程。

怎么可以讓頁面上有從1到100顯示的過程呢,就是用setTimeout或者Promise.then等方法去模擬。

講道理,如果不在vue里,單獨(dú)運(yùn)行這段程序的話,輸出一定是從1到100,但是為什么在vue中就不一樣了呢?

for(let i=1; i<=100; i++){console.log(i);}

這就涉及到Vue底層的異步更新原理,也要說一說nextTick的實(shí)現(xiàn)。不過在說nextTick之前,有必要先介紹一下JS的事件運(yùn)行機(jī)制。

JS運(yùn)行機(jī)制

眾所周知,JS是基于事件循環(huán)的單線程的語言。 執(zhí)行的步驟大致是:

當(dāng)代碼執(zhí)行時(shí),所有同步的任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧; 在主線程之外還有一個(gè)任務(wù)隊(duì)列(task queue),只要異步任務(wù)有了運(yùn)行結(jié)果就在任務(wù)隊(duì)列中放置一個(gè)事件; 一旦執(zhí)行棧中所有同步任務(wù)執(zhí)行完畢(主線程代碼執(zhí)行完畢),此時(shí)主線程不會(huì)空閑而是去讀取任務(wù)隊(duì)列。此時(shí),異步的任務(wù)就結(jié)束等待的狀態(tài)被執(zhí)行。 主線程不斷重復(fù)以上的步驟。

詳解Vue的異步更新實(shí)現(xiàn)原理

我們把主線程執(zhí)行一次的過程叫一個(gè)tick,所以nextTick就是下一個(gè)tick的意思,也就是說用nextTick的場景就是我們想在下一個(gè)tick做一些事的時(shí)候。

所有的異步任務(wù)結(jié)果都是通過任務(wù)隊(duì)列來調(diào)度的。而任務(wù)分為兩類:宏任務(wù)(macro task)和微任務(wù)(micro task)。它們之間的執(zhí)行規(guī)則就是每個(gè)宏任務(wù)結(jié)束后都要將所有微任務(wù)清空。 常見的宏任務(wù)有setTimeout/MessageChannel/postMessage/setImmediate,微任務(wù)有MutationObsever/Promise.then。

想要透徹學(xué)習(xí)事件循環(huán),推薦Jake在JavaScript全球開發(fā)者大會(huì)的演講,保證講懂!

nextTick原理派發(fā)更新

大家都知道vue的響應(yīng)式的靠依賴收集和派發(fā)更新來實(shí)現(xiàn)的。在修改數(shù)組之后的派發(fā)更新過程,會(huì)觸發(fā)setter的邏輯,執(zhí)行dep.notify():

// src/core/observer/watcher.jsclass Dep {notify() { //subs是Watcher的實(shí)例數(shù)組 const subs = this.subs.slice() for(let i=0, l=subs.length; i<l; i++){ subs[i].update() } }}

遍歷subs里每一個(gè)Watcher實(shí)例,然后調(diào)用實(shí)例的update方法,下面我們來看看update是怎么去更新的:

class Watcher {update() { ... //各種情況判斷之后 else{ queueWatcher(this) } }}

update執(zhí)行后又走到了queueWatcher,那就繼續(xù)去看看queueWatcher干啥了(希望不要繼續(xù)套娃了:

//queueWatcher 定義在 src/core/observer/scheduler.jsconst queue: Array<Watcher> = []let has: { [key: number]: ?true } = {}let waiting = falselet flushing = falselet index = 0export function queueWatcher(watcher: Watcher) {const id = watcher.id //根據(jù)id是否重復(fù)做優(yōu)化 if(has[id] == null){ has[id] = true if(!flushing){ queue.push(watcher) }else{ let i=queue.length - 1 while(i > index && queue[i].id > watcher.id){ i-- } queue.splice(i + 1, 0, watcher) } if(!waiting){ waiting = true //flushSchedulerQueue函數(shù): Flush both queues and run the watchers nextTick(flushSchedulerQueue) } }}

這里queue在pushwatcher時(shí)是根據(jù)id和flushing做了一些優(yōu)化的,并不會(huì)每次數(shù)據(jù)改變都觸發(fā)watcher的回調(diào),而是把這些watcher先添加到⼀個(gè)隊(duì)列⾥,然后在nextTick后執(zhí)⾏flushSchedulerQueue。

flushSchedulerQueue函數(shù)是保存更新事件的queue的一些加工,讓更新可以滿足Vue更新的生命周期。

這里也解釋了為什么for循環(huán)不能導(dǎo)致頁面更新,因?yàn)閒or是主線程的代碼,在一開始執(zhí)行數(shù)據(jù)改變就會(huì)將它push到queue里,等到for里的代碼執(zhí)行完畢后i的值已經(jīng)變化為100時(shí),這時(shí)vue才走到nextTick(flushSchedulerQueue)這一步。

nextTick源碼

接著打開vue2.x的源碼,目錄core/util/next-tick.js,代碼量很小,加上注釋才110行,是比較好理解的。

const callbacks = []let pending = falseexport function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, ’nextTick’) } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() }

首先將傳入的回調(diào)函數(shù)cb(上節(jié)的flushSchedulerQueue)壓入callbacks數(shù)組,最后通過timerFunc函數(shù)一次性解決。

let timerFuncif (typeof Promise !== ’undefined’ && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== ’undefined’ && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === ’[object MutationObserverConstructor]’)) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true} else if (typeof setImmediate !== ’undefined’ && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) }} else { timerFunc = () => { setTimeout(flushCallbacks, 0) }}

timerFunc下面一大片if else是在判斷不同的設(shè)備和不同情況下選用哪種特性去實(shí)現(xiàn)異步任務(wù):優(yōu)先檢測是否原生⽀持Promise,不⽀持的話再去檢測是否⽀持MutationObserver,如果都不行就只能嘗試宏任務(wù)實(shí)現(xiàn),首先是setImmediate,這是⼀個(gè)⾼版本 IE 和 Edge 才⽀持的特性,如果都不⽀持的話最后就會(huì)降級(jí)為 setTimeout 0。

這⾥使⽤callbacks⽽不是直接在nextTick中執(zhí)⾏回調(diào)函數(shù)的原因是保證在同⼀個(gè) tick 內(nèi)多次執(zhí)⾏nextTick,不會(huì)開啟多個(gè)異步任務(wù),⽽把這些異步任務(wù)都?jí)撼?#12032;個(gè)同步任務(wù),在下⼀個(gè) tick 執(zhí)⾏完畢。

nextTick使用

nextTick不僅是vue的源碼文件,更是vue的一個(gè)全局API。下面來看看怎么使用吧。

當(dāng)設(shè)置 vm.someData = ’new value’,該組件不會(huì)立即重新渲染。當(dāng)刷新隊(duì)列時(shí),組件會(huì)在下一個(gè)事件循環(huán)tick中更新。多數(shù)情況我們不需要關(guān)心這個(gè)過程,但是如果你想基于更新后的 DOM 狀態(tài)來做點(diǎn)什么,這就可能會(huì)有些棘手。雖然 Vue.js 通常鼓勵(lì)開發(fā)人員使用數(shù)據(jù)驅(qū)動(dòng)的方式思考,避免直接接觸 DOM,但是有時(shí)我們必須要這么做。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM,可以在數(shù)據(jù)變化之后立即使用Vue.nextTick(callback)。這樣回調(diào)函數(shù)將在 DOM 更新完成后被調(diào)用。

官網(wǎng)用例:

<div id='example'>{{message}}</div>

var vm = new Vue({ el: ’#example’, data: { message: ’123’ }})vm.message = ’new message’ // 更改數(shù)據(jù)vm.$el.textContent === ’new message’ // falseVue.nextTick(function () { vm.$el.textContent === ’new message’ // true})

并且因?yàn)?nextTick() 返回一個(gè) Promise 對象,所以也可以使用async/await 語法去處理事件,非常方便。

以上就是詳解Vue的異步更新實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于vue 異步更新的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Vue
相關(guān)文章:
主站蜘蛛池模板: 水压力传感器_数字压力传感器|佛山一众传感仪器有限公司|首页 | 英超直播_英超免费在线高清直播_英超视频在线观看无插件-24直播网 | 合肥弱电工程_安徽安防工程_智能化工程公司-合肥雷润 | 油液红外光谱仪-油液监测系统-燃油嗅探仪-上海冉超光电科技有限公司 | 冷镦机-多工位冷镦机-高速冷镦机厂家-温州金诺机械设备制造有限公司 | 影像测量仪_三坐标测量机_一键式二次元_全自动影像测量仪-广东妙机精密科技股份有限公司 | 齿轮减速电机一体机_蜗轮蜗杆减速马达-德国BOSERL齿轮减速机带电机生产厂家 | 视觉检测设备_自动化检测设备_CCD视觉检测机_外观缺陷检测-瑞智光电 | 合肥仿石砖_合肥pc砖厂家_合肥PC仿石砖_安徽旭坤建材有限公司 | 即用型透析袋,透析袋夹子,药敏纸片,L型涂布棒-上海桥星贸易有限公司 | 加热制冷恒温循环器-加热制冷循环油浴-杭州庚雨仪器有限公司 | 润东方环保空调,冷风机,厂房车间降温设备-20年深圳环保空调生产厂家 | ★塑料拖链__工程拖链__电缆拖链__钢制拖链 - 【上海闵彬】 | 四川实木门_成都实木门 - 蓬溪聚成门业有限公司 | TPM咨询,精益生产管理,5S,6S现场管理培训_华谋咨询公司 | 微波消解仪器_智能微波消解仪报价_高压微波消解仪厂家_那艾 | 磁力反应釜,高压釜,实验室反应釜,高温高压反应釜-威海自控反应釜有限公司 | 成都中天自动化控制技术有限公司 | 丝杆升降机-不锈钢丝杆升降机-非标定制丝杆升降机厂家-山东鑫光减速机有限公司 | 软膜天花_软膜灯箱_首选乐创品牌_一站式天花软膜材料供应商! | 首页-恒温恒湿试验箱_恒温恒湿箱_高低温试验箱_高低温交变湿热试验箱_苏州正合 | 999范文网_优质范文下载写作帮手 | 【德信自动化】点胶机_全自动点胶机_自动点胶机厂家_塑料热压机_自动螺丝机-深圳市德信自动化设备有限公司 | 温室大棚建设|水肥一体化|物联网系统| 【黄页88网】-B2B电子商务平台,b2b平台免费发布信息网 | 防水套管厂家-柔性防水套管-不锈钢|刚性防水套管-天翔管道 | 西子馋火锅鸡加盟-太原市龙城酉鼎餐饮管理有限公司 | 智能楼宇-楼宇自控系统-楼宇智能化-楼宇自动化-三水智能化 | 回收二手冲床_金丰旧冲床回收_协易冲床回收 - 大鑫机械设备 | 一体化污水处理设备_生活污水处理设备_全自动加药装置厂家-明基环保 | 微型气泵-真空-蠕动-水泵-厂家-深圳市品亚科技有限公司 | 镀锌钢格栅_热镀锌格栅板_钢格栅板_热镀锌钢格板-安平县昊泽丝网制品有限公司 | PE拉伸缠绕膜,拉伸缠绕膜厂家,纳米缠绕膜-山东凯祥包装 | 北京遮阳网-防尘盖土网-盖土草坪-迷彩网-防尘网生产厂家-京兴科技 | 粉末冶金注射成型厂家|MIM厂家|粉末冶金齿轮|MIM零件-深圳市新泰兴精密科技 | 筛分机|振动筛分机|气流筛分机|筛分机厂家-新乡市大汉振动机械有限公司 | 礼仪庆典公司,礼仪策划公司,庆典公司,演出公司,演艺公司,年会酒会,生日寿宴,动工仪式,开工仪式,奠基典礼,商务会议,竣工落成,乔迁揭牌,签约启动-东莞市开门红文化传媒有限公司 | 电机保护器-电动机综合保护器-上海硕吉电器有限公司 | 登车桥动力单元-非标液压泵站-非标液压系统-深圳市三好科技有限公司 | 3D全息投影_地面互动投影_360度立体投影_水幕灯光秀 | 广东银虎 蜂窝块状沸石分子筛-吸附脱硫分子筛-萍乡市捷龙环保科技有限公司 | 冷藏车厂家|冷藏车价格|小型冷藏车|散装饲料车厂家|程力专用汽车股份有限公司销售十二分公司 |