vue的$nextTick的使用+源碼分析

2021-02-21 前端技術專欄


關注 前端技術專欄 ,回復「 資源 」免費領取全套視頻教程

各位!久等了。

時隔五個月,我又開始更新公眾號博客了。最近自己有點懶散,造成這麼長一段時間公眾號和博客斷更了,在這裡對關注我的各位同學們說聲抱歉!!

這幾個月一直忙於工作,休息時間是一半追劇,一半用來去提升自己了,然後看點書玩玩基金什麼的,荒廢了不少時間。最近也在一直調整自己的狀態,覺得還是要更新起來,把自己學到的一些東西分享給各位同學,這樣對於一個技術人來說,是一件有意義的事情!

上面扯了半天白話,說了最近自己的狀況,還是快點進入主題吧。

開始前,我覺得我們需要先弄清楚vue的$nextTick是幹什麼用的,它的存在是為了解決什麼問題?大家也可以自問一下。
nextTick英文翻譯過來「下一個記號或者標記」,我覺得這裡應該用狀態來描述更加合理一些。上面,我拋了一個問題,現在來聊一聊這個問題,首先說一說nextTick的作用,這裡我就直接引用官網api上的一句話,我覺得已經說的非常清楚了:將回調延遲到下次 DOM 更新循環之後執行。在修改數據之後立即使用它,然後等待 DOM 更新。它跟全局方法 Vue.nextTick 一樣,不同的是回調的 this 自動綁定到調用它的實例上。
new Vue({    methods: {        example: function () {            this.message = 'changed'            this.$nextTick(function () {                        this.doSomethingElse()      })    }  }})

上面我用了官網的例子和解釋,我覺得例子還沒完全說明白nextTick解決了什麼樣的問題,所以我覺得我需要補充一下我自己的理解。
大家都知道,在vue的created生命周期裡面,如果你需要在這個生命周期鉤子裡面操作dom是不行的,因為這個鉤子執行的時候dom並沒有渲染到頁面上,所以會直接報錯。這裡有的同學就說,如果我必須要在這裡操作dom呢?那這個時候就到了$nextTick登場的時候,根據官方的解釋和例子也可以證明這一點,nextTick的參數回調函數執行的時候就是dom已經渲染到了頁面,掛載好了,並且把當前this綁定到了當前的實例上面去了,這個時候就可以獲取到更新後的dom元素去操作dom。
其實真正的用意,並不是為了解決這個,其實是為了解決響應式更新的問題。我們都知道,vue是響應式的,什麼是響應式?簡單說,就是數據變了,視圖變,視圖變了,數據變。這樣一來就有一個問題,不知道大家發現了沒,如果沒發現,我們就先看一下下面這段代碼
<template>  <div>    {{a}}    {{b}}    {{c}}  </div></template>

new Vue({  data () {    return {      a: 1,      b: 2,      c: 3    }  },  created () {    this.a = 2    this.b = 3    this.c = 4  }})

上面這段代碼看似很簡單,對不對。但是這裡有一個很有趣的事,大家先想一想,我們說了vue是響應式的,那當我們this.a做重新賦值的時候是不是就把a的值進行修改了,那修改了是不是就應該要觸發頁面的更新,把最新的值顯示到頁面上去,按理來說應該會更新三次對吧,因為我們先修改了a,然後修改b,最後修改的是c,但是結果告訴我們頁面只更新了一次,為什麼呢?說到底,這是因為nextTick方法在裡面作怪,我覺得這個做法非常聰明,這樣可以將性能損失降到最小。這裡只更新一次是因為在源碼上,在收集更新Wathcer時將更新通過nextTick方法做了延遲執行,所以當更新的時候,是先把所有的更新Wathcer收集了起來,然後調用nextTick方法做延遲更新的執行,這樣一來,賦值操作就是在前一直更新數據,更新就會不斷的添加更新Wathcer到隊列中,最後只需要拿出隊列中的所有更新Wathcer去進行挨個更新,這樣一來就會是現在看到的這樣,在賦值的時候並不會馬上更新反應到頁面,而是會先等你的賦值都做完後,最後統一更新,這樣就解決了頁面更新頻率問題。
下面我們來看看官方的對應源碼,來證明一下我說的是不是這樣的。代碼位置在github對應:vue/blob/dev/src/core/observer/scheduler.js文件中,大家可以去看一下
export function queueWatcher (watcher: Watcher) {  const id = watcher.id  if (has[id] == null) {    has[id] = true    if (!flushing) {      queue.push(watcher)    } else {                  let i = queue.length - 1      while (i > index && queue[i].id > watcher.id) {        i--      }      queue.splice(i + 1, 0, watcher)    }        if (!waiting) {      waiting = true
if (process.env.NODE_ENV !== 'production' && !config.async) { flushSchedulerQueue() return }             nextTick(flushSchedulerQueue) } }}

