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:
$emit
More consistent with one-way data flow, the child component only notifies, and the parent component listens for changes; while$listeners
The parent component’s methods are used directly in the child.- Debugging tools can listen to the child components
$emit
Event, but cannot listen to$listeners
Method call in. (Think about why)- Due to the
$listeners
Can get the method passed through, so calling the method can get its return value. but$emit
You 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
-
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
-
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.
-
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.
-
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:
- Converts the template string to
AST
- will
AST
convertrender
function
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
- Converts the template string to
Please statevue2
Response 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:
- Observer
- Dep
- Watcher
- 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.
-
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
_render
Create 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
- run
-
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
-
-
Comparison flow of patch functions
Explanation of terms:
- “The same“: indicates the label type of the two virtual nodes.
key
The values are the same, butinput
And the elementstype
attribute - “The new element“: creates a real DOM element based on the information provided by a virtual node and mounts it to the virtual node
elm
On the properties - “Destruction of the element“Refers to:
vnode.elm.remove()
- 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.
- Compare child nodes: Compares the child nodes of two virtual nodes. Details are described later
Detailed process:
-
Root node comparison
The patch function first compares the root node
If two nodes:
-
“Same”, enter ** “update” process **
-
Assign the real DOM of the old node to the new node: newvNode.elm = oldvNode.elm
-
Compare the properties of the new node with those of the old node and update them to the real DOM with changes
-
The current two nodes finish processing, start ** “compare child nodes” **
-
-
Not the same
- New node recursive “New element”
- Old nodes “destroy elements”
-
-
“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
- “The same“: indicates the label type of the two virtual nodes.
What happened after New Vue? What happens when the data changes?
-
The process for creating a VUE instance is basically the same as creating a component
-
First do some initialization, mainly setting some private properties to the instance
-
Run the lifecycle hook function
beforeCreate
-
Enter the injection process: handle attributes, computed, methods, data, provide, inject, and finally mount them into the instance using proxy mode
-
Run the lifecycle hook function
created
-
Generate the render function: if configured, use the configured render; if not, use the runtime compiler to compile the template into Render
-
Run the lifecycle hook function
beforeMount
-
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.
-
Run the lifecycle hook function
mounted
-
-
Heavy rendering?
-
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
-
The Watcher is run in a nextTick, or microqueue, by the scheduler to prevent multiple dependent data changes from being executed multiple times
-
Run the lifecycle hook function
beforeUpdate
-
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.
-
Run the lifecycle hook function
updated
-
What is the difference between computed and methods
Standard and simple answers
- When used, computed is used as an attribute, and methods as a method call
- Computed can have getters and setters, so it can assign values, whereas methods cannot
- Computed cannot accept multiple parameters, whereas methods can
- 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