中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

dw網(wǎng)頁(yè)設(shè)計(jì)期末作業(yè)seo的主要分析工具

dw網(wǎng)頁(yè)設(shè)計(jì)期末作業(yè),seo的主要分析工具,南昌網(wǎng)站建設(shè)價(jià)位,網(wǎng)站搭建平臺(tái)有哪些前言 這篇文章分析了 Vue 更新過(guò)程中使用的異步更新隊(duì)列的相關(guān)代碼。通過(guò)對(duì)異步更新隊(duì)列的研究和學(xué)習(xí)&#xff0c;加深對(duì) Vue 更新機(jī)制的理解 什么是異步更新隊(duì)列 先看看下面的例子&#xff1a; <div id"app"><div id"div" v-if"isShow&…

前言

這篇文章分析了 Vue 更新過(guò)程中使用的異步更新隊(duì)列的相關(guān)代碼。通過(guò)對(duì)異步更新隊(duì)列的研究和學(xué)習(xí),加深對(duì) Vue 更新機(jī)制的理解

什么是異步更新隊(duì)列

先看看下面的例子:

<div id="app"><div id="div" v-if="isShow">被隱藏的內(nèi)容</div><input @click="getDiv" value="按鈕" type="button"></div><script>let vm = new Vue({el: '#app',data: {//控制是否顯示#divisShow: false},methods:{getDiv: function () {this.isShow=truevar content = document.getElementById('div').innerHTML;console.log('content',content)}}})
</script>
  • 上面的例子是,點(diǎn)擊按鈕顯示被隱藏的 div,同時(shí)打印 div 內(nèi)部 html 的內(nèi)容。
  • 按照我們一般的認(rèn)知,應(yīng)該是點(diǎn)擊按鈕能夠顯示 div 并且在控制臺(tái)看到 div 的內(nèi)部 html 的內(nèi)容。

但是實(shí)際執(zhí)行的結(jié)果確是,div 可以顯示出來(lái),但是打印結(jié)果的時(shí)候會(huì)報(bào)錯(cuò),錯(cuò)誤原因就是 innerHTML 為 null,也就是 div 不存在。
只有當(dāng)我們?cè)俅吸c(diǎn)擊按鈕的時(shí)候才會(huì)打印出 div 里面的內(nèi)容。這就是 Vue 的異步更新隊(duì)列的結(jié)果

異步更新隊(duì)列的概念

Vue 的 dom 更新是異步的,當(dāng)數(shù)據(jù)發(fā)生變化時(shí) Vue 不是立刻去更新 dom,而是開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一個(gè)事件中循環(huán)發(fā)生的所有數(shù)據(jù)變化。
在緩沖時(shí),會(huì)去除重復(fù)的數(shù)據(jù),避免多余的計(jì)算和 dom 操作。在下一個(gè)事件循環(huán) tick 中,刷新隊(duì)列并執(zhí)行已去重的工作。

  • 所以上面的代碼報(bào)錯(cuò)是因?yàn)楫?dāng)執(zhí)行 this.isShow=true 時(shí),div 還未被創(chuàng)建出來(lái),知道下次 Vue 事件循環(huán)時(shí)才開(kāi)始創(chuàng)建

  • 查重機(jī)制降低了 Vue 的開(kāi)銷(xiāo)

  • 異步更新隊(duì)列實(shí)現(xiàn)的選擇:由于瀏覽器的差異,Vue 會(huì)根據(jù)當(dāng)前環(huán)境選擇 Promise.then 或者 MuMutationObserver,如果兩者都不支持,則會(huì)用 setImmediate 或者 setTimeout 代替

異步更新隊(duì)列解析

異步隊(duì)列源碼入口

通過(guò)之前對(duì) Vue 數(shù)據(jù)響應(yīng)式的分析我們知道,當(dāng) Vue 數(shù)據(jù)發(fā)生變化時(shí),會(huì)觸發(fā) dep 的 notify() 方法,該方法通知觀察者 watcher 去更新 dom,我們先看一下這的源碼

  • from src/core/observer/dep.js