上面說完了使用,下面就是乾貨了,我們來說說nextTick的原理實現,分析一下源碼是怎麼寫的。源碼分析,源碼對應位置在github的vue/blob/dev/src/core/util/next-tick.js文件下面

import { noop } from 'shared/util'import { handleError } from './error'import { isIE, isIOS, isNative } from './env'export let isUsingMicroTask = false
const callbacks = [] let pending = false 
function flushCallbacks () { pending = false   const copies = callbacks.slice(0)  callbacks.length = 0    for (let i = 0; i < copies.length; i++) { copies[i]() }}

let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {  const 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]')) {
  
let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {    timerFunc = () => { setImmediate(flushCallbacks) }} else {    timerFunc = () => { setTimeout(flushCallbacks, 0) }}
export function nextTick (cb?: Function, ctx?: Object) { let _resolve   callbacks.push(() => {    if (cb) {  try {        cb.call(ctx)  } catch (e) {          handleError(e, ctx, 'nextTick') }    } else if (_resolve) {      _resolve(ctx) } })   if (!pending) {     pending = true     timerFunc()  }    if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) }}

最後需要說一下重點的一個變量,就是timerFunc變量,它的最後值決定當前nextTick異步實現的處理方式,代碼會挨個順序判斷兼容情況,最後選擇一個最適合的方式:Promise,MutationObserver, setImmediate,setTimeout

長按識別二維碼

關注「前端技術專欄」加星標

每天給您推送最新原創技術文章

好看,幫點擊在看❤️

