30 Vue interview questions

Vuex Interview questions summary

Vue. Js server side rendering guide

Vue SSR tramping pit tour

Parent-child component communication

Most of the communication provided by vUE itself is parent-child component communication

prop

One of the most common forms of component communication is passing from parent to child

event

One of the most common ways for components to communicate is to notify the parent component through an event when something happens to a child component

this.$emit('Function name', parameter);// Can have multiple arguments, or can be a callback function
Copy the code

Style and class

A parent component can pass style and class to its children, which are merged into the root element of the child component

The sample

The parent component

<template>
  <div id="app">
    <HelloWorld
      style="color:red"
      class="hello"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  components: {
    HelloWorld,
  },
};
</script>
Copy the code

Child components

<template>
  <div class="world" style="text-align:center">
    <h1>{{msg}}</h1>
  </div>
</template>

<script>
export default {
  name: "HelloWorld".props: {
    msg: String,}};</script>
Copy the code

Render result:

<div id="app">
  <div class="hello world" style="color:red; text-aling:center">
    <h1>Welcome to Your Vue.js App</h1>
  </div>
</div>
Copy the code

attribute

If a parent component passes attributes to a child component that the child component does not declare, they are called attributes, and these attributes are attached directly to the root element of the child component

Style and class are not included, they are treated specially

The sample

The parent component

<template>
  <div id="app">
    <! -- attribute (MSG) -->
    <HelloWorld
      data-a="1"
      data-b="2"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  components: {
    HelloWorld,
  },
};
</script>
Copy the code

Child components

<template>
  <div>
    <h1>{{msg}}</h1>
  </div>
</template>

<script>
export default {
  name: "HelloWorld".props: {
    msg: String,},created() {
    console.log(this.$attrs); {"data-a": "1", "data-b": "2"}}};</script>
Copy the code

Render result:

<div id="app">
  <div data-a="1" data-b="2">
    <h1>Welcome to Your Vue.js App</h1>
  </div>
</div>
Copy the code

Subcomponents can be configured with inheritAttrs: false, which disallows attributes to be attached to the root element of the subcomponent, but does not affect fetching through $attrs

Natvie modifier

When registering an event, the parent component can use the native modifier to register the event on the root element of the child component

The sample

The parent component

<template>
  <div id="app">
    <HelloWorld @click.native="handleClick" />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  components: {
    HelloWorld,
  },
  methods: {
    handleClick() {
      console.log(1); ,}}};</script>
Copy the code

Child components

<template>
  <div>
    <h1>Hello World</h1>
  </div>
</template>
Copy the code

The renderings

<div id="app">
  <! -- Click on the div to print 1 -->
  <div>
    <h1>Hello World</h1>
  </div>
</div>
Copy the code

$listeners

Child components can retrieve all event handlers passed by the parent via $Listeners

<! -- Parent component -->
<Child @event1="handleEvent1" @event2="handleEvent2" />
Copy the code
/ / child component
this.$listeners // { event1: handleEvent1, event2: handleEvent2 }
Copy the code

V-model (Principle?)

The V-model directive is essentially a syntactic sugar that is a combination of the value attribute and the input event

<input :value="data" @input="data=$event.target.value" />
<! -- equivalent to -->
<input v-model="data" />
Copy the code
  • Text and Textarea elements use value property and input events;
  • Checkbox and radio use checked Property and change events;
  • The SELECT field takes value as prop and change as an event.

See: Form input binding

V-model can also be applied to custom components, and when applied to custom components, it generates a value property and an input event by default.

<Comp v-model="data" />
<! -- equivalent to -->
<Comp :value="data" @input="data=$event" />
Copy the code

Developers can change generated properties and events through the component’s Model configuration

// Comp
const Comp = {
  model: {
    prop: "number".// The default is value
    event: "change" // Default is input
  }
  // ...
}
Copy the code
<Comp v-model="data" />
<! -- equivalent to -->
<Comp :number="data" @change="data=$event" />
Copy the code

Sync modifier

Similar to v-Model, it is used for bidirectional binding, except that v-Model can only bidirectional bind for one data, while sync modifier is unlimited

The sample

Child components

<template>
  <div>
    <p>
      <button @click="$emit(`update:num1`, num1 - 1)">-</button>
      {{ num1 }}
      <button @click="$emit(`update:num1`, num1 + 1)">+</button>
    </p>
    <p>
      <button @click="$emit(`update:num2`, num2 - 1)">-</button>
      {{ num2 }}
      <button @click="$emit(`update:num2`, num2 + 1)">+</button>
    </p>
  </div>
</template>