//直接看核心代碼notify () {//這是Dep的notify方法,Vue的會(huì)對(duì)data數(shù)據(jù)進(jìn)行數(shù)據(jù)劫持,該方法被放到data數(shù)據(jù)的set方法中最后執(zhí)行//也就是通知更新操作// stabilize the subscriber list firstconst subs = this.subs.slice()if (process.env.NODE_ENV !== 'production' && !config.async) {subs.sort((a, b) => a.id - b.id)}for (let i = 0, l = subs.length; i < l; i++) {// !!!核心:通知watcher進(jìn)行數(shù)據(jù)更新//這里的subs[i]其實(shí)是Dep維護(hù)的一個(gè)watcher數(shù)組,所以我們下面是執(zhí)行的watcher中的update方法subs[i].update()}}
  • 上面的代碼簡(jiǎn)單來(lái)說(shuō)就是 dep 通知 watcher 盡心更新操作,我們看一下 watcher 相關(guān)的代碼
    from :src/core/observer/watcher.js
//這里只展示部分核心代碼//watcher的update方法update () {/* istanbul ignore else *///判斷是否存在lazy和sync屬性if (this.lazy) {this.dirty = true} else if (this.sync) {this.run()} else {//核心:將當(dāng)前的watcher放到一個(gè)隊(duì)列中queueWatcher(this)}}
  • 上面 watcher 的 update 更新方法簡(jiǎn)單來(lái)說(shuō)就是調(diào)用了一個(gè) queueWatcher 方法,這個(gè)方法其實(shí)是將當(dāng)前的 watcher 實(shí)例放入到一個(gè)隊(duì)列中,以便完成后面的異步更新隊(duì)列操作

異步隊(duì)列入隊(duì)

下面看看 queueWatcher 的邏輯 from src/core/observer/scheduler.js

