Интервьюер: Я вижу в вашем резюме, что вы знаете Vue?

опрос
Интервьюер: Я вижу в вашем резюме, что вы знаете Vue?
分享在面试过程中,被问到Vue知识点的一些碎事。

Интервьюер: Что такое vue?

Vue — это прогрессивный фреймворк JavaScript. Так называемый прогрессивный: начиная с рендеринга слоя центрального вида и распространяя слой инструментов сборки. Этот процесс будет проходить через: рендеринг слоя просмотра -> механизм компонента -> механизм маршрутизации -> управление состоянием -> инструменты построения; пять уровней.

Особенности: простой в использовании, гибкий, эффективный, низкие входные барьеры.

Изображение взято из Baidu 👆

Интервьюер: В чем разница между v-if и v-show?

Первый предназначен для создания и удаления DOM, а второй — для изменения значения display для управления отображением и скрытием DOM.

Интервьюер: Каков жизненный цикл vue? Что произошло в процессе нового Vue для vm.$destory?

Этап инициализации

Beforecreate и создать

горная сцена

перед монтированием и монтированием

этап обновления

перед обновлением и обновлением

этап удаления

передУничтожить и уничтожить


когдаnew Vue()после первого初始化事件а также生命周期, который затем выполнитbeforeCreate生命周期钩子, вы не можете получить его в этот крючокthis.$elа такжеthis.$data; затем спускайтесь вниз初始化injectа также将data的数据进行侦测也就是进行双向绑定; затем выполнитcreate钩子函数, вы можете получить это в этом крючкеthis.$dataдо сих пор не могу понятьthis.$el; На этом фаза инициализации завершена. Затем он перейдет к этапу компиляции шаблона, на котором он сначала определит, есть ли какие-либоelЕсли есть вариант, то он продолжит снижаться, если нет, то будет вызванvm.$mount(el); Затем продолжайте судить, существует лиtemplateвариант, если есть, будетtemplateПредоставленный шаблон компилируется вrender函数中; если нет, то пройдетel选项Выберите шаблон, фаза компиляции завершена. (Напоминание: этот этап доступен только для полной версии Vue.js, которая также представлена ​​через cmd; в одностраничных приложениях такого этапа компиляции нет, поскольку vue-loader был скомпилирован заранее. Поэтому одностраничный. Используемый vue.js является версией среды выполнения). После того, как шаблон скомпилирован (вот полная версия, если это runtime-версия, то после фазы инициализации он перейдет непосредственно к фазе монтирования), а затем войдет в фазу монтирования, сначала или сработает в фазе зависанияbeforeMount钩子, в этот хук можно попасть толькоthis.$dataдо сих пор не могу понятьthis.$el; затем выполнитmounted钩子, в этом хуке вы можете получить обаthis.$elтакже получитьthis.$data; К этому этапу монтажа весь экземпляр смонтирован. Когда данные изменяются, они переходят в фазу обновления, которая сначала запускаетbeforeUpdate钩子, затем срабатываетupdated钩子, на этом этапе будет произведен пересчет для создания нового Vnode, а затем будет использован алгоритм сравнения в функции patch для сравнения вновь сгенерированного Vnode со старым Vnode в кеше и, наконец, обновление разницы в представлении. когдаvm.$destoryПри вызове он войдет в фазу выгрузки, в этой фазе первый триггерbeforeDestory钩子затем вызватьdestoryed钩子, на этом этапе Vue удалит себя из родительского компонента, отменит все отслеживание экземпляра и удалит все прослушиватели событий. На этом весь жизненный цикл Vue завершен.

Изображение взято с официального сайта vue 👆

Интервьюер: Каков процесс компиляции шаблона Vue?

Сначала шаблон будет разобран в AST (Абстрактное синтаксическое дерево) через парсер, а затем AST-дерево будет пройдено через оптимизатор, и все статические узлы в нем будут найдены и помечены, чтобы избежать перегенерации при выходе из строя. данные обновляются.Проделайте бесполезную работу при создании нового Vnode и сделайте какое-то бесполезное сравнение при сравнении с алгоритмом diff, потому что статические узлы - это то, что они есть в этой жизни, и они не изменятся. Затем генератор кода скомпилирует AST в строку кода, которая будет вызываться функцией createElement в Vdom, и, наконец, сгенерирует Vnode.

Интервьюер: Как Vue реализует обнаружение данных?

