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