Since I have not learned the principle of VUe3 deeply, this paper mainly introduces vue2 and its response principle

What is a VUE? What are the advantages?

  • An incremental framework that can be used as a third-party library or to build complex single-page projects
  • Get started simple, as long as the basic HTML, CSS, JS can get started development
  • Internal use of virtual DOM, efficient operation
  • Two-way data binding

VUE versus MVVM model

VUE is similar to MVVM model. It makes use of VIewModel layer to bind the data of model layer and VIew layer bidirectional. When the data of VIew layer changes, it will automatically synchronize to Model, and when the data of Model changes, it will update the VIew layer. The VIew has no direct connection with the Model layer, which reduces the coupling degree. In this way, the programmer only cares about the implementation of business logic when writing code, and basically does not need to manually manipulate DOM elements and worry about data updates.

Why is VUE similar to the MVVM model?

VUE official website has such a sentence

The reason is that vue has a refs attribute, which can directly fetch the DOM elements of the page, breaking the requirement that the View and Model are not associated, so vUE is not strictly an MVVM Model

VUE fundamentals

The data was hijacked

When a VUE instance is created, the data placed in its data is added to the vUE’s responsive system, causing the view to be updated as the data changes.

new Vue({
    // Mount to the root element of the page
    el: '#app'.data: {
        name: 'zs'.obj: {
            age: 20}}})Copy the code

In fact, vue2’s responsivity is based on Object.defineProperty. It hijures the listener for each property in data, sets getter and setter for each property separately. In addition, each attribute has its own DEP attribute, which stores the Watcher it depends on (dependency collection). When the attribute changes, it will notify its corresponding Watcher to update it.

function reactive(data) {
    if (Object.prototype.toString.call(data) == '[object Object]') {
        Object.keys(data).forEach(key= > {
            if (Object.prototype.toString.call(data[key]) == '[object Object]') {
                reactive(data[key])
            }
            Object.defineProperty(data, key, {
                enumerable: true.configurable: true.get() {
                    return data[key]
                },
                set(newVal) {
                    if(newVal ! == data[key]) { data[key] = newVal } } }) }) } }Copy the code

The data binding code of the simplified version above is similar to the internal operation principle of VUE2. Data binding in VUe3 mainly uses Proxy, which will be explained later

This.$set() is used to add a value to the built-in reactive object (data), and this value is also reactive. Including Array subscript or length attribute changes, the official solution is to rewrite some of the Array prototype methods (splice, push, sort, etc.).

Dep (Subscriber)

The Dep class is used in VUE to create a subscriber. The subscriber is used to store the Watcher object and notify the corresponding Watcher to update it when the data changes

class Dep {
    constructor() {
        this.watchers = []
    }
    // Collect the corresponding watcher
    addWatcher(item) {
        this.watchers.push(watcher)
    }
    // Notify the collected Watcher to update when data changes
    notify() {
        this.watcher.forEach(item= >{ item? .update() }) } }Copy the code

Watcher

The Watch class is used in Vue to create subscribers. Each attribute in data may correspond to multiple subscribers, and the role of subscribers is to update the data when it is updated

class Watcher {
    constructor() {
        Dep.target = this
    }
    
    update() {
        // Update the logic code
    }
}

Dep.target = null
Copy the code

This is vue’s observer and subscriber pattern. In the following sections, I will describe how Vue uses data hijacking in conjunction with the observer and subscriber pattern for two-way data binding

// When compile parsing is done, a Watcher object is automatically instantiated for each attribute
// I'll skip the Watcher instantiation process here

function reactive(data) {
    if (Object.prototype.toString.call(data) == '[object Object]') {
        Object.keys(data).forEach(key= > {
            if (Object.prototype.toString.call(data[key]) == '[object Object]') {
                reactive(data[key])
            }
            const dep = new Dep()
            Object.defineProperty(data, key, {
                enumerable: true.configurable: true.get() {
                    // Rely on collection
                    Dep.target && dep.addWatcher(Dep.target)
                    return data[key]
                },
                set(newVal) {
                    if(newVal ! == data[key]) {// When the changed value is an object, its attributes are hijacked
                        if (Object.prototype.toString.call(newVal) == '[object Object]'){
                            reactive(newVal)
                        }
                        data[key] = newVal
                        // Notification update
                        dep.notify()
                    }
                }
            })
        })
    }
}
Copy the code

In data hijacking, each attribute corresponds to a subscriber. When the attribute value is obtained, the corresponding observer is stored in the subscriber. When the data is updated, the subscriber notifies all observers it collects to make the update.

About Dep. Target

About why dep. target is set to watcher instance, and why dep. target is set to null later; This is a bit of a puzzle when it comes to the compile phase of the source code, and it’s a bit confusing if I just look at the code above, so I’ll try to make it as clear as I can

Each attribute in data has a subscriber, and during data hijacking you can see that a subscriber is created for each attribute. Collections and updates are then implemented in the form of closures. A property in data may be bound at the View layer many times, not once, so the dependencies in DEP are collected as arrays. Every time the VIew layer is used, there is a watcher. When the VIew layer is used (compile stage), the Watcher instance is generated, and the property value is obtained, the corresponding getter is triggered, and the corresponding Watcher is added. If not set to null, watcher will always be added to the DEP as the data changes, something like this

Batch Asynchronous Update (nexTtick)

So let’s say we have code like this

<div id="app">
    <div>{{number}}</div>
    <div @click="handleClick">click</div>
</div>

new Vue({
    el: "#app",
    data {
        number: 0
    },
    methods: {
        clickHandle() {
            for(let i = 0; i < 1000; i++) {
                this.number++; }}}})Copy the code

If the View is updated 1000 times, the View layer will be updated 1000 times, but vUE implements a nextTick method to prevent this from happening. This means that each change pushes the corresponding Watcher into a queue, and the next tick triggers an update of the Watcher in the queue.

The role of the key attribute in a VUE loop

I think the key attribute is used to diff whether two VNodes belong to sameVNode or not. If two vNodes belong to sameVNode, node reuse will be performed. Therefore, when a large number of node updates are rendered, the efficiency with key(unique ID) is lower than that without key(unique ID), and the performance overhead is large, because each comparison with key(unique ID) will always determine that the node is different, and vNodes will be recreated instead of being reused. If you use index as the key, it’s the same as if you didn’t, because the index might not change every time you update it.

Applicable scenarios:

  • Don’t take the key

This is useful for rendering stateless components, because there is no state of its own, so keys are not required

  • With key (unique ID)

Applies to stateful components: say there are two TAB columns, corresponding to different lists. If an item is selected in the first TAB bar, when switching to the second status bar without key, it will be considered as the same node in comparison and reused. In this way, the item in the second TAB bar will also be selected. If there is a key(unique ID), the VNode will be considered different by comparison, so that the status of the first TAB does not affect the status of the second TAB.

VUE3

  • Data hijacking using Proxy, the new attributes also achieved hijacking
  • Diff algorithm is updated. IsStatic tag is added. Only marked virtual nodes are compared during patch, and VUe2 is the full comparison
  • Static improvement (sacrifice memory for efficiency?)

In VUe2, elements are created regardless of whether they participate in the update. In VUe3, elements that do not participate in the update are created only once

  • To monitor the cache

The onclick event is bound to the same function, so the static tag is removed and reused without tracing