論述制作網(wǎng)站的一般過程百度移動(dòng)端關(guān)鍵詞優(yōu)化
??
?🎬?江城開朗的豌豆:個(gè)人主頁
?🔥?個(gè)人專欄?:《 VUE 》?《 javaScript 》
?📝?個(gè)人網(wǎng)站?:《 江城開朗的豌豆🫛 》?
???生活的理想,就是為了理想的生活?!
目錄
??? 專欄簡介
?📘? 文章引言
一、NextTick是什么
為什么要有nexttick
二、使用場景
三、實(shí)現(xiàn)原理
?? 寫在最后
??? 專欄簡介
????????歡迎來到前端入門之旅!這個(gè)專欄是為那些對(duì)Web開發(fā)感興趣、剛剛開始學(xué)習(xí)前端的讀者們打造的。無論你是初學(xué)者還是有一些基礎(chǔ)的開發(fā)者,我們都會(huì)在這里為你提供一個(gè)系統(tǒng)而又親切的學(xué)習(xí)平臺(tái)。我們以問答形式更新,為大家呈現(xiàn)精選的前端知識(shí)點(diǎn)和最佳實(shí)踐。通過深入淺出的解釋概念,并提供實(shí)際案例和練習(xí),讓你逐步建立起一個(gè)扎實(shí)的基礎(chǔ)。無論是HTML、CSS、JavaScript還是最新的前端框架和工具,我們都將為你提供豐富的內(nèi)容和實(shí)用技巧,幫助你更好地理解并運(yùn)用前端開發(fā)中的各種技術(shù)。
????????同時(shí),我們也會(huì)關(guān)注最新的前端趨勢和發(fā)展動(dòng)態(tài)。隨著Web技術(shù)的不斷演進(jìn),前端開發(fā)也在不斷推陳出新。我們會(huì)及時(shí)介紹最新的前端框架、工具和技術(shù),使你能夠站在前沿,與時(shí)俱進(jìn)。通過掌握最新的前端技術(shù),你將能夠在競爭激烈的Web開發(fā)領(lǐng)域中有更大的競爭力。
?📘? 文章引言
一、NextTick是什么
官方對(duì)其的定義
在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個(gè)方法,獲取更新后的 DOM
什么意思呢?
我們可以理解成,Vue
?在更新?DOM
?時(shí)是異步執(zhí)行的。當(dāng)數(shù)據(jù)發(fā)生變化,Vue
將開啟一個(gè)異步更新隊(duì)列,視圖需要等隊(duì)列中所有數(shù)據(jù)變化完成之后,再統(tǒng)一進(jìn)行更新
舉例一下
Html
結(jié)構(gòu)
<div id="app"> {{ message }} </div>
構(gòu)建一個(gè)vue
實(shí)例
const vm = new Vue({el: '#app',data: {message: '原始值'}
})
修改message
this.message = '修改后的值1'
this.message = '修改后的值2'
this.message = '修改后的值3'
這時(shí)候想獲取頁面最新的DOM
節(jié)點(diǎn),卻發(fā)現(xiàn)獲取到的是舊值
console.log(vm.$el.textContent) // 原始值
這是因?yàn)?code>message數(shù)據(jù)在發(fā)現(xiàn)變化的時(shí)候,vue
并不會(huì)立刻去更新Dom
,而是將修改數(shù)據(jù)的操作放在了一個(gè)異步操作隊(duì)列中
如果我們一直修改相同數(shù)據(jù),異步操作隊(duì)列還會(huì)進(jìn)行去重
等待同一事件循環(huán)中的所有數(shù)據(jù)變化完成之后,會(huì)將隊(duì)列中的事件拿來進(jìn)行處理,進(jìn)行DOM
的更新
為什么要有nexttick
舉個(gè)例子
{{num}} for(let i=0; i<100000; i++){num = i }
如果沒有?nextTick
?更新機(jī)制,那么?num
?每次更新值都會(huì)觸發(fā)視圖更新(上面這段代碼也就是會(huì)更新10萬次視圖),有了nextTick
機(jī)制,只需要更新一次,所以nextTick
本質(zhì)是一種優(yōu)化策略
二、使用場景
如果想要在修改數(shù)據(jù)后立刻得到更新后的DOM
結(jié)構(gòu),可以使用Vue.nextTick()
第一個(gè)參數(shù)為:回調(diào)函數(shù)(可以獲取最近的DOM
結(jié)構(gòu))
第二個(gè)參數(shù)為:執(zhí)行函數(shù)上下文
// 修改數(shù)據(jù)
vm.message = '修改后的值'
// DOM 還沒有更新
console.log(vm.$el.textContent) // 原始的值
Vue.nextTick(function () {// DOM 更新了console.log(vm.$el.textContent) // 修改后的值
})
組件內(nèi)使用?vm.$nextTick()
?實(shí)例方法只需要通過this.$nextTick()
,并且回調(diào)函數(shù)中的?this
?將自動(dòng)綁定到當(dāng)前的?Vue
?實(shí)例上
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
this.$nextTick(function () {console.log(this.$el.textContent) // => '修改后的值'
})
$nextTick()
?會(huì)返回一個(gè)?Promise
?對(duì)象,可以是用async/await
完成相同作用的事情
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
await this.$nextTick()
console.log(this.$el.textContent) // => '修改后的值'
三、實(shí)現(xiàn)原理
源碼位置:/src/core/util/next-tick.js
callbacks
也就是異步操作隊(duì)列
callbacks
新增回調(diào)函數(shù)后又執(zhí)行了timerFunc
函數(shù),pending
是用來標(biāo)識(shí)同一個(gè)時(shí)間只能執(zhí)行一次
export function nextTick(cb?: Function, ctx?: Object) {let _resolve;// cb 回調(diào)函數(shù)會(huì)經(jīng)統(tǒng)一處理壓入 callbacks 數(shù)組callbacks.push(() => {if (cb) {// 給 cb 回調(diào)函數(shù)執(zhí)行加上了 try-catch 錯(cuò)誤處理try {cb.call(ctx);} catch (e) {handleError(e, ctx, 'nextTick');}} else if (_resolve) {_resolve(ctx);}});// 執(zhí)行異步延遲函數(shù) timerFuncif (!pending) {pending = true;timerFunc();}// 當(dāng) nextTick 沒有傳入函數(shù)參數(shù)的時(shí)候,返回一個(gè) Promise 化的調(diào)用if (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve;});}
}
timerFunc
函數(shù)定義,這里是根據(jù)當(dāng)前環(huán)境支持什么方法則確定調(diào)用哪個(gè),分別有:
Promise.then
、MutationObserver
、setImmediate
、setTimeout
通過上面任意一種方法,進(jìn)行降級(jí)操作
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {//判斷1:是否原生支持Promiseconst p = Promise.resolve()timerFunc = () => {p.then(flushCallbacks)if (isIOS) setTimeout(noop)}isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {//判斷2:是否原生支持MutationObserverlet 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)) {//判斷3:是否原生支持setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}
} else {//判斷4:上面都不行,直接用setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}
}
無論是微任務(wù)還是宏任務(wù),都會(huì)放到flushCallbacks
使用
這里將callbacks
里面的函數(shù)復(fù)制一份,同時(shí)callbacks
置空
依次執(zhí)行callbacks
里面的函數(shù)
function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}
小結(jié):
- 把回調(diào)函數(shù)放入callbacks等待執(zhí)行
- 將執(zhí)行函數(shù)放到微任務(wù)或者宏任務(wù)中
- 事件循環(huán)到了微任務(wù)或者宏任務(wù),執(zhí)行函數(shù)依次執(zhí)行callbacks中的回調(diào)
?? 寫在最后
請(qǐng)大家不吝賜教,在下方評(píng)論或者私信我,十分感謝🙏🙏🙏.
? 認(rèn)為我某個(gè)部分的設(shè)計(jì)過于繁瑣,有更加簡單或者更高逼格的封裝方式
? 認(rèn)為我部分代碼過于老舊,可以提供新的API或最新語法
? 對(duì)于文章中部分內(nèi)容不理解
? 解答我文章中一些疑問
? 認(rèn)為某些交互,功能需要優(yōu)化,發(fā)現(xiàn)BUG
? 想要添加新功能,對(duì)于整體的設(shè)計(jì),外觀有更好的建議
最后感謝各位的耐心觀看,既然都到這了,點(diǎn)個(gè) 👍贊再走吧!