Intensive reading of the Vue official document series 🎉

introduce

Vue’s non-invasive responsive system relies on its special “data model.” More specifically, a normal JavaScript object with a reactive (reactive) feature that updates the view as it is modified.

How to track change

Vue will use object.defineProperty to convert all properties on the component’s data option to properties with getters/setters. The dependencies are then tracked through these getters/setters.

Object.defineproperty is a non-shim feature in ES5, which is why Vue does not support IE8 and earlier browsers.

There is no strict standard for the definition of shim versus polyfill. The common goal is to smooth out the differences between environments and apis. Shim means “Shim”. More commonly referred to as providing a code base that overlays a new environment on top of the old (not necessarily browser) environment to smooth out all the differences overall, while implementing specific features or apis is done by polyfills. Polyfill means “to fill; Be bored with child “. It is more likely to provide a plugin to address the differences between an API in the browser, for example, by introducing the Array polyfill plugin to use the Array traversal methods defined in the ES5 standard. In summary, a SHIm is more about smoothing out differences between environments and is a holistic concept, whereas a polyfill is more about a specific API or functional implementation, so a ShiM includes a polyfill. About this, can see more clearly in es5 – shim repository, es5 – shim is a whole, by specific part of the whole as Array. The prototype. Every blob/main/polyfill. Js is concrete polyfill implementations.

When a property with getter/setter is accessed or modified, the change is notified, and the object notified is the Watcher instance.

Each watcher instance corresponds to a component that records the properties touched as dependencies during the component’s rendering process, and then notifies watcher when the setters of the dependencies are triggered, causing its associated component to be rerendered.

Because different browsers have different formats for output data, vue-devTools is recommended for uniform and friendly debugging.

Precautions for detecting changes

Since Vue uses the object.defineProperty method to perform getter/setter conversion on the property when the component instance is initialized (after the beforeCreated hook), You must ensure that the property exists on the data option before the component instance is initialized.

The essential difference is the object. Property and Object. DefineProperty methods.

Vue does not allow dynamic addition of root-level responsive properties for an already created instance.

var vm = new Vue({
  data: {a:1}})// 'vm.a' is reactive

vm.b = 2
// 'vm.b' is non-responsive
// The root level refers to the property of the component instance itself.
Copy the code

However, it is possible to dynamically add responsive properties to a reference type value that is already responsive.

{
  data() {
    return {
      student: {
        name: "xiaoming",}}; },mounted() {
    setTimeout(() = > {
      Vue.set(this.student, "id"."0001");
      this.$set(this.student, "age".18);
      this.student = Object.assign({}, this.student, { sex: "male" });
      this.student = { ... this.student, ... {height: "tall"}}; },1000); }},Copy the code

The main ways are:

  1. useVueConstructorsetMethods.
  2. Use on the current component instance$setMethods.
  3. Use ES6Object.assignMethods and extension operators.
  4. With the help of other libraries_.extend()Methods.

The basic idea behind methods like 3 and 4 is to create a new object that overrides the value of student, because student itself is also a setter/getter property change that triggers Vue checks normally.

student: ObjectGet student: Æ’ proxyGetter() set student: Æ’ proxySetter(val)Copy the code

For “array”, another common type of reference type, Vue cannot detect changes to an array in the following ways:

  1. When setting an array element directly with an index, for examplearr[arr.length] = newValue.
  2. When you change the length of an array, for examplearr.length = arr.length - 1.

For arrays, you can either use the array’s own operations, such as splice and push, to trigger Vue detection updates, or you can continue to use the set method, but in a slightly different way. The second argument is the index of the array element.

this.$set(this.arr, 0.1);
Copy the code

Declare a root-level responsive property

Vue does not allow dynamically adding root-responsive properties, so you must declare all root-responsive properties, even a null value, before initializing the instance.

Asynchronous update queue

Vue updates the DOM asynchronously.

Asynchronous mode, greatly improved performance, if the use of synchronization, will inevitably block UI rendering.

As soon as data changes are listened for, Vue opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is fired multiple times, it will only be pushed into the queue once. This removal of duplicate data at buffering time is important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop “tick”, Vue refreshes the queue and performs the actual (deduplicated) work.

An Event Loop is a browser-based Event Loop. From synchronous code execution -> microtask execution completed -> macro task execution completed is an event loop. Watcher with the same ID will not be queued repeatedly, so the Watcher run will not be executed 1000 times. The end result is to directly change the value of the data change from 1 to 1000, greatly improving performance. A tick can be thought of as a step (the scale concept of a data chart 🤔), and Vue does a lot of work during each event loop, and each work can be thought of as a tick. For example, looking up an asynchronous queue, pushing to an asynchronous queue is a TICK, performing a DOM update is a TICK, updating completed, and performing a callback is a TICK.

Vue internally tries to use native Promise. Then, MutationObserver, and setImmediate for asynchronous queues, and if the execution environment does not support it, setTimeout(fn, 0) is used instead.

It’s a degrading scheme in which promises and mutationobservers are micro-tasks, and setImmediate and setTimeout are macro-tasks. The former has a higher priority than the latter.

For example, when you set vm.someData = ‘new value’ the component does not immediately rerender. When the queue is flushed, the component is updated in the “tick” of the next event loop. Most of the time we don’t need to care about this process, but if you want to do something based on the updated DOM state, it can get a little tricky. While vue.js generally encourages developers to think “data-driven” and avoid direct contact with the DOM, sometimes it is necessary. To wait for Vue to finish updating the DOM after the data change, use vue.nexttick (callback) immediately after the data change. The callback function will then be called after the DOM update is complete. Such as:

The change to vm.someData = ‘new value’ is actually made in a synchronous environment. Vue.nexttick (callback) can be thought of as a callback method after the watcher has completed execution.

var vm = new Vue({
  el: '#example'.data: {
    message: '123'
  }
})
vm.message = 'new message' // Change the data
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})
Copy the code

It is easier to use the $nextTick method directly on the component instance.

Since $nextTick() returns a Promise object, you can use the new ES2017 async/await syntax to do the same thing:

methods: {
  updateMessage: async function () {
    this.message = 'Updated'
    console.log(this.$el.textContent) // => 'not updated'
    await this.$nextTick()
    console.log(this.$el.textContent) // => 'updated'}}Copy the code