前端框架面試之 Vue 面試真題 和 Vue3 相關考點

一、Vue 面試真題

  1. v-showv-if 的區別,答案如下所示:
  • v-show 通過 CSS display 控制顯示與隱藏
  • v-if 組件真正的渲染和銷燬,而不是顯示與隱藏
  • 頻繁切換顯示狀態用 v-show,否則用 v-if
  1. 爲何在 v-for 中用 key,答案如下所示:
  • 必須用 key,且不能是 indexrandom
  • diff 算法中通過 tagkey 來判斷,是否是 sameNode
  • 減少渲染次數,提升渲染性能
  1. Vue 組件如何通訊,常見的方式,答案如下所示:
  • 父子組件 propsthis.$emit
  • 自定義事件 event.$on、event.$off 和 event.$emit
  • vuex
  1. 雙向數據綁定 v-model 的實現原理,答案如下所示:
  • input 元素的value = this.name
  • 綁定 input 事件 this.name = $event.target.value
  • data 更新觸發 re-render
  1. computed 有何特點,答案如如下所示:
  • 緩存,data 不變不會重新計算
  • 提高性能
  1. ajax 請求應該放在哪個生命週期,答案如下所示:
  • mounted 生命週期函數
  • JS 是單線程的, ajax是異步獲取數據
  • 放在 mounted 之前沒有用,只會讓邏輯更加混亂
  1. 如何將組件所有 props 傳遞給子組件,答案如下所示:
  • $props
  • <User v-bind = "$props" />
  • 細節知識點,優先級不高
  1. 多個組件有相同的邏輯,如何抽離,答案如下所示:
  • 使用 mixin
  1. 何時要使用異步組件,答案如下所示:
  • 加載大組件
  • 路由異步加載
  1. 何時需要使用 keep-alive,答案如下所示:
  • 緩存組件,不需要重複渲染
  • 如多個靜態 tab 頁的切換
  • 優化性能
  1. 何時需要使用 beforeDestory,答案如下所示:
  • 解綁自定義事件 event.$off
  • 清除定時器
  • 解綁自定義的 DOM 事件,如 window scroll
  1. Vuexactionmutation 有何區別,答案如下所示:
  • action 中處理異步,mutation 不可以
  • mutation 做原子操作
  • action 可以整合多個 mutation
  1. Vue-router 常用的路由模式,答案如下所示:
  • hash 默認
  • H5 history,需要服務端支持
  • 兩者比較
  1. 監聽 data 變化的核心 API 是什麼,答案如下所示:
  • Object.defineProperty
  • 深度監聽、監聽數組
  • 有何缺點
  1. Vue 如何監聽數組變化,答案如下所示:
  • Object.defineProperty 不能監聽數組變化
  • 重新定義原型,重寫 pushpop 等方法,實現監聽
  • Proxy 可以原生支持監聽數組變化
  1. 請描述響應式原理,答案如下所示:
  • 監聽 data 變化
  • 組件渲染和更新的流程
  1. diff 算法的時間複雜度,答案如下所示:
  • O(n)
  • O(n^3) 基礎上做了一些調整
  1. 簡述 diff 算法過程,答案如下所示:
  • patch(elem, vnode)patch(vnode, newVnode)
  • patchVnodeaddVnodesremoveVnodes
  • updateChildrenkey 很重要
  1. Vue 爲何是異步渲染,$nextTick 何用,答案如下所示:
  • 異步渲染以及合併 data 修改,以提高渲染性能
  • $nextTickDOM 更新完成之後,觸發回調
  1. Vue 常見的性能優化方式,答案如下所示:
  • 合理使用 v-showv-if
  • 合理使用 computed
  • v-for 時加 key,以及避免和 v-if 同時使用
  • 自定義事件、DOM 事件以及銷燬
  • 合理使用異步組件
  • 合理使用 keep-alive
  • data 層級不要太深
  • 使用 vue-loader 在開發環境做模版編譯,預編譯
  • webpack 層面的優化
  • 前端通用的性能優化,如圖片懶加載
  • 使用 SSR

