這篇文章跟大家分享一下Vue純乾貨,介紹一下你不知道的Vue.nextTick,希望對大家有幫助!
用過Vue的朋友多多少少都知道
$nextTick
~ 在正式講解nextTick之前,我想你應該清楚知道Vue在更新DOM 時是異步
執行的,因為接下來講解過程會結合元件更新一起講~ 事不宜遲,我們直轉主題吧(本文以v2.6.14版本的Vue原始碼進行講解)【相關推薦:vuejs影片教學】
#一、nextTick小測驗
你真的了解nextTick嗎?來,直接上題~
<template> <div id="app"> <p ref="name">{{ name }}</p> <button @click="handleClick">修改name</button> </div> </template> <script> export default { name: 'App', data () { return { name: '井柏然' } }, mounted() { console.log('mounted', this.$refs.name.innerText) }, methods: { handleClick () { this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText)) this.name = 'jngboran' console.log('sync log', this.$refs.name.innerText) this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText)) } } } </script>
請問上述程式碼中,當點選按鈕「修改name」時,'nextTick1'
,'sync log'
,'nextTick2'
對應的this.$refs.name.innerText
分別會輸出什麼? 注意,這裡印出來的是DOM的innerText~(文章結尾處會貼出答案)
如果此時的你有非常堅定的答案,那你可以不用繼續往下看了~但如果你對自己的答案有所顧慮,那不如跟著我,接著往下看。相信你看完,不需要看到答案都能有個肯定的答案了~!
二、nextTick原始碼實作
#源碼位於core/util/next-tick。可以將其分為4個部分來看,直接上程式碼
1. 全域變數
callbacks
佇列、pending
狀態
const callbacks = [] // 存放cb的队列 let pending = false // 是否马上遍历队列,执行cb的标志
2. flushCallbacks
#遍歷callbacks執行每個cb
function flushCallbacks () { pending = false // 注意这里,一旦执行,pending马上被重置为false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() // 执行每个cb } }
3. nextTick
的非同步實作
根據執行環境的支援程度採用不同的非同步實作策略
let timerFunc // nextTick异步实现fn if (typeof Promise !== 'undefined' && isNative(Promise)) { // Promise方案 const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) // 将flushCallbacks包装进Promise.then中 } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // MutationObserver方案 let counter = 1 const observer = new MutationObserver(flushCallbacks) // 将flushCallbacks作为观测变化的cb const textNode = document.createTextNode(String(counter)) // 创建文本节点 // 观测文本节点变化 observer.observe(textNode, { characterData: true }) // timerFunc改变文本节点的data,以触发观测的回调flushCallbacks timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // setImmediate方案 timerFunc = () => { setImmediate(flushCallbacks) } } else { // 最终降级方案setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0) } }
- 這裡用個真實案例加深對
MutationObserver
的理解。畢竟比起其他三種非同步方案,這個應該是大家最陌生的const observer = new MutationObserver(() => console.log('观测到文本节点变化')) const textNode = document.createTextNode(String(1)) observer.observe(textNode, { characterData: true }) console.log('script start') setTimeout(() => console.log('timeout1')) textNode.data = String(2) // 这里对文本节点进行值的修改 console.log('script end')
- 知道對應的輸出會是怎麼樣的嗎?
script start
、script end
會在第一輪巨集任務中執行,這點沒問題setTimeout
會被放入下一輪巨集任務執行#MutationObserver
是微任務,所以會在本輪巨集任務後執行,所以先於setTimeout
- 結果如下圖:
4. nextTick
方法實作
cb
、Promise
方式
export function nextTick (cb?: Function, ctx?: Object) { let _resolve // 往全局的callbacks队列中添加cb callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { // 这里是支持Promise的写法 _resolve(ctx) } }) if (!pending) { pending = true // 执行timerFunc,在下一个Tick中执行callbacks中的所有cb timerFunc() } // 对Promise的实现,这也是我们使用时可以写成nextTick.then的原因 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
- 深入細節,理解
pending
有什麼用,如何運作?
案例1,同一輪Tick中執行2次$nextTick
,timerFunc
只會執行一次
this.$nextTick(() => console.log('nextTick1')) this.$nextTick(() => console.log('nextTick2'))
- 用圖看看比較直覺?
三、Vue元件的非同步更新
這裡如果有對Vue元件化、派發更新不是十分了解的朋友,可以先戳這裡,看圖解Vue響應式原理了解下Vue組件化和派發更新的相關內容再回來看噢~
Vue的非同步更新DOM其實也是使用nextTick
來實現的,跟我們平時使用的$nextTick其實是同一個~
這裡我們回顧一下,當我們改變一個屬性值的時候會發生什麼事?
根據上圖派發更新過程,我們從watcher.update開時講起,以渲染Watcher為例,進入到queueWatcher
裡
1. queueWatcher
做了什麼?
// 用来存放Wathcer的队列。注意,不要跟nextTick的callbacks搞混了,都是队列,但用处不同~ const queue: Array<Watcher> = [] function queueWatcher (watcher: Watcher) { const id = watcher.id // 拿到Wathcer的id,这个id每个watcher都有且全局唯一 if (has[id] == null) { // 避免添加重复wathcer,这也是异步渲染的优化做法 has[id] = true if (!flushing) { queue.push(watcher) } if (!waiting) { waiting = true // 这里把flushSchedulerQueue推进nextTick的callbacks队列中 nextTick(flushSchedulerQueue) } } }
2. flushSchedulerQueue
做了什麼?
function flushSchedulerQueue () { currentFlushTimestamp = getNow() flushing = true let watcher, id // 排序保证先父后子执行更新,保证userWatcher在渲染Watcher前 queue.sort((a, b) => a.id - b.id) // 遍历所有的需要派发更新的Watcher执行更新 for (index = 0; index < queue.length; index++) { watcher = queue[index] id = watcher.id has[id] = null // 真正执行派发更新,render -> update -> patch watcher.run() } }
- 最後,一張圖搞懂元件的非同步更新過程
#、回歸題目本身
相信經過上文對nextTick源碼的剖析,我們已經揭開它神秘的面紗了。這時的你一定可以堅定地把答案說出來了~話不多說,我們一起核實下,看看是不是如你所想!
1、如图所示,mounted
时候的innerText是“井柏然”的中文
2、接下来是点击按钮后,打印结果如图所示
-
没错,输出结果如下(意不意外?惊不惊喜?)
sync log 井柏然
nextTick1 井柏然
nextTick2 jngboran
-
下面简单分析一下每个输出:
this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText)) this.name = 'jngboran' console.log('sync log', this.$refs.name.innerText) this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText))
sync log
:这个同步打印没什么好说了,相信大部分童鞋的疑问点都不在这里。如果不清楚的童鞋可以先回顾一下EventLoop,这里不多赘述了~nextTick1
:注意其虽然是放在$nextTick
的回调中,在下一个tick执行,但是他的位置是在this.name = 'jngboran'
的前。也就是说,他的cb会比App组件的派发更新(flushSchedulerQueue
)更先进入队列,当nextTick1
打印时,App组件还未派发更新,所以拿到的还是旧的DOM值。nextTick2
就不展开了,大家可以自行分析一下。相信大家对它应该是最肯定的,我们平时不就是这样拿到更新后的DOM吗?
最后来一张图加深理解
写在最后,nextTick其实在Vue中也算是比较核心的一个东西了。因为贯穿整个Vue应用的组件化、响应式的派发更新与其息息相关~深入理解nextTick的背后实现原理,不仅能让你在面试的时候一展风采,更能让你在日常开发工作中,少走弯路少踩坑!好了,本文到这里就暂告一段落了,如果读完能让你有所收获,就帮忙点个赞吧~画图不易、创作艰辛鸭~
以上是乾貨分享,帶你了解Vue中的Vue.nextTick的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Vue.js在Web開發中的角色是作為一個漸進式JavaScript框架,簡化開發過程並提高效率。 1)它通過響應式數據綁定和組件化開發,使開發者能專注於業務邏輯。 2)Vue.js的工作原理依賴於響應式系統和虛擬DOM,優化性能。 3)實際項目中,使用Vuex管理全局狀態和優化數據響應性是常見實踐。

