This article has participated in the weekend learning program, click the link to see more details: juejin.cn/post/696572…

Vue family barrel development project for a long time, I feel very skilled. But still being asked questions about Vue during the interview? It shouldn’t, so make a decision to write down the following questions in one go, and believe me, Vue will never bother you again!

Content is not much, hope patience to read, repeatedly chewing.

key Function and principle of

Key is the unique identifier of each Vnode and an optimization strategy of DIff. It can be used to find the corresponding VNode more accurately and faster according to the key, reducing dom operations and improving DIff efficiency.

scenario

1. V-for requires a key

  • If a key is not used, Vue uses the in-place reuse principle: it minimizes movement of an element and tries to patch or reuse the same type of element as much as possible in the same place.
  • If a key is used, Vue records elements based on the order of keys. Elements that once had a key are removed or destoryed if they no longer appear

  • When rerender with the new value is used as the key and a Comp with the new key appears, the old key Comp is removed and the new key Comp triggers re-rendering (walking the component life cycle).

Why use key

By default, Vue usually reuses existing elements rather than rendering them from scratch in order to render them efficiently. However, this may lead to some data confusion, which is not in line with our actual development needs. So Vue provides a way to add a key attribute with a unique value that says “these two elements are completely independent, don’t reuse them.”

Does setting a key necessarily improve diff efficiency?

It’s not, and the documentation makes that clear

When vue.js is updating a rendered element list with V-for, it defaults to a “reuse in place” strategy. If the order of the data items is changed, Vue will not move the DOM elements to match the order of the data items, but will simply reuse each element here and make sure it shows every element that has been rendered under a specific index