二、Vue3 的相關考點

  1. Vue3 雖然尚未發佈,還在開發中,但是還是很重要的,在面試中也還是會考察的。Vue3 中升級的內容,如下所示:
  • 全部用 ts 重寫,響應式、vdom、模版編譯等
  • 性能提升,代碼量減少
  • 會調整部分 API
  1. Vue2.x 馬上就要過時了嗎,答案如下所示:
  • Vue3 從正式發佈到推廣開來,還需要一段時間
  • Vue2.x 應用範圍非常廣,有大量項目需要維護、升級
  • Proxy 存在瀏覽器兼容性問題,且不能 polyfill
  • Vue3 比較重要的一點,就是重寫響應式
  1. Object.defineProperty 的缺點,如下所示:
  • 深度監聽需要一次性遞歸
  • 無法監聽新增屬性和刪除屬性,Vue.setVue.delete
  • 無法原生監聽數組,需要特殊處理
  1. Proxy 實現響應式,基本使用、Reflect 和 實現響應式。對於 Reflect 的作用,和 Proxy 能力一一對應,規範化、標準化、函數式,替代掉 Object 上的工具函數。

  2. Proxy 實現響應式,深度監聽,性能更好,可監聽新增和刪除屬性,可監聽數組變化。Proxy 能夠規避 Object.defineProperty 的問題,Proxy 無法兼容所有瀏覽器,無法 polyfill

  3. 關於響應式的相關代碼,如下所示:

  • observe.js
// 觸發更新視圖
function updateView() {
    console.log('視圖更新')
}

// 重新定義數組原型
const oldArrayProperty = Array.prototype
// 創建新對象,原型指向 oldArrayProperty ,再擴展新的方法不會影響原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
    arrProto[methodName] = function () {
        updateView() // 觸發視圖更新
        oldArrayProperty[methodName].call(this, ...arguments)
        // Array.prototype.push.call(this, ...arguments)
    }
})

// 重新定義屬性,監聽起來
function defineReactive(target, key, value) {
    // 深度監聽
    observer(value)

    // 核心 API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                // 深度監聽
                observer(newValue)

                // 設置新值
                // 注意,value 一直在閉包中,此處設置完之後,再 get 時也是會獲取最新的值
                value = newValue

                // 觸發更新視圖
                updateView()
            }
        }
    })
}

// 監聽對象屬性
function observer(target) {
    if (typeof target !== 'object' || target === null) {
        // 不是對象或數組
        return target
    }

    // 污染全局的 Array 原型
    // Array.prototype.push = function () {
    //     updateView()
    //     ...
    // }

    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }

    // 重新定義各個屬性(for in 也可以遍歷數組)
    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}

// 準備數據
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: '北京' // 需要深度監聽
    },
    nums: [10, 20, 30]
}

// 監聽數據
observer(data)

// 測試
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增屬性,監聽不到 —— 所以有 Vue.set
// delete data.name // 刪除屬性,監聽不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度監聽
data.nums.push(4) // 監聽數組

  • proxy.js
// const data = {
//     name: 'zhangsan',
//     age: 20,
// }
const data = ['a', 'b', 'c']

const proxyData = new Proxy(data, {
    get(target, key, receiver) {
        // 只處理本身(非原型的)屬性
        const ownKeys = Reflect.ownKeys(target)
        if (ownKeys.includes(key)) {
            console.log('get', key) // 監聽
        }

        const result = Reflect.get(target, key, receiver)
        return result // 返回結果
    },
    set(target, key, val, receiver) {
        // 重複的數據,不處理
        if (val === target[key]) {
            return true
        }

        const result = Reflect.set(target, key, val, receiver)
        console.log('set', key, val)
        // console.log('result', result) // true
        return result // 是否設置成功
    },
    deleteProperty(target, key) {
        const result = Reflect.deleteProperty(target, key)
        console.log('delete property', key)
        // console.log('result', result) // true
        return result // 是否刪除成功
    }
})


  • proxy-observe.js
// 創建響應式
function reactive(target = {}) {
    if (typeof target !== 'object' || target == null) {
        // 不是對象或數組,則返回
        return target
    }

    // 代理配置
    const proxyConf = {
        get(target, key, receiver) {
            // 只處理本身(非原型的)屬性
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('get', key) // 監聽
            }
    
            const result = Reflect.get(target, key, receiver)
        
            // 深度監聽
            // 性能如何提升的?
            return reactive(result)
        },
        set(target, key, val, receiver) {
            // 重複的數據,不處理
            if (val === target[key]) {
                return true
            }
    
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('已有的 key', key)
            } else {
                console.log('新增的 key', key)
            }

            const result = Reflect.set(target, key, val, receiver)
            console.log('set', key, val)
            // console.log('result', result) // true
            return result // 是否設置成功
        },
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            console.log('delete property', key)
            // console.log('result', result) // true
            return result // 是否刪除成功
        }
    }

    // 生成代理對象
    const observed = new Proxy(target, proxyConf)
    return observed
}

// 測試數據
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        city: 'beijing',
        a: {
            b: {
                c: {
                    d: {
                        e: 100
                    }
                }
            }
        }
    }
}

const proxyData = reactive(data)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章