相關焦點

  • Tick tick tick tick tick tick boom
    Tick tick tick tick tick tick boom. Our seasonal time limited drinks are also unveiled. Delay no more and make your next coffee moment memorable.五一假期就要來啦,你如何駕馭它?不要再重複相同的東西!
  • vuex實現預熱篇-vue插件開發
    #泛舟計劃·讓知識更好看#1.如何引用vue插件呢?Vue.use(Vuex)2.use做了什麼事呢?來看源碼,通過源碼得知咱們執行了install方法Vue.use = function (plugin) {var installedPlugins = (this._installedPlugins || (this.
  • 【Vue原理】Props - 源碼版
    專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助於理解工作原理,源碼版助於了解內部詳情,讓我們一起學習吧研究基於 Vue版本2.5.17今天記錄 Props 源碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。幸好我做了詳細的文章,忘記了什麼的,回憶起來必然是很快的。
  • 鴻蒙內核源碼分析:調度機制篇
    狹義上的後續有 鴻蒙內核源碼分析(啟動過程篇) 來說明。不知道大家有沒有這種體會,學一個東西的過程中要接觸很多新概念,尤其像 Java/android 的生態,概念賊多,很多同學都被繞在概念中出不來,痛苦不堪。那問題是為什麼需要這麼多的概念呢?舉個例子就明白了:假如您去深圳參加一個面試老闆問你哪裡人?你會說是江西人,湖南人...
  • 尤雨溪在Vue3.0 Beta直播裡聊到了這些…
    混入(mixin) 將不再作為推薦使用, Composition API可以實現更靈活且無副作用的復用代碼。class組件還會繼續支持,但是需要引入vue-class-component@next,該模塊目前還處在 alpha 階段。還有Vue 3 + TypeScript 插件正在開發,有類型檢查,自動補全等功能。目前進展可喜。
  • 使用 Vue.js 和 Semantic-UI 做一個簡單的願望清單
    本文相關源碼及文件,可以在公眾號 簡說Python 回覆:願望清單 獲取。,那麼好的框架當然是越學越香,更多詳細使用可以參考官方文檔。把編譯出的文件和最新的 jQuery 一起包含到 HTML 中就可以使用 Semantic UI了,更多詳細使用可以參考官方文檔。
  • Vue框架之生命周期鉤子
    vue中三大常用屬性的小結、生命周期鉤子的了解,以及指令的初步接觸。指令有點多,只能留到明天繼續補全了。一、回顧與生命周期補充說明三大屬性,當然vue肯定不止這些,只不過時間有限,只學下最常見的。1回顧每次new一個Vue實例都需要關聯模板,Vue會基於此模板進行視圖渲染。
  • clipboard.js 的源碼分析
    2020 年即將結束了,不知不覺 源碼分析 專題已經寫了 9 篇文章,往期的 8 篇文章介紹了 Axios、BetterScroll、koa-compose 和 FileSaver.js 等優秀的開源項目,該專題的每篇文章阿寶哥都花了挺多時間與精力。
  • 如何創建vue項目並使用element框架中的el-select
    1、在電腦硬碟上,找到一個位置,新建一個文件夾wmn;滑鼠右鍵選擇Git Bash Here,並在Git窗口輸入cnpm install --global vue-cli命令:cnpm install --global vue-cli
  • 一張圖教你快速玩轉vue-cli3
    對於普通的 npm 包而言,我們仍然可以(根據所選的 npm 包)使用包管理器。如果你想要通過 Babel 顯式轉譯一個依賴,可以在這個選項中列出來transpileDependencies: ['glob']}可以使用 @vue/babel-preset-app 的 polyfills 選項預包含所需要的 polyfill
  • 使用electron+vue開發一個跨平臺todolist(便籤)桌面應用
    點擊上方藍字"小黑在哪裡"關注我吧# 1最近一直在使用electron
  • 帶你從零看清 Node 源碼 createServer 和負載均衡整個過程
    (給前端大全加星標,提升前端技能)作者: 前端巔峰 公號 / Peter 譚金傑寫在開頭:作為一名曾經重度使用
  • 深入源碼剖析componentWillXXX為什麼UNSAFE
    == nextContext) {  callComponentWillReceiveProps(    workInProgress,    instance,    newProps,    nextContext,  );}你可以從這裡1看到這段源碼
  • 如何寫一個vue組件專題及常見問題 - CSDN
    允許外部環境將額外的內容組合在組件中prop組件具有自身狀態,當沒有相關 porps 傳入時,使用自身狀態完成渲染和交互邏輯;當該組件被調用時,如果有相關 props 傳入,那麼將會交出控制權,由父組件控制其行為僅一個值傳入組件如果該組件設計上支持雙向綁定,可使用v-model將該參數傳入組件,減少記憶成本(畢竟 vue 官方的語法糖,不用白不用)
  • 一文搞懂 CountDownLatch 用法和源碼!
    CountDownLatch 的源碼CountDownLatch 源碼分析CountDownLatch 使用起來比較簡單,但是卻非常有用,現在你可以在你的工具箱中加上 CountDownLatch 這個工具類了。
  • 技術分享:vue 過濾器
    vue 過濾器 1 關於 vue 過濾器 在vue1.0的時候其實是內置了過濾器的,但是考慮到好多過濾器並不一定會被開發所調用,所以把原本內置的過濾器就給去掉了,但是過濾器還是比較普遍的,所以我們從vue2.0之後就需要自己定義過濾器
  • 準備將您的Vue應用遷移到Vue 3
    例如:// helper/filter.jsexportfunctiontoCurrency (value) {return`$${value.toFixed(2)}`}然後,您可以將輔助函數導入到需要使用它的組件中。例如:// price-component.vueimport { toCurrency } from'.
  • 阿里技術官架構使用總結:Spring+MyBatis源碼+Tomcat架構解析等
    今天就由阿里一線P8架構師揭秘,對他使用的技術進行了一個總結,這個PDF總結主要涉及到Spring、MyBatis源碼以及Tomcat等,希望能夠幫助到大家,對自己有一定提升。需要PDF版的朋友,直接私信我【架構】即可免費領取哦~01 Spring源碼深度解析第一部分 核心實現第1章 Spring整體架構和環境搭建
  • 鴻蒙內核源碼分析(內存主奴篇) | 紫禁城的主子和奴才如何相處? | 中文註解HarmonyOS源碼 | v9.03
    鴻蒙內核源碼注釋中文版 【 Gitee倉 | CSDN倉 | Github倉 | Coding倉 】精讀內核源碼,中文註解分析.深挖地基工程,構建底層網圖
  • vue+swiper+animate.css製作全屏滾動H5
    H5長翻頁動畫效果接下來就來介紹一下我的製作步驟;1.安裝swiper、animate.cssnpm install swiper@3 npm install animate.css 2.在main.js中引入animate.cssimport Vue from "vue"import