This default pattern is efficient, but only for non-dependent child component states or temporary DOM states (for example: It is recommended to supply keys whenever possible with V-for, unless traversing the DOM content of the output is very simple or you deliberately rely on default behavior for performance gains

$nextTick([callback]) asynchronously updates the queue

Remember this: defer the callback function until after the next DOM update loop. It is usually used immediately after data modification to retrieve the updated DOM. Vue internal asynchronous queue attempts to use the native microtask that several functions to perform.

// Modify the data
vm.msg = "Hello";
// DOM has not been updated yet
Vue.nextTick(function () {
  // DOM is updated
});

// Use as a Promise (new since 2.1.0, see hints below)
Vue.nextTick().then(function () {
  // DOM is updated
});
Copy the code

Virtual DOM

What is the virtual DOM

A more lightweight, pure JavaScript object (tree) describes the DOM tree, called the “virtual DOM” because it is not a real DOM object.

Because there are so many native DOM properties, manipulating JS objects is much more efficient than manipulating DOM.

Reveal story: in fact, is to use the sacrifice of CPU to JS processing performance to replace the GPU to page rendering performance.

Why use the virtual DOM

Example: Add and sort a list.

1. The mid-stage template engine does not solve the problem of tracking state changes: when we manipulate the DOM (add, sort, etc.), because there is no state tracking list, the DOM needs to be deleted and then rebuilt.

2. The addition of the virtual DOM only updates the changed DOM elements. Sorting the virtual DOM just reverses the element position.To put it bluntly, you trade the computational performance of JS for the performance of manipulating the real DOM. Virtual DOM nodes corresponding to the real DOM are generated before and after data changes, and then the new and old VNodes are compared to update the different DOM nodes, so as to achieve the goal of updating the view with the least operation on the real DOM.

The role of the virtual DOM

  • Maintain the relationship between views and states
  • Simple pure DOM performance is still possible to improve rendering performance in complex views.
  • In addition to DOM rendering, it can also realize cross-platform SSR(nuxt.js \ Next-js), Native application (Weex\React Native), small program (mpvuue \uni-app), etc. Because the virtual DOM is itself a JS object, you can program it in any way you want.

The following problems need to be solved:

1. Efficient DIFF algorithm, namely the comparison of two Virtual DOM, is actually the patch process. 2

snabbdomVirtual DOM open source library

Introduction to the

The Virtual DOM used internally in Vue 2.x is a modified SnabbDOM. Lightweight Virtual DOM implementation, less code, modular, TS development structure is clear.

Look at the document

  • The first step to learning about any library is to look at the documentation
  • Understand the library’s role through documentation
  • See the examples provided in the documentation for a quick demo of your own
  • View API usage through documentation

Core events:

  • Use the h() function to create javascript objects (vNodes) that describe the real DOM
  • Init () sets the module, creates the patch()
  • Path () compares the old and new vNodes and updates the changes to the real DOM tree

The module

How modules are used

1. Import modules: similar to plug-ins

  • Import the required modules
  • Register modules in init([])
  • When creating a Vnode using the h() function, you can set the second parameter to the object and move the other parameters back.
import { init, h } from "snabbdom";
import { init, h, eventListenersModule, styleModule } from "snabbdom"; / / the latest version
// Style module
// Event listener module
Copy the code

2. Registration module

let patch = init([styleModule, eventListenersModule]);
Copy the code

3. Pass in the data (object) required by the module using the second argument to h().

let vnode = h(
  "div",
  {
    style: {
      backgroundColor: "red",},on: {
      click: eventHandler,
    },
  },
  [
    // Array child
    h("h1"."This is Children H1."),
    h("p"."This is the P tag."),]);function eventHandler() {
  console.log("Click on me");
}

let app = document.querySelector("#app");
patch(app, vnode); // The page is rendered
Copy the code

The source code parsing

snabbdom github

h.ts

Hyperscript used JavaScript to create hypertext. The h() function here is enhanced to create a VNode

H (‘sel’, data, children) takes a label or selector in string format, an optional data object, an optional string or an array of child nodes.

The main use of h function overload, three parameters three cases of judgment, finally through the vNode function to create nodes, as well as SVG processing.

The internal implementation of so performs various judgments on the three arguments passed in. If children is an array, the loop calls vNode to create a virtual node

Key point VNode

The creation of a VNode

VNode renders the real DOM

The init function is called in the most critical init.ts file, and the patch function is returned by the higher-order function, which makes it more convenient for patch to access the two parameters passed in by init to form the closure.

Publish subscribe mode and observer mode

So the last picture was a little bit clearer

Publish and subscribe model

  • The subscriber
  • The publisher
  • Event center

For example, Vue’s custom events and eventbus.js use this pattern.

// Implement an event trigger
class EventEmiter {
  constructor() {
    {'click': [fn1,fn2],change: [fn3, fn4]}
    this.subs = Object.create(null);
  }

  // Register the event as an array since it can be multiple events
  $on(eventType, handle) {
    this.subs[eventType] = this.subs[eventType] || []; // Initialize the currently registered event (because there was no event to start with)
    this.subs[eventType].push(handle); // Store it as an array
  }

  // Trigger (publish) events
  $emit(eventType) {
    // Find all registered events according to the event type
    this.subs[eventType].forEach((handle) = >{ handle(); }); }}/ / test
let em = new EventEmiter();

em.$on("click".() = > {
  console.log("em.$on");
});

em.$on("click".() = > {
  console.log("em.$on2222");
});

em.$emit("click".() = > {
  console.log("em.$emit");
});
Copy the code

Observer model

  • Subscribers – Watch Watcher

Update () (responsible for updating the view)

  • Publisher – Target Dep (Dependency)

    • Subs array: Stores all observers
    • AddSub (): Adds an observer
    • Notify (): Call the update() method for all observers when an event occurs (when data changes)
  • No event center

// Define what the observer needs to do
class Watcher {
  update() {
    console.log("I'm the observer update function, I'm going to update the view."); }}/ / release
class Dep {
  constructor() {
    this.subs = [];
  }
  / / collection
  addSub(sub) {
    // Make sure to be an observer
    if (sub && sub.update) {
      this.subs.push(sub); }}/ / notice
  notify() {
    // Call your own method through all observers
    this.subs.forEach((sub) = >{ sub.update(); }); }}/ / test
let wac = new Watcher();
let dep = new Dep();
dep.addSub(wac);
dep.notify();
Copy the code

Simulate the Vue responsive principle

Let’s have a brief analysis:

  • Vue: Inject all members of data into the Vue instance and add getters/setters, internally calling Observer\Compiler
  • Observer data hijacking: Listens for all attributes of a data object and notifies Dep of any changes to the latest values
  • Compiler: Parses the instructions and interpolations in each element and replaces them with the corresponding data
  • Dep: Adds observers and notifies all observers when data changes
  • Watcher: Defines the update function, which updates the view

Vue

Observer data hijacking

Dep relies on collection

The Watcher observer

Have attributes:Vm: Vue instance key: the property of the object, which data is used to obtain cb: the callback function, which handles how to update the view oldValye: the old value, compared with the new value should be updatedMethods:Update () : Mainly deals with the ability to update views

This combination makes a Vue responsive core.

Vue array change detection

First: Change the value and length directly based on the indexthis.list[0] = 3,this.list.length = 5Both methods do not trigger two-way data binding.Copy the code

The most detailed version interpretation: mp.weixin.qq.com/s/FimW7pNVZ… The problem of array change monitoring in Vue2. X is not that Object. DefinePropertype method can’t detect array changes. That’s the rewrite of the array mentioned above.

The way of using Proxy in Vue3.0 perfectly solves the problems in 2.x, so if you encounter the processing of array listening in Vue in the future interview, you must be clear about which version.

Parent-child component lifecycle order

  • Rendering process:

Parent beforeCreate -> Parent created -> parent beforeMount-> child beforeCreate -> child created -> child beforeMount-> Child Mounted -> parent Mounted

  • Sub-component update process:

Parent beforeUpdate -> Child beforeUpdate -> Child updated -> Parent updated

  • Parent component update process:

Parent beforeUpdate -> Parent updated

  • Destruction process:

Parent beforeDestroy -> Child beforeDestroy -> Child destroyed -> Parent destroyed

/** run: **/<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>Parent component life cycle</title>
  </head>

  <body>
    <div id="app">Precede parent beforeCreate ---- beforeMount child beforeCreate start > Mounted keep-alive Trigger Activated Data changes. Updated After the view is updated<! V-model = v-bind + V-on
      <input type="text" v-model="message" />
      <input
        type="text"
        v-bind:value="message"
        v-on:input="message = $event.target.value"
      />
      <p>{{message}}</p>
      <keep-alive>
        <my-components :msg="msg1" v-if="show"></my-components>
      </keep-alive>
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>

  <script>
    var child = {
      template: "<div>from child: {{childMsg}}</div>".props: ["msg"].data: function () {
        return {
          childMsg: "child"}; },beforeCreate: function () {
        debugger;
      },
      created: function () {
        debugger;
      },
      beforeMount: function () {
        debugger;
      },
      mounted: function () {
        debugger;
      },
      deactivated: function () {
        alert("KeepAlive disabled");
      },
      activated: function () {
        console.log("component activated");
      },
      beforeDestroy: function () {
        console.group("BeforeDestroy Pre-destruction state ===============");
        var state = {
          el: this.$el,
          data: this.$data,
          message: this.message,
        };
        console.log(this.$el);
        console.log(state);
      },
      destroyed: function () {
        console.group("Destroyed destruction completion state ===============");
        var state = {
          el: this.$el,
          data: this.$data,
          message: this.message,
        };
        console.log(this.$el);
        console.log(state); }};var vm = new Vue({
      el: "#app".data: {
        message: "father".msg1: "hello".show: true,},beforeCreate: function () {
        debugger;
      },
      created: function () {
        debugger;
      },
      beforeMount: function () {
        debugger;
      },
      mounted: function () {
        debugger;
      },
      beforeUpdate: function () {
        alert("Before page View is updated");
      },
      updated: function () {
        alert("Page view updated");
        console.log("1== I'll do it first.");

        this.$nextTick(function () {
          // Execute this callback after the next DOM update loop. Use this method immediately after modifying the data to get the updated DOM.

          console.log("3== I can't execute immediately until the page is rendered");
        });

        console.log("2== I'm going to execute last but before $nextTick");
      },
      beforeDestroy: function () {
        console.group("BeforeDestroy Pre-destruction state ===============");
        var state = {
          el: this.$el,
          data: this.$data,
          message: this.message,
        };
        console.log(this.$el);
        console.log(state);
      },
      destroyed: function () {
        console.group("Destroyed destruction completion state ===============");
        var state = {
          el: this.$el,
          data: this.$data,
          message: this.message,
        };
        console.log(this.$el);
        console.log(state);
      },
      components: {
        "my-components": child,
      },
    });
  </script>
</html>
Copy the code

Value transfer/communication between components

This is the basis. Here are the key words:

1. v-bind \ : // Parent <child @handlechange ="changeName"></child> // child this.$emit('handleChange', 'Jack') 3 $parent / $children / $refs / $refs / $refs / $refs / $refs / $refs $emit("editName", eventbus.js); This.name) // triggers the global event, and passes the changed value into the event function. Provide /inject No matter how deep the component hierarchy is, this variable remains in effect for as long as the parent component is in effect. Note: Provide and Inject bindings are not responsive. That is, when the name of the parent component changes, the child component does not change. 6. Powerful VuexCopy the code

Conclusion:

  • Parent/child communication: the parent passes data to the child via props and the child passes data to the parent via $emit;

    Communicate via parent/parent /parent /children; Ref can also access component instances; Dojo.provide/inject; [ref can also access component instance; dojo.provide/inject; [ref can also access component instance; dojo.provide/inject; attrs / $listeners (segmentfault.com/a/119000002…

  • Brother communication: Bus; Vuex

  • Cross-level communication: Bus; Vuex; provide / inject ; Attrs/attrs/attrs/listeners;

Implementation principle of V-Model

The syntax is :value + @input

  • Native DOM: input \ select \ textarea
<input v-model="message"></input>

<input :value="message" @input="message = $event.target.value"></input>
Copy the code

Why is data a function in a component

If it is an object, according to the JS prototype chain, when multiple instances reference the same object, one of them is modified, and all the other objects are affected. Obviously that’s not going to work.

The code examples to explain: zhuanlan.zhihu.com/p/100859260