国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

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

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

前言

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

什么是異步更新隊列

先看看下面的例子:

<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>
  • 上面的例子是,點擊按鈕顯示被隱藏的 div,同時打印 div 內(nèi)部 html 的內(nèi)容。
  • 按照我們一般的認知,應(yīng)該是點擊按鈕能夠顯示 div 并且在控制臺看到 div 的內(nèi)部 html 的內(nèi)容。

但是實際執(zhí)行的結(jié)果確是,div 可以顯示出來,但是打印結(jié)果的時候會報錯,錯誤原因就是 innerHTML 為 null,也就是 div 不存在。
只有當我們再次點擊按鈕的時候才會打印出 div 里面的內(nèi)容。這就是 Vue 的異步更新隊列的結(jié)果

異步更新隊列的概念

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

  • 所以上面的代碼報錯是因為當執(zhí)行 this.isShow=true 時,div 還未被創(chuàng)建出來,知道下次 Vue 事件循環(huán)時才開始創(chuàng)建

  • 查重機制降低了 Vue 的開銷

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

異步更新隊列解析

異步隊列源碼入口

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

  • from src/core/observer/dep.js
//直接看核心代碼notify () {//這是Dep的notify方法,Vue的會對data數(shù)據(jù)進行數(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進行數(shù)據(jù)更新//這里的subs[i]其實是Dep維護的一個watcher數(shù)組,所以我們下面是執(zhí)行的watcher中的update方法subs[i].update()}}
  • 上面的代碼簡單來說就是 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 {//核心:將當前的watcher放到一個隊列中queueWatcher(this)}}
  • 上面 watcher 的 update 更新方法簡單來說就是調(diào)用了一個 queueWatcher 方法,這個方法其實是將當前的 watcher 實例放入到一個隊列中,以便完成后面的異步更新隊列操作

異步隊列入隊

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

export function queueWatcher (watcher: Watcher) {const id = watcher.id//去重的操作,先判斷是否在當前隊列中存在,避免重復操作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}// 啟動異步任務(wù)(刷新當前的計劃任務(wù))nextTick(flushSchedulerQueue)}}}
  • 上面這段 queueWatcher 的代碼的主要作用就是對任務(wù)去重,然后啟動異步任務(wù),進行跟新操作。接下來我們看一線 nextTick 里面的操作

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

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

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

/**這部分邏輯就是根據(jù)環(huán)境來判斷timerFunc到底是使用什么樣的異步隊列**/let timerFunc//首選微任務(wù)執(zhí)行異步操作:Promise、MutationObserver//次選setImmediate最后選擇setTimeout// 根據(jù)當前瀏覽器環(huán)境選擇用什么方法來執(zhí)行異步任務(wù)if (typeof Promise !== 'undefined' && isNative(Promise)) {//如果當前環(huán)境支持Promise,則使用Promise執(zhí)行異步任務(wù)const p = Promise.resolve()timerFunc = () => {//最終是執(zhí)行的flushCallbacks方法p.then(flushCallbacks)//如果是IOS則回退,因為IOS不支持Promiseif (isIOS) setTimeout(noop)}//當前使用微任務(wù)執(zhí)行isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' && (//如果當前瀏覽器支持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方法來完成異步更新隊列timerFunc = () => {setTimeout(flushCallbacks, 0)}}
  • 從上面代碼可以看出,不論 timerFunc 使用的是什么樣的異步更新隊列,最終執(zhí)行的函數(shù)還是落在了 flushCallbacks 上面,那么我們來看一看,這個方法到底是什么

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]()}}
  • 上面的這個方法就是遍歷執(zhí)行了我們 nextTick 維護的那個回調(diào)函數(shù)數(shù)組, 其實就是將數(shù)組的方法依次添加進異步隊列進行執(zhí)行。同時清空 callbacks 數(shù)組為下次更新作準備。

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

異步隊列的具體更新方法

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

from src/core/observer/scheduler.js

/**我們這里只粘貼跟本次異步隊列更新相關(guān)的核心代碼**///具體的更新操作
function flushSchedulerQueue () {currentFlushTimestamp = getNow()flushing = truelet watcher, id//重新排列queue數(shù)組,是為了確保://更新順序是從父組件到子組件//用戶的watcher先于render 的watcher執(zhí)行(因為用戶watcher先于render watcher創(chuàng)建)//當子組件的watcher在父組件的watcher執(zhí)行時被銷毀,則跳過該子組件的watcherqueue.sort((a, b) => a.id - b.id)//queue數(shù)組維護的一個watcher數(shù)組//遍歷queue數(shù)組,在queueWatcher方法中我們將傳入的watcher實例push到了該數(shù)組中for (index = 0; index < queue.length; index++) {watcher = queue[index]if (watcher.before) {watcher.before()}id = watcher.id//清空has對象里面的"id"屬性(這個id屬性之前在queueWatcher方法里面查重的時候用到了)has[id] = null//核心:最終執(zhí)行的其實是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),先跳過const activatedQueue = activatedChildren.slice()const updatedQueue = queue.slice()resetSchedulerState()callActivatedHooks(activatedQueue)callUpdatedHooks(updatedQueue)if (devtools && config.devtools) {devtools.emit('flush')}
}
  • 上面的一堆 flushSchedulerQueue 代碼,簡單來說就是排列了 queue 數(shù)組,然后遍歷該數(shù)組,執(zhí)行 watcher.run 方法。所以,異步隊列更新當我們?nèi)腙犕暌院?#xff0c;真正執(zhí)行的方法其實是 watcher.run 方法

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

from src/core/observer/watcher.js

/*** Scheduler job interface.* Will be called by the scheduler.* 上面這段英文注釋 是官方注釋,從這我們看出該方法最終會被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 方法,該方法我們在之前的源碼分析有講過,主要實現(xiàn)的功能是調(diào)用了 data 數(shù)據(jù)的 get 方法,獲取最新數(shù)據(jù)。

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

關(guān)于 Vue.$nextTick

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

from src/core/instance/render.js

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

注意,$nextTick() 是會將我們傳入的函數(shù)加入到異步更新隊列中的,但是這里有個問題,如果我們想獲得 dom 更新后的數(shù)據(jù),我們應(yīng)該把該邏輯放到更新操作之后
因為加入異步隊列先后的問題,如果我們在更新數(shù)據(jù)之前入隊的話 ,是獲取不到更新之后的數(shù)據(jù)的

總結(jié)

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

http://aloenet.com.cn/news/33445.html

相關(guān)文章:

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