export function queueWatcher (watcher: Watcher) {const id = watcher.id//去重的操作,先判斷是否在當(dāng)前隊(duì)列中存在,避免重復(fù)操作if (has[id] == null) {has[id] = trueif (!flushing) {queue.push(watcher)} else {// if already flushing, splice the watcher based on its id// if already past its id, it will be run next immediately.let i = queue.length - 1while (i > index && queue[i].id > watcher.id) {i--}queue.splice(i + 1, 0, watcher)}// queue the flushif (!waiting) {waiting = trueif (process.env.NODE_ENV !== 'production' && !config.async) {flushSchedulerQueue()return}// 啟動(dòng)異步任務(wù)(刷新當(dāng)前的計(jì)劃任務(wù))nextTick(flushSchedulerQueue)}}}
  • 上面這段 queueWatcher 的代碼的主要作用就是對(duì)任務(wù)去重,然后啟動(dòng)異步任務(wù),進(jìn)行跟新操作。接下來(lái)我們看一線 nextTick 里面的操作

from src/core/util/next-tick.js

//cb:
export function nextTick (cb?: Function, ctx?: Object) {let _resolve//callbacks:這個(gè)方法維護(hù)了一個(gè)回調(diào)函數(shù)的數(shù)組,將回調(diào)函數(shù)添家進(jìn)數(shù)組callbacks.push(() => {//添加錯(cuò)誤處理if (cb) {try {cb.call(ctx)} catch (e) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {pending = true//啟動(dòng)異步函數(shù)timerFunc()}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}
  • 這里的核心,其實(shí)就在 timerFunc 的函數(shù)上,該函數(shù)根據(jù)不同的運(yùn)行時(shí)環(huán)境,調(diào)用不同的異步更新隊(duì)列,下面看一下代碼

from src/core/util/next-tick.js

/**這部分邏輯就是根據(jù)環(huán)境來(lái)判斷timerFunc到底是使用什么樣的異步隊(duì)列**/let timerFunc//首選微任務(wù)執(zhí)行異步操作:Promise、MutationObserver//次選setImmediate最后選擇setTimeout// 根據(jù)當(dāng)前瀏覽器環(huán)境選擇用什么方法來(lái)執(zhí)行異步任務(wù)if (typeof Promise !== 'undefined' && isNative(Promise)) {//如果當(dāng)前環(huán)境支持Promise,則使用Promise執(zhí)行異步任務(wù)const p = Promise.resolve()timerFunc = () => {//最終是執(zhí)行的flushCallbacks方法p.then(flushCallbacks)//如果是IOS則回退,因?yàn)镮OS不支持Promiseif (isIOS) setTimeout(noop)}//當(dāng)前使用微任務(wù)執(zhí)行isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' && (//如果當(dāng)前瀏覽器支持MutationObserver則使用MutationObserverisNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]')) {let counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {//如果支持setImmediate,則使用setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}} else {//如果上面的條件都不滿足,那么最后選擇setTimeout方法來(lái)完成異步更新隊(duì)列timerFunc = () => {setTimeout(flushCallbacks, 0)}}
  • 從上面代碼可以看出,不論 timerFunc 使用的是什么樣的異步更新隊(duì)列,最終執(zhí)行的函數(shù)還是落在了 flushCallbacks 上面,那么我們來(lái)看一看,這個(gè)方法到底是什么

from src/core/util/next-tick.js

function flushCallbacks () {pending = false//拷貝callbacks數(shù)組內(nèi)容const copies = callbacks.slice(0)//清空callbackscallbacks.length = 0//遍歷執(zhí)行for (let i = 0; i < copies.length; i++) {//執(zhí)行回調(diào)方法copies[i]()}}
  • 上面的這個(gè)方法就是遍歷執(zhí)行了我們 nextTick 維護(hù)的那個(gè)回調(diào)函數(shù)數(shù)組, 其實(shí)就是將數(shù)組的方法依次添加進(jìn)異步隊(duì)列進(jìn)行執(zhí)行。同時(shí)清空 callbacks 數(shù)組為下次更新作準(zhǔn)備。

上面這幾段代碼其實(shí)都是 watcher 的異步隊(duì)列更新中的入隊(duì)操作,通過(guò) queueWatcher 方法中調(diào)用的 nextTick(flushSchedulerQueue), 我們知道,其實(shí)是將 flushSchedulerQueue 這個(gè)方法入隊(duì)

異步隊(duì)列的具體更新方法

所以下面我們看一下 flushSchedulerQueue 這個(gè)方法到底執(zhí)行了什么操作

from src/core/observer/scheduler.js

/**我們這里只粘貼跟本次異步隊(duì)列更新相關(guān)的核心代碼**///具體的更新操作
function flushSchedulerQueue () {currentFlushTimestamp = getNow()flushing = truelet watcher, id//重新排列queue數(shù)組,是為了確保://更新順序是從父組件到子組件//用戶的watcher先于render 的watcher執(zhí)行(因?yàn)橛脩魒atcher先于render watcher創(chuàng)建)//當(dāng)子組件的watcher在父組件的watcher執(zhí)行時(shí)被銷(xiāo)毀,則跳過(guò)該子組件的watcherqueue.sort((a, b) => a.id - b.id)//queue數(shù)組維護(hù)的一個(gè)watcher數(shù)組//遍歷queue數(shù)組,在queueWatcher方法中我們將傳入的watcher實(shí)例push到了該數(shù)組中for (index = 0; index < queue.length; index++) {watcher = queue[index]if (watcher.before) {watcher.before()}id = watcher.id//清空has對(duì)象里面的"id"屬性(這個(gè)id屬性之前在queueWatcher方法里面查重的時(shí)候用到了)has[id] = null//核心:最終執(zhí)行的其實(shí)是watcher的run方法watcher.run()//下面是一些警告提示,可以先忽略if (process.env.NODE_ENV !== 'production' && has[id] != null) {circular[id] = (circular[id] || 0) + 1if (circular[id] > MAX_UPDATE_COUNT) {warn('You may have an infinite update loop ' + (watcher.user? `in watcher with expression "${watcher.expression}"`: `in a component render function.`),watcher.vm)break}}}//調(diào)用組件updated生命周期鉤子相關(guān),先跳過(guò)const activatedQueue = activatedChildren.slice()const updatedQueue = queue.slice()resetSchedulerState()callActivatedHooks(activatedQueue)callUpdatedHooks(updatedQueue)if (devtools && config.devtools) {devtools.emit('flush')}
}
  • 上面的一堆 flushSchedulerQueue 代碼,簡(jiǎn)單來(lái)說(shuō)就是排列了 queue 數(shù)組,然后遍歷該數(shù)組,執(zhí)行 watcher.run 方法。所以,異步隊(duì)列更新當(dāng)我們?nèi)腙?duì)完以后,真正執(zhí)行的方法其實(shí)是 watcher.run 方法

下面我們來(lái)繼續(xù)看一下 watcher.run 方法,到底執(zhí)行了什么操作

from src/core/observer/watcher.js

/*** Scheduler job interface.* Will be called by the scheduler.* 上面這段英文注釋 是官方注釋,從這我們看出該方法最終會(huì)被scheduler調(diào)用*/run () {if (this.active) {//這里調(diào)用了watcher的get方法const value = this.get()if (value !== this.value ||// Deep watchers and watchers on Object/Arrays should fire even// when the value is the same, because the value may// have mutated.isObject(value) ||this.deep) {// set new valueconst oldValue = this.valuethis.value = valueif (this.user) {try {this.cb.call(this.vm, value, oldValue)} catch (e) {handleError(e, this.vm, `callback for watcher "${this.expression}"`)}} else {this.cb.call(this.vm, value, oldValue)}}}}
  • 上述 run 方法最終要的操作就是調(diào)用了 watcher 的 get 方法,該方法我們?cè)谥暗脑创a分析有講過(guò),主要實(shí)現(xiàn)的功能是調(diào)用了 data 數(shù)據(jù)的 get 方法,獲取最新數(shù)據(jù)。

至此,Vue 異步更新隊(duì)列的核心代碼我們就分析完了,為了便于理清思路,我們來(lái)一張圖總結(jié)一下

關(guān)于 Vue.$nextTick

我們都知道 . n e x t T i c k 方 法 , 其 實(shí) 這 個(gè) ? ? .nextTick 方法,其實(shí)這個(gè) ** .nextTick 方法,其實(shí)這個(gè)??nextTick** 方法就是直接調(diào)用的上面的 nextTick 方法

from src/core/instance/render.js

Vue.prototype.$nextTick = function (fn: Function) {return nextTick(fn, this)}
  • 由上面的代碼我們可以看出,$nextTick 是將我們傳入的回調(diào)函數(shù)加入到了異步更新隊(duì)列,所以它才能實(shí)現(xiàn) dom 更新后回調(diào)

注意,$nextTick() 是會(huì)將我們傳入的函數(shù)加入到異步更新隊(duì)列中的,但是這里有個(gè)問(wèn)題,如果我們想獲得 dom 更新后的數(shù)據(jù),我們應(yīng)該把該邏輯放到更新操作之后
因?yàn)榧尤氘惒疥?duì)列先后的問(wèn)題,如果我們?cè)诟聰?shù)據(jù)之前入隊(duì)的話 ,是獲取不到更新之后的數(shù)據(jù)的

