Share some trivia about Vue knowledge during the interview process.Copy the code

Interviewer: What is a vue?

Vue is a progressive javascript framework. Progressive is the layer of build tools that starts with the central view layer rendering and spreads out. This process will go through: View layer rendering -> Component mechanism -> Routing mechanism -> State Management -> Build tools; Five levels.

Features: Easy to use, flexible, efficient, low entry barrier.

Interviewer: What’s the difference between v-if and V-show?

The former is to create and delete the DOM and the latter is to change the display value to control the display and hiding of the DOM.

Interviewer: What is the life cycle of vUE? What happens when you go from new Vue to vm.$destory?

Initialization phase

BeforeCreate and create

Mount the stage

BeforeMount and mounted

Update the stage

BeforeUpdate and update

Unloading phase

BeforeDestory and destory


This.$el and this.$data are not available for each new Vue() function. Next, it initializes inject and detects data, that is, bidirectional binding; This.$data; this.$el; So this is the end of the initialization phase. $mount(el) is called if there is an el option.$mount(el) is called if there is no el option. The render function compiles the template provided by template if it has the template option. If not, the template is selected with the EL option; This compilation phase is over. (Note: this phase is only experienced by the full version of vue.js, which is introduced by CMD; In single-page applications, this compilation phase is not available because vue-loader has compiled it ahead of time, so the vue.js used for single-page applications is the run-time version. When the template is compiled, it goes to the mount phase immediately after the initialization phase. BeforeMount hooks are used to retrieve this.$data and not this. Mounted hook = this.$el = this.$data; mounted hook = this. At this point in the mount phase, you’re done, and the entire instance is mounted. When data changes, it goes into the update phase, firing the beforeUpdate hook and then the updated hook, At this stage, a new Vnode will be generated through recalculation. Then, the diff algorithm in the patch function will be used to compare the newly generated Vnode with the old Vnode in the cache, and finally the difference will be updated to the view. When vm.$destory is called, it enters the unload phase, in which the beforeDestory hook is fired and then the DeStoryed hook is fired. In this phase, Vue removes itself from the parent component, cancelling all tracing on the instance, and removing all event listening. At this point the Vue lifecycle ends.

The image is from vue’s official website 👆

Interviewer: What is the template compilation process like for Vue?

Will first template by first parser, parsed into AST (abstract syntax tree), and then by the optimizer, traverse the AST tree, will find out all the static node inside, and, it can avoid the data update for regenerated new Vnode do some useless kung fu, and diff algorithm comparison some useless comparison, Because static nodes are what they are for the rest of their lives. The code generator then compiles the AST into a code string, which is called by the createElement function in the Vdom, and generates a Vnode.

Interviewer: How does Vue implement data detection?

Vue mainly detects data through Object.defineProperty. Vue data detection has two types: 1.Object data detection. 2.Array data detection. Object type data detection is easier to achieve directly through Object.defineProperty and recursion, but Array type detection is more difficult. Need to push on the hijacked Array prototype, pop, shift, unshift, splice, ` sort, reverse method to implement the detection, because of this a few methods can change their own data. Object defineProperty also has poor Array support. (To Vue.3, Vue data detection will be overwritten by proxy)

The specific code is as follows:

const arrProto = Array.prototype

const arrayMethods = Object.create(arrProto)

const m = ['push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse']

m.forEach(function (method) {

    const original = arrProto[method]

    Object.defineProperty(arrayMethods, method, {

        value: functionv(... args) {return original.apply(this, args)

        }

    })

})

function defineReactive(data) {

    if(data && typeof data ! = ='object') return

    if (Array.isArray(data)) {

        data.__proto__ = arrayMethods

    } else {

        Object.keys(data).forEach(function (val) {

            observer(data, val, data[val])

        })

    }

}

function observer(data, key, value) {

    defineReactive(data)

    Object.defineProperty(data, key, {

        get() {

            return value

        },

        set(newVal) {

            if (newVal === value) return

            value = newVal
        }

    })

}
Copy the code

XXX [XXX] = XXX, add a data to data, vue can detect it? Why is that?

When new Vue() is initialized, the data initialized in the instance data will be detected. Because during the life cycle between the beforeCreate and the CREATE hook, the bidirectional binding of the data is detected. There is no way to complete detection initialization for data added after instance initialization.

Interviewer: Is there any way to solve this problem?

You can solve this problem with vm.$set().

Interviewer: how does vm.$set() work?

vm.$set(target,key,val)

1. Target If it is an array, check whether the key is a valid subscript. Assign a maximum value to target.length and the key passed in, and then call splice to modify the array

2. The key already exists in target and not in the target prototype, that is, only changes the value

3. If the target is not responsive data, it only changes the data without notifying Watcher

4. If target is a vue instance, or target is this.$data, exit the program

5. If none of the above criteria is met, then it is the newly added response data, then call defineReactive() directly to detect the data and notify Watcher

The specific implementation code is as follows:

function set(target, key, val) {

    const ob = target.__ob__

    if (Array.isArray(target) && key >= 0) {

        target.length = Math.max(target.length, key)

        target.splice(key, 1, val)

        return val

    }

    if ((key intarget && ! (keyinObject.prototype)) || ! ob) { target[key] = valreturn val

    }

    if (target._isVue || (ob && ob.vmCount)) {

        return val

    }

    defineReactive(ob.value, key, val)

    ob.dep.notify()

    return val

}

Copy the code

Tip: __ob__, target._isVue,ob.vmCount. If target is a bidirectional bound data, it has an __ob__ attribute on its prototype. If it has an _isVue attribute on its prototype, it is an instance of Vue. If __ob__.vmCount is greater than 0 then the target is the root data

Interviewer: What about vm.$delete?

vm.delete(target,key)

1. If the target is an array and the key is valid, use splice to change the array

2. Target If it is a Vue instance. Or this.$data, then exit the program

If the data is not bound bidirectionally, delete the data directly without notifyWatcher

4. If none of the above conditions are met, then target binds the data bidirectionally and informs Watcher after delete

The specific implementation code is as follows:

function del(target, key) {

    if (Array.isArray(target) && key > 0) {

        target.splice(key, 1)

        return

    }

    const ob = target.__ob__

    if((target._isVue && (ob && ob.vmCount)) || ! target.hasOwnProperty(key))return

    delete target[key]

    if(! ob)return

    ob.dep.notify()

}
Copy the code

Interviewer: Have youVm. $on the vm $off, vm. $once, vm. $emit? What is the implementation principle? Can you write it by hand?

$on,$off,$once, and $emit are standard subscription publishing methods

The specific implementation code is as follows:

vm.$on

Vue.prototype.$on = function (event, fn) {

    const vm = this

    if (Array.isArray(event)) {

        for (let i = 0; i < event.length; i++) {

            vm.$on(event[i], fn)

        }

    } else {

        (vm._events[event] || (vm._events[event] = [])).push[fn]

    }

    return vm

}
Copy the code

Note: _events is defined during initialization, this.events = object.create (null), so don’t be confused about where _events comes from.

vm.$off

Vue.prototype.$off = function (event, fn) {

    const vm = this

    if(! arguments.length) { vm._events = Object.create(null)return vm

    }

    if (Array.isArray(event)) {

        for (let i = 0; i < event.length; i++) {

            vm.$off(event[i], fn)

        }

        return vm

    }

    const cbs = vm._events[event]

    if(! cbs)return vm

    if (arguments.length === 1) {

        vm._events[event] = null

        return vm

    }

    if (fn) {

        let len = cbs.length

        while (len--) {

            let cb = cbs[len]

            if (cb === fn || cb.fn === fn) {

                cbs.splice(len, 1)

                break}}}return vm

}
Copy the code

vm.$once

Vue.prototype.$once = function (event,fn){

    const vm = this

    function on(){

        vm.$off(event,on)

        fn.apply(vm,arguments)

    }

    on.fn = fn
    
    vm.$on(event,on)

    return vm

}
Copy the code

vm.$emit

Vue.prototype.$emit = function(event, ... params) { const vm = thislet cbs = vm._events[event]

    if (cbs) {

        for (let i = 0; i < cbs.length; i++) {

            cbs[i].apply(vm, params)

        }

    }
    
    return vm

}
Copy the code

There are also questions about the implementation principle of vm.$nextTick and the execution principle of the command, because too much food did not answer up 😭.

Come back to check the above two problems to achieve 👇

vm.$nextTick

NextTick is implemented mainly through the implementation mechanism principle of JS eventLoop, which adds callback (promise) to microTask and executes the callback function after the next DOM cycle.

The specific implementation code is as follows:

const callback = []

let pendding = false

function nextTick(cb){
    
    if(cb){
        
        callback.push(()=>{
            cb()
        })
        
    }
    
    if(! pendding){ pendding =true
        
        promise.resolve().then(()=>{
    
            padding =false
 
            const copies = callback.slice(0)
 
            callback.length = 0
 
            for(leti = 0; i < copies.length; i++){ copies[i]() } }) } }Copy the code

How instructions are executed

In the template phase, the directives on the node are resolved and added to the AST’s directives property. Cache data is then passed to the Vnode, and the directives bound to a node can be obtained by the VNod.data. Directives. Finally, when VDom is patched, some hook functions are triggered based on the comparison results of the nodes. The update program listens for the CREATE,update, and deStory hook functions, compares VNode with oldVNode when these three hook functions start, and finally triggers the hook function based on the comparison.