This is the first day of my participation in Gwen Challenge

Note: the following is personal understanding, if there is wrong also hope to correct!

Use nextTick 😁

Method description: Executes a deferred callback after the next DOM update loop. Use this method immediately after modifying the data to get the updated DOM, which is a callback hook that you go to after data initializes the data and renders the page successfully. This method can be used within the lifecycle created, Mounted, or any method

NextTick case 😄

After entering the page, we successfully display a scroll element through a series of operations, and then monitor the scroll event of the displayed element

    //html
    <div id="app">
        <div id="name" v-if="show">Scroll bar content test</div>
        <button @click="changeStatus">Operation success button</button>
    </div>
    //js
    data(){
      return{
        show:false}},methods: {changeStatus(){
          this.show = true;
          // Failed to get node
          let dom = document.getElementById('name')
          dom.addEventListener('scroll'.(e) = >{ console.log(e) })
          // Use nextTick to get the DOM for listening
          this.$nextTick( () = >{
             let dom = document.getElementById('name')
             dom.addEventListener('scroll'.(e) = >{ console.log(e) })
          })
       }
     }
    
Copy the code

Case we know when we go to modify the data above show status value, immediately in the synchronized code execution for dom, found we can't get, this is because the vue won't instantly updated dom, but keep the operation of the need to update the dom in asynchronous update queue, imagine if every time a change is to update operations, blocking synchronization generation Vue also provides the $nextTick hook after dom update in order to solve the operation of dom update asynchronously

Think nextTick 🤔

Let’s take a moment to think about the nextTick implementation. Why does this API allow dom updates to trigger? Are there other native methods? The answer is: yes

setTimeout( () = >{
   console.log('setTimeout' ,document.getElementById('name'))let dom = document.getElementById('name')
   dom.addEventListener('scroll'.(e) = >{ console.log(e) })
})
Copy the code

How does it work when you find that the DOM is also available? Let’s start with the source code analysis of nextTick

NextTick source code analysis 🤔

Locate the source/SRC /core/util/next-tick.js and paste the core

  • Save the set of callback methods in nextTick and clear them after each page rendering
const callbacks = []
// Define a status flag to ensure that only one flushCallbacks function runs after each update
let pending = false
Copy the code
  • Perform a clear operation
function flushCallbacks () {
  // Initialize the tag
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
Copy the code
  • Core, using the eventLoop principle to determine the environment, perform the emptying method
// Prioritize whether promises exist and use microtasks to perform empty callbacks
if (typeof Promise! = ='undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () = > {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true

  // Without promSIE, use the dom change callback provided by H5
} else if(! isIE &&typeofMutationObserver ! = ='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
  SetImmediate is still better than setTimeout
  // This method is used to break up long-running operations and run the callback function as soon as the browser has "done something else"
} else if (typeofsetImmediate ! = ='undefined' && isNative(setImmediate)) {
  timerFunc = () = > {
    setImmediate(flushCallbacks)
  }
  // Use setTimeout last
} else {
  timerFunc = () = > {
    setTimeout(flushCallbacks, 0)}}Copy the code

Through the source code, we find that vue uses the js eventLoop mechanism “eventLoop” principle to do this operation, so why we add setTimeout in the code can also get dom

Doubt 🤔

Why the vue can accurate know when update now, look down from the source, in/SRC/core/observer/scheduler. Have a js

/ / the queue queue
export function queueWatcher (watcher: Watcher) {
     / /... ignore
    if(! waiting) { waiting =true

      if(process.env.NODE_ENV ! = ='production' && !config.async) {
        flushSchedulerQueue()
        return
      }
      // nextTick is executed, and the flushCallbacks callback can be triggered
      nextTick(flushSchedulerQueue)
    }
  }
}
Copy the code

Here he dumps the component watcher queue methods that need to be updated to nextTick, uses the queue first-in, first-out principle, and executes the rest of the developer-defined hooks in nextTick after the component completes its update

conclusion

The first line of code changes the data variable, then goes to vue’s Set method to trigger dep.notify, and tells the collected Watcher to execute the update method. The update method puts itself in the Watcher queue, and then puts the queue in nextT The ICK hook checks to see if there are any flushCallbacks running in nextTick, and if not, it triggers the next-tick.js timerFunc execution (which puts the callbacks into the microtask or macro task and goes back to the end of the first line of our code) This.$nextTick(…); this.$nextTick(…); , and add one of our own definition to callbacks callback methods, remember this time with a component update method hasn’t started, he is better than our advanced queue execution, so why first so we can in the next callback to dom elements inside, because this time the vue have switched their dom updates to perform the remaining c Resolve (). Then () => {… Get dom}, get DOM!