<script>
export default {
  props: ["num1"."num2"]};</script>
Copy the code

The parent component

<template>
  <div id="app">
    <Numbers :num1.sync="n1" :num2.sync="n2" />
    <! -- equivalent to -->
    <Numbers
      :num1="n1"
      @update:num1="n1 = $event"
      :num2="n2"
      @update:num2="n2 = $event"
    />
  </div>
</template>

<script>
import Numbers from "./components/Numbers.vue";

export default {
  components: {
    Numbers,
  },
  data() {
    return {
      n1: 0.n2: 0}; }};</script>
Copy the code

The parent and children

Inside the component, you can get the parent and child instances of the current component using the $parent and $children attributes, respectively

Slots and scopedSlots

Subsequent chapters

ref

The parent component can get instances of the children through ref

Cross component communication

Dojo.provide and Inject

The sample

// Parent component provides 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// Inject component 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}
Copy the code

See: cn.vuejs.org/v2/api/?#pr…

router

If a component changes the address bar, all components listening for the address bar react accordingly

The most common scenario is to change the address by clicking on the router-link component, and the router-View component renders other content

vuex

Data warehouse for large projects

Store model

Data warehouse for small to medium sized projects

// store.js
const store = {
  loginUser:... .setting:... }// compA
const compA = {
  data(){
    return {
      loginUser: store.loginUser
    }
  }
}

// compB
const compB = {
  data(){
    return {
      setting: store.setting,
      loginUser: store.loginUser
    }
  }
}
Copy the code

eventbus

A component notifies the event bus that something happened, and the event bus notifies all other components listening for the event to run a function

  • Provides an interface to listen for an event
  • Provides an interface to cancel listening
  • Interface that triggers events (passes data)
  • The listener is automatically notified when the event is triggered
//eventBus.js
// const listeners = {};
// // Event bus principles
// export default {
// // Listens for an event
// $on(eventName, handler) {
// if (! listeners[eventName]) {
// listeners[eventName] = new Set();
/ /}
// listeners[eventName].add(handler);
/ /},
// // Cancel listening
// $off(eventName, handler) {
// if (! listeners[eventName]) {
// return;
/ /}
// listeners[eventName].delete(handler);
/ /},
// // Trigger event
// $emit(eventName, ... args) {
// if (! listeners[eventName]) {
// return;
/ /}
// for (const handler of listeners[eventName]) {
// handler(... args);
/ /}
/ /},
// };


import Vue from "vue";
// A new Vue instance will be created, because the Vue instance is an event bus, because the Vue instance has events like $ON, $emit, and $off.
export default new Vue({});

Copy the code
//main.js
// Test the event bus
import eventBus from "./eventBus";

function handler1(data) {
  console.log("handler1", data);
}
function handler2(data) {
  console.log("handler2", data);
}
eventBus.$on("event1", handler1);
eventBus.$on("event1", handler2);
eventBus.$on("event2", handler1);

window.eventBus = eventBus;
window.handler1 = handler1;
window.handler2 = handler2;
Copy the code

The similarities and differences between the emit and the listeners

Similarities: Both enable child components to pass messages to parent components

In addition:

  • $emitMore consistent with one-way data flow, the child component only notifies, and the parent component listens for changes; while$listenersThe parent component’s methods are used directly in the child.
  • Debugging tools can listen to the child components$emitEvent, but cannot listen to$listenersMethod call in. (Think about why)
  • Due to the$listenersCan get the method passed through, so calling the method can get its return value. but$emitYou simply send a notification to the parent without knowing the result of the parent’s processing

The third point above can be solved by passing the callback function in $emit

The parent component:

<template>
	<Child @click="handleClick" />
</template>

<script>
  import Child from "./Child"
	export default {
    components:{
      Child
    },
    methods: {handleClick(data, callback){
        console.log(data); // Get the data in the child component event
        setTimeout(() = >{
          callback(1); // After some time, the callback function passed by the child component is called
        }, 3000)}}}</script>
Copy the code

Child components:

<template>
	<button @click="handleClick">
    click
  </button>
</template>