總結(jié)

總結(jié)起來(lái)就是,當(dāng)觸發(fā)數(shù)據(jù)更新通知時(shí),dep 通知 watcher 進(jìn)行數(shù)據(jù)更新,這時(shí) watcher 會(huì)將自己加入到一個(gè)異步的更新隊(duì)列中。然后更新隊(duì)列會(huì)將傳入的更新操作進(jìn)行批量處理。
這樣就達(dá)到了多次更新同時(shí)完成,提升了用戶體驗(yàn),減少了瀏覽器的開(kāi)銷(xiāo),增強(qiáng)了性能。

http://www.risenshineclean.com/news/33445.html

相關(guān)文章:

  • 公司網(wǎng)站設(shè)計(jì)方案網(wǎng)站查詢是否安全
  • 天津做網(wǎng)站哪個(gè)公司好seo排名優(yōu)化工具推薦
  • 做游戲開(kāi)發(fā)需要學(xué)哪些技術(shù)優(yōu)化大師下載安裝app
  • 網(wǎng)站建設(shè) 長(zhǎng)沙百度推廣怎么提高關(guān)鍵詞排名
  • 勝芳網(wǎng)站建設(shè)qiansi全國(guó)疫情最新情況公布
  • 前端怎么做電商網(wǎng)站網(wǎng)絡(luò)營(yíng)銷(xiāo)和網(wǎng)絡(luò)銷(xiāo)售的關(guān)系
  • 做論壇網(wǎng)站需要哪些前置審批申請(qǐng)一個(gè)網(wǎng)站需要多少錢(qián)
  • 網(wǎng)站域名備案需要資料網(wǎng)站內(nèi)鏈優(yōu)化
  • 專業(yè)的營(yíng)銷(xiāo)型網(wǎng)站最新報(bào)價(jià)網(wǎng)絡(luò)推廣策劃
  • 怎么給網(wǎng)站做aapseo優(yōu)化專家
  • 廈網(wǎng)站建設(shè)培訓(xùn)學(xué)校創(chuàng)建網(wǎng)站要錢(qián)嗎
  • 做外貿(mào)網(wǎng)站需要什么卡西安網(wǎng)站快速排名提升
  • 外貿(mào)需要網(wǎng)站做生產(chǎn)車(chē)間展示友聯(lián)互換
  • 網(wǎng)站規(guī)劃與設(shè)計(jì)范文seo招聘網(wǎng)
  • 什么樣建網(wǎng)站百度的seo排名怎么刷
  • 建站之星怎么用國(guó)外網(wǎng)站搭建
  • 網(wǎng)站 css江門(mén)百度seo公司
  • 東莞市建設(shè)網(wǎng)站首頁(yè)百度怎樣發(fā)布作品
  • app開(kāi)發(fā)及后期維護(hù)費(fèi)用重慶企業(yè)站seo
  • 陜西省建設(shè)廳網(wǎng)站三類(lèi)b證磁力蜘蛛
  • 怎么做網(wǎng)絡(luò)推廣營(yíng)銷(xiāo)seo專員的工作內(nèi)容
  • 商務(wù)網(wǎng)站建設(shè)實(shí)訓(xùn)報(bào)告網(wǎng)絡(luò)銷(xiāo)售面試問(wèn)題有哪些
  • 男人互做網(wǎng)站關(guān)鍵詞排名靠前
  • 金華建設(shè)學(xué)校繼續(xù)教育網(wǎng)站廣東培訓(xùn)seo
  • 企業(yè)模擬網(wǎng)站建設(shè)本地推廣平臺(tái)有哪些
  • 大型電子商務(wù)網(wǎng)站開(kāi)發(fā)架構(gòu)技能培訓(xùn)學(xué)校
  • python開(kāi)發(fā)手機(jī)網(wǎng)站開(kāi)發(fā)湖南網(wǎng)站營(yíng)銷(xiāo)推廣
  • 蘭州網(wǎng)站建設(shè)招聘b2b電子商務(wù)網(wǎng)站
  • 怎么做租房網(wǎng)站營(yíng)銷(xiāo)失敗案例分析
  • 定制開(kāi)發(fā)電商網(wǎng)站建設(shè)百度seo和sem的區(qū)別