вью в основном черезObject.definePropertyОбнаружение данных: Vue имеет два типа обнаружения данных: 1. Обнаружение данных типа объекта. 2. Обнаружение данных типа массива. Обнаружение данных типа объекта легче передать напрямуюObject.definePropertyЭтого можно добиться путем комбинирования рекурсии, но определение типа массива более проблематично, и необходимо перехватить прототип массива наpush,pop,shift,unshift,splice,`sort,reverseметод для достижения обнаружения, потому что эти методы будут изменять свои собственные данные. Определение типа массива более проблематично или потому, чтоObject.definePropertyПоддержка массивов плохая. (В Vue.3 обнаружение данных vue будет переписано через прокси)

Конкретный код реализован следующим образом:

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: function v(...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
        }

    })

}

Интервьюер: Я это.xxx[xxx] = xxx, добавьте данные к данным, может ли vue это обнаружить? Почему?

Нет, новое время инициализации Vue () инициализирует данные в экземпляре данных, чтобы их можно было обнаружить, потому что жизненный цикл beforeCreate будет выполняться для обнаружения данных в данных в двустороннюю привязку между созданием крючка, примеры данных после завершения инициализации, а затем добавляются, нет возможности обнаружить завершение инициализации.

Интервьюер: Что у этой проблемы нет решения?

Эту проблему можно решить с помощью vm.$set().

Интервьюер: Как работает vm.$set()?

vm.$set(target,key,val)

1. Если цель является массивом, сначала определите, является ли ключ допустимым нижним индексом.Если оба условия выполнены, то присвойте максимальное значение target.length и входящий ключ для target.length, а затем вызовите splice для изменения массива

2. Ключ уже есть в таргете а не на целевом прототипе, то есть меняется только значение

3. Если цель не отвечает на данные, то она просто меняет данные и не требует уведомления наблюдателя.

4. Если целью является экземпляр vue или целью является this.$data, то выйти из программы напрямую

5. Если вышеуказанные условия не выполняются, то это новые добавленные данные ответа, затем непосредственно вызовите defineReactive() для обнаружения данных, а затем уведомите наблюдателя.

Конкретный код реализации выглядит следующим образом:

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 in target && !(key in Object.prototype)) || !ob) {

        target[key] = val

        return val

    }

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

        return val

    }

    defineReactive(ob.value, key, val)

    ob.dep.notify()

    return val

}

Подсказка: объясните это здесьtarget.__ob__,target._isVue,ob.vmCountЧто это такое, если целью является двухсторонняя привязка данных, на ней будет прототип__ob__свойства, если таковые имеются на прототипе_isVueсвойство, доказывая, что это экземпляр Vue, если__ob__.vmCountЕсли это больше 0, это доказывает, что целью является корневым даннымthis.$data

Интервьюер: Тогда как это делает vm.$delete?

vm.delete(target,key)

1. Если целью является массив и ключ является допустимым, измените массив с помощью сплайсинга.

2. Если target является экземпляром vue или this.$data, то выйти из программы напрямую

3. Если целью не являются данные с двусторонней привязкой, то удалить их напрямую Не нужно, уведомить наблюдателя

4. Если ни одно из вышеперечисленных условий не выполняется, то целью являются данные двусторонней привязки, и наблюдатель уведомляется после удаления.

Конкретный код реализации выглядит следующим образом:

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()

}

Интервьюер: Вы когда-нибудь использовалиvm.$on,vm.$off,vm.$once,vm.$emit?Какой принцип реализации? Можешь написать от руки?

$on,$off,$once,$emit, эти четыре метода реализации являются очень стандартным режимом публикации подписки.

Конкретный код реализации выглядит следующим образом:

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

}

Напоминание: _events определяется при инициализации реализации, this.events = Object.create(null), поэтому не путайте, откуда берется _events.

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

}

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

}

vm.$emit

Vue.prototype.$emit = function (event, ...params) {

    const vm = this

    let cbs = vm._events[event]

    if (cbs) {

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

            cbs[i].apply(vm, params)

        }

    }
    
    return vm

}

Я также спросил о принципе реализации vm.$nextTick и принципе выполнения инструкции, но не ответил, потому что это было слишком плохо.

Вернитесь и проверьте два вопроса выше, чтобы достичь 👇

vm.$nextTick

nextTick в основном реализуется путем добавления обратного вызова в микрозадачу по принципу механизма выполнения js eventLoop и выполнения функции обратного вызова после следующего цикла DOM.

Конкретный код реализации выглядит следующим образом:

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(let i = 0;i < copies.length;i++){
     
                copies[i]()
     
            }
        })
    }
}

Принцип выполнения инструкции

На этапе шаблона директивы на узле анализируются и добавляются в атрибут директив AST. Затем данные директивы будут переданы на Vnode, а затем инструкции, привязанные к узлу, могут быть получены через vnode.data.directives. Наконец, когда VDom исправлен, некоторые функции ловушек запускаются на основе результатов сравнения узлов. Программа инструкции обновления будет отслеживать функции создания, обновления и уничтожения ловушек и сравнивать VNode и oldVNode при запуске этих трех функций ловушек и, наконец, запускать функцию ловушки инструкции в соответствии со сравнением.