<script>
	export default {
    methods: {handleClick(){
        this.$emit("click".123.(data) = >{
          console.log(data); // data is the data obtained after the parent component completes processing})}}}</script>
Copy the code

Event modifier

For native events of DOM nodes, VUE supports a variety of modifiers to simplify code

See: event modifiers, key modifiers, system modifiers

Please explain your understanding of vUE virtual DOM

  1. What is the virtual DOM?

    The virtual DOM is essentially a plain JS object that describes the interface structure of a view

    In VUE, each component has a render function, and each render function returns a virtual DOM tree, which means there is a virtual DOM tree for each component

  2. Why do you need the virtual DOM?

    In Vue, the render view calls the Render function, which occurs not only when the component is created, but also when the view-dependent data is updated. If the real DOM is directly used in rendering, the creation, update, insertion and other operations of the real DOM will bring a lot of performance loss, thus greatly reducing the rendering efficiency.

    Therefore, vUE uses virtual DOM instead of real DOM in rendering, mainly to solve the problem of rendering efficiency.

  3. How is the virtual DOM translated into the real DOM?

    When a component instance is first rendered, it forms a virtual DOM tree, creates a real DOM from the virtual DOM tree, and mounts the real DOM to the appropriate place on the page, one for each virtual DOM.

    If a component affected by responsive data change, need to apply colours to a drawing, it will still call the render function again, create a new virtual dom tree, compared with new and old trees, by contrast, vue will find minimum update quantity, and then update the necessary virtual dom node, in the end, these updated virtual nodes, Will modify their corresponding real DOM

    In this way, minimal changes to the real DOM are guaranteed.

  4. The relationship between templates and virtual DOM

    The Compile module in the Vue framework is responsible for converting the template into the Render function, which is called to produce the virtual DOM.

    The compilation process is divided into two steps:

    1. Converts the template string toAST
    2. willASTconvertrenderfunction

    With the traditional import approach, compilation time occurs when the component is first loaded, which is called runtime compilation.

    If this is the default configuration of vue-CLI, compilation occurs at packaging time, which is called template precompilation.

    Compilation is an extremely performance-consuming operation. Precompilation can effectively improve the performance of the runtime. In addition, since compilation is no longer needed during the runtime, vuE-CLI will exclude the compile module in vue during packaging to reduce the packaging volume

    Templates exist only to make it easier for developers to write interface code

    When vue finally runs, it ultimately needs the render function, not the template, so the syntax in the template does not exist in the virtual DOM and becomes the configuration of the virtual DOM

Please statevue2Response principle

Vue official statement: cn.vuejs.org/v2/guide/re…

The ultimate goal of reactive data is to run functions when the object itself or object properties change, the most common being the Render function.

In terms of implementation, VUE uses several core components:

  1. Observer
  2. Dep
  3. Watcher
  4. Scheduler

Observer

The objective of the Observer is very simple: to convert an ordinary object into a responsive object

To achieve this, the Observer converts each property of the Object through object.defineProperty to a property with a getter and setter, so that vue has a chance to do something else when the property is accessed or set.

An Observer is an internal vUE constructor that can be used indirectly through a static vUE method called vue.Observable (Object).

In the component lifecycle, this happens after beforeCreate and before Created.

In practice, it will recursively traverse all the attributes of the object to complete the deep attribute conversion.

Because the traversal can only traverse the current attributes of an object, it cannot detect future dynamically added or deleted attributes. Therefore, VUE provides two instance methods, $set and $DELETE, which allow developers to add or delete attributes to existing reactive objects.

For an array, VUE changes its implicit stereotype because it needs to listen for methods that might change the contents of the array

In short, the objective of the Observer is for an object to be perceived by the VUE as reading, assigning, and changing its internal array.

Dep

There are two unsolved problems, namely what to do when a property is read, and what to do when a property changes, and that needs to be solved by Dep.

Dep stands for Dependency.

Vue creates a Dep instance for each property in the responsive object, the object itself, and the array itself. Each Dep instance has the ability to do two things:

  • Record dependencies: Who is using me
  • Send out updates: I have changed, and I want to inform those who use me

When a property of a reactive object is read, it does dependency collection: someone used me

When you change a property, it sends an update: Listen to those who use me, I’ve changed

Watcher

So here’s another question, how does Dep know who’s using me?

To solve this problem, we need to rely on another thing, Watcher.

When a function executes and uses reactive data, reactive data has no way of knowing which function is using it

So vUE solves this problem in a clever way

Instead of executing the function directly, we hand it over to something called a Watcher, and a Watcher is an object, and every time a function like this is executed, it should create a Watcher and execute it through a Watcher

/ / deP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP); / / DeP (deP) There’s a Watcher that uses my property

When the Dep dispatches an update, it notifies all previously logged Watchers that I have changed

For each vUE component instance, there is at least one Watcher that records the render function of that component.

Watcher will first run the Render function once to collect dependencies, and the responsive data used in render will record the Watcher.

The DEP notifizes the Watcher when the data changes, and Watcher rerun the Render function to re-render the interface and re-record the current dependencies.

Scheduler

This leaves one final problem, which is that if Watcher rerun the corresponding function after Dep notifes watcher, it could cause the function to run frequently, resulting in inefficiency

Imagine a function given to Watcher that uses attributes A, B, c, and D. The a, B, C, and D properties log dependencies, and the following code triggers four updates:

state.a = "new data";
state.b = "new data";
state.c = "new data";
state.d = "new data";
Copy the code

This is obviously not appropriate, so instead of immediately executing the corresponding function when Watcher is notified of the update, it actually hands itself over to something called a scheduler

The scheduler maintains an execution queue that only exists once for the same watcher. Instead of executing the watcher immediately, the scheduler uses a tool method called nextTick to put the executed Watcher into the microqueue of the event loop. The specifics of nextTick are done through Promise

NextTick is exposed to developers via this.$nextTick

Specific treatment methods of nextTick can be found at cn.vuejs.org/v2/guide/re…

That is, when reactive data changes, the render function executes asynchronously and in microqueues

The overall process

Please explain the DIff algorithm of Vue

Reference Answer:

When a component is created and updated, vUE performs an internal update function that uses the render function to generate a virtual DOM tree that compares the old and new dom trees to find differences and finally updates to the real DOM

The process of comparing differences is called DIff, and Vue does this internally through a function called patch

In comparison, VUE adopts the depth-first and same-layer comparison method.

Vue determines whether two nodes are the same by the key and tag of the virtual node

Specifically, the root node is compared first, and if they are the same, the reference to the real DOM associated with the old node is attached to the new node, then the properties are updated to the real DOM as needed, and then the array of child nodes is compared. If they are different, all real DOM’s are created recursively based on the information of the new node and attached to the corresponding virtual node at the same time, and then the old DOM is removed.

When comparing arrays of child nodes, Vue uses two Pointers to each array of child nodes, one to the head and one to the end, and then to the middle for comparison. The purpose of this is to reuse the real DOM as much as possible, and to destroy and create the real DOM as little as possible. If they are the same, go through the same comparison process as the root node, and if they are different, move the real DOM to the appropriate location.

And so on, recursively, until the whole tree is compared.

  1. The timing of the diff

    When a component is created, and when a dependent property or data changes, a function is run that does two things:

    • run_renderCreate a new virtual DOM tree (vNode tree)
    • run_update, pass the root node of the virtual DOM tree, compare the old and new trees, and finally complete the update of the real DOM

    The core code is as follows:

    // Vue constructor
    function Vue(){
      / /... Other code
      var updateComponent = () = > {
        this._update(this._render())
      }
      new Watcher(updateComponent);
      / /... Other code
    }
    Copy the code

    Diff occurs during the _update function

  2. What is the _update function doing

    The _update function receives a vNode argument, which is the newly generated virtual DOM tree

    Meanwhile, the _update function retrieves the old virtual DOM tree from the _vNode property of the current component

    The _update function first reassigns the component’s _vNode property to point to the new tree

    It then determines whether the old tree exists:

    • Does not exist: indicates that this is the first time to load components, so the internal patch function is used to directly traverse the new tree to generate real DOM for each node and mount it to the ELM attribute of each node

    • Existing: indicates that the component has been rendered before, so the internal patch function is used to compare the old and new trees to achieve the following two objectives:

      • Complete the minimization of all the real DOM
      • Make the nodes of the new tree correspond to the appropriate real DOM

  3. Comparison flow of patch functions

    Explanation of terms:

    1. The same“: indicates the label type of the two virtual nodes.keyThe values are the same, butinputAnd the elementstypeattribute
    2. The new element“: creates a real DOM element based on the information provided by a virtual node and mounts it to the virtual nodeelmOn the properties
    3. Destruction of the element“Refers to:vnode.elm.remove()
    4. Update: A comparison update is performed on two virtual nodes only when the two virtual nodes are the same. The process will be described later.
    5. Compare child nodes: Compares the child nodes of two virtual nodes. Details are described later

    Detailed process:

    1. Root node comparison

      The patch function first compares the root node

      If two nodes:

      • “Same”, enter ** “update” process **

        1. Assign the real DOM of the old node to the new node: newvNode.elm = oldvNode.elm

        2. Compare the properties of the new node with those of the old node and update them to the real DOM with changes

        3. The current two nodes finish processing, start ** “compare child nodes” **

      • Not the same

        1. New node recursive “New element”
        2. Old nodes “destroy elements”
    2. “Contrast child node”

      When “comparing child nodes”, the starting point of vUE is:

      • Try not to do anything
      • Otherwise, try to change only element attributes
      • If not, move elements around instead of deleting and creating them
      • If not, delete and create elements

What happened after New Vue? What happens when the data changes?

  1. The process for creating a VUE instance is basically the same as creating a component

    1. First do some initialization, mainly setting some private properties to the instance

    2. Run the lifecycle hook functionbeforeCreate

    3. Enter the injection process: handle attributes, computed, methods, data, provide, inject, and finally mount them into the instance using proxy mode

    4. Run the lifecycle hook functioncreated

    5. Generate the render function: if configured, use the configured render; if not, use the runtime compiler to compile the template into Render

    6. Run the lifecycle hook functionbeforeMount

    7. Create a Watcher and pass in a function called updateComponent that runs render and passes the resulting vNode to _update for execution.

      During the execution of the Render function, all dependencies are collected and the updateComponent function is rerunced for future dependency changes

      During the execution of _update function, the patch function is triggered. Since there is no old tree at present, the ELM attribute, namely the real DOM, is directly generated for every common node of the current virtual DOM tree.

      If you encounter a vnode that creates a component, you will enter the component instantiation process, which is basically the same as the process of creating a vue instance, and will eventually mount the created componentInstance to the vnode’s componentInstance property for reuse.

    8. Run the lifecycle hook functionmounted

  2. Heavy rendering?

    1. After the data changes, all the Watcher dependent on the data will be run again. Only the Watcher corresponding to the updateComponent function is considered here

    2. The Watcher is run in a nextTick, or microqueue, by the scheduler to prevent multiple dependent data changes from being executed multiple times

    3. Run the lifecycle hook functionbeforeUpdate

    4. The updateComponent function is executed again

      During the render function execution, the previous dependencies are removed, all dependencies are re-collected, and the updateComponent function is rerunced when the dependencies change in the future

      When _update is executed, the patch function is triggered.

      Compare the old and new trees.

      Comparison of normal HTML nodes causes real nodes to be created, deleted, moved, and updated

      The comparison of component nodes causes components to be created, deleted, moved, and updated

      When a new component needs to be created, the instantiation process is entered

      When the old component needs to be deleted, the old component’s $destroy method is called to delete the component. This method triggers the lifecycle hook function beforeDestroy, and then recursively calls the child component’s $Destroy method, which triggers the lifecycle hook function’s Destroyed

      When a component property is updated, the updateComponent function equivalent to the component is re-triggered to enter the re-rendering process, as in this section.

    5. Run the lifecycle hook functionupdated

What is the difference between computed and methods

Standard and simple answers

  1. When used, computed is used as an attribute, and methods as a method call
  2. Computed can have getters and setters, so it can assign values, whereas methods cannot
  3. Computed cannot accept multiple parameters, whereas methods can
  4. Computed has caching, whereas Methods does not

The answer is closer to the underlying principle

Vue’s handling of methods is relatively simple. It simply iterates through each attribute in the methods configuration, uses bind to bind its corresponding function to the current component instance, and copies its reference to the component instance

Vue’s handling of computed is a little more complicated.

When the component instance triggers the lifecycle function beforeCreate, it does a number of things, including processing for computed

It iterates through all the properties in a computed configuration, creates a Watcher object for each property, and passes in a function that is essentially the getter in a computed configuration, so that the getter collects dependencies as it runs

But unlike rendering functions, a Watcher created for a calculated property is not executed immediately, because it takes into account whether the calculated property will be used by the rendering function, and if not, it will not be executed. Therefore, when Watcher is created, it uses a lazy configuration, which prevents Watcher from executing immediately.

With lazy, Watcher stores two key attributes for caching: value and dirty

The value property is used to hold the results of the Watcher run. Affected by lazy, the value is initially undefined

The dirty attribute is used to indicate whether the current value is obsolete, that is, dirty, affected by lazy, which is initially true

After Watcher is created, Vue uses proxy mode to mount calculated properties to the component instance

When a calculated property is read, vue checks if its corresponding Watcher is dirty, if so, runs the function, evaluates the dependency, and gets the corresponding value, saves it in Watcher’s value, then sets dirty to false, and returns.

If dirty is false, watcher’s value is returned

The neat thing is that in dependency collection, the dependent data is collected not only into the Watcher of the calculated property, but also into the Watcher of the component

When a dependency change is calculated, Watcher first triggers the execution of the calculation property, which simply sets dirty to true and does nothing.

Because of the dependency on the Watcher that is also collected for the component, the component is re-rendered, which in turn reads the calculated properties, and since the calculated properties are now dirty, the getter is re-run for the calculation

For setters that evaluate properties, it’s extremely simple to just run the setter when you set the evaluated property