Vue.js是由尤雨溪在2014年發布的漸進式JavaScript框架,用於構建用戶界面。它的核心優勢包括:1.響應式數據綁定,數據變化自動更新視圖;2.組件化開發,UI可拆分為獨立、可複用的組件。

Netflix使用React作為其前端框架。 1)React的組件化開發模式和強大生態系統是Netflix選擇它的主要原因。 2)通過組件化,Netflix將復雜界面拆分成可管理的小塊,如視頻播放器、推薦列表和用戶評論。 3)React的虛擬DOM和組件生命週期優化了渲染效率和用戶交互管理。

Netflix在前端技術上的選擇主要集中在性能優化、可擴展性和用戶體驗三個方面。 1.性能優化:Netflix選擇React作為主要框架,並開發了SpeedCurve和Boomerang等工具來監控和優化用戶體驗。 2.可擴展性:他們採用微前端架構,將應用拆分為獨立模塊,提高開發效率和系統擴展性。 3.用戶體驗:Netflix使用Material-UI組件庫,通過A/B測試和用戶反饋不斷優化界面,確保一致性和美觀性。

NetflixusesAcustomFrameworkcalled“ Gibbon” BuiltonReact,notReactorVuedIrectly.1)TeamSperience:selectBasedonFamiliarity.2)ProjectComplexity:vueforsimplerprojects:reactforforforproproject,reactforforforcompleplexones.3)cocatizationneedneeds:reactoffipicatizationneedneedneedneedneedneeds:reactoffersizationneedneedneedneedneeds:reactoffersizatization needefersmoreflexibleise.4)

Netflix在框架選擇上主要考慮性能、可擴展性、開發效率、生態系統、技術債務和維護成本。 1.性能與可擴展性:選擇Java和SpringBoot以高效處理海量數據和高並發請求。 2.開發效率與生態系統:使用React提升前端開發效率,利用其豐富的生態系統。 3.技術債務與維護成本:選擇Node.js構建微服務,降低維護成本和技術債務。

Netflix主要使用React作為前端框架,輔以Vue用於特定功能。 1)React的組件化和虛擬DOM提升了Netflix應用的性能和開發效率。 2)Vue在Netflix的內部工具和小型項目中應用,其靈活性和易用性是關鍵。

Vue.js是一種漸進式JavaScript框架,適用於構建複雜的用戶界面。 1)其核心概念包括響應式數據、組件化和虛擬DOM。 2)實際應用中,可以通過構建Todo應用和集成VueRouter來展示其功能。 3)調試時,建議使用VueDevtools和console.log。 4)性能優化可通過v-if/v-show、列表渲染優化和異步加載組件等實現。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Atom編輯器mac版下載
最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。