最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端高頻面試題之Vue高級篇(含詳細代碼)

 更新時間:2026年05月25日 08:27:12   作者:程序員小寒  
Vue已經(jīng)成為企業(yè)項目開發(fā)的熱門框架,也是前端面試中常被問到的內(nèi)容,下面這篇文章主要介紹了前端高頻面試題之Vue高級篇的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

1、說一下 Vue.js 的響應式原理

1.1 Vue2 響應式原理

核心原理就是通過 Object.defineProperty 對對象屬性進行劫持,重新定義對象的 gettersetter,在 getter 取值時收集依賴,在 setter 修改值時觸發(fā)依賴更新,更新頁面。

Vue2 對數(shù)組和對象做了兩種不同方式的處理。

監(jiān)聽對象變化:

針對對象來說,Vue 會循環(huán)遍歷對象的每一個屬性,用 defineReactive 重新定義 gettersetter

function defineReactive(target,key,value){
    observer(value);
    Object.defineProperty(target,key,{ ?v
        get(){
            // ... 收集依賴邏輯
            return value;
        },
        set(newValue){
            if (value !== newValue) {
                value = newValue;
                observer(newValue) // 把新設置的值包裝成響應式
            }
            // ...觸發(fā)依賴更新邏輯
        }
    })
}
function observer(data) {
    if(typeof data !== 'object'){
        return data
    }
    for(let key in data){
        defineReactive(data,key,data[key]);
    }
}

監(jiān)聽數(shù)組變化:

我們都知道,數(shù)組其實也是對象,同樣可以用 Object.defineProperty 劫持數(shù)組的每一項,但如果數(shù)組有100萬項,那就要調(diào)用 Object.defineProperty 一百萬次,這樣的話性能太低了。鑒于平時我們操作數(shù)組大都是采用數(shù)組提供的原生方法,于是 Vue 對數(shù)組重寫原型鏈,在調(diào)用7個能改變自身的原生方法(push,pop,shiftunshift,splicesort,reverse)時,通知頁面進行刷新,具體實現(xiàn)過程如下:

// 先拿到數(shù)組的原型
const oldArrayProtoMethods = Array.prototype
// 用Object.create創(chuàng)建一個以oldArrayProtoMethods為原型的對象
const arrayMethods = Object.create(oldArrayProtoMethods)
const methods = [
    'push',
    'pop',
    'shift',
    'unshift',
    'sort',
    'reverse',
    'splice'
]
methods.forEach(method => {
    // 給arrayMethods定義7個方法
    arrayMethods[method] = function (...args){
        // 先找到數(shù)組對應的原生方法進行調(diào)用
        const result = oldArrayProtoMethods[method].apply(this, args)
        // 聲明inserted,用來保存數(shù)組新增的數(shù)據(jù)
        let inserted
        // __ob__是Observer類實例的一個屬性,data中的每個對象都是一個Observer類的實例
        const ob = this.__ob__
        switch(method) {
            case 'push':
            case 'unshift':
                inserted = args
                break
            case 'splice':
                inserted = args.slice(2)
            default:
                break
        }
        // 比如有新增的數(shù)據(jù),新增數(shù)據(jù)也要被定義為響應式
        if(inserted) ob.observeArray(inserted)
        // 通知頁面更新
        ob.dep.notify()
        return result
    }
})

Object.defineProperty的缺點:

  1. 無法監(jiān)聽新增屬性和刪除屬性的變化,需要通過 $set$delete 實現(xiàn)。
  2. 監(jiān)測數(shù)組的索引性能太低,故而直接通過數(shù)組索引改值無法觸發(fā)響應式。
  3. 初始化時需要一次性遞歸調(diào)用,性能較差。

1.2 Vue3 的響應式改進

Vue3 采用 Proxy + Reflect 配合實現(xiàn)響應式。能解決上述 Object.defineProperty 的所有缺陷,唯一缺點就是兼容性沒有 Object.defineProperty 好。

let handler = {
  get(target, key) {
    if (typeof target[key] === "object") {
      return new Proxy(target[key], handler);
    }
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    let oldValue = target[key];
    if (oldValue !== value) {
      return Reflect.set(target, key, value);
    }
    return true;
  },
};
let proxy = new Proxy(obj, handler);

2、介紹一下 Vue 中的 diff 算法?

Vue 的 diff 算法是平級比較,不考慮跨級比較的情況。內(nèi)部采用深度遞歸的方式 + 雙指針的方式進行比較。

比較過程:

  1. 先比較是否是相同節(jié)點。
  2. 相同節(jié)點比較屬性,并復用老節(jié)點。
  3. 比較兒子節(jié)點,考慮老節(jié)點和新節(jié)點兒子的情況。
  4. 優(yōu)化比較:頭頭、尾尾、頭尾、尾頭。
  5. 比對查找進行復用。

Vue3 在這個比較過程的基礎上增加了最長遞增子序列實現(xiàn)diff算法。

  • 找出不需要移動的現(xiàn)有節(jié)點。
  • 只對需要移動的節(jié)點進行操作。
  • 最小化 DOM 操作次數(shù)。

3、Vue 的模板編譯原理是什么?

Vue 中的模板編譯就是把我們寫的 template 轉換為渲染函數(shù)(render function) 的過程,它主要經(jīng)歷3個步驟:

  1. 解析(Parse):將 template 模板轉換成 ast 抽象語法樹。
  2. 優(yōu)化(Optimize):對靜態(tài)節(jié)點做靜態(tài)標記,減少 diff 過程中的比對。
  3. 生成(Generate):重新生成代碼,將 ast 抽象語法數(shù)轉化成可執(zhí)行的渲染函數(shù)代碼。

3.1 解析階段

<div id="app">
  <p>{{ message }}</p>
</div>
  • 用 HTML 解析器將模板解析為 AST。
  • AST中用 js 對象描述模板,里面包含了元素類型、屬性、子節(jié)點等信息。
  • 解析指令(v-for、v-if)和事件(@click)、插值表達式{{}}等 vue 語法。

3.2 優(yōu)化階段

  • 遍歷上一步生成的 ast,標記靜態(tài)節(jié)點,比如用 v-once 的節(jié)點,以及沒有用到響應式數(shù)據(jù)的節(jié)點。
  • 標記靜態(tài)根節(jié)點,避免不必要的渲染。

3.3 代碼生成階段

vue2 解析結果:

function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('p', [_v(_s(message))])])
  }
}
  • _c: 是 createElement 的別名,用于創(chuàng)建 VNode。
  • _v: 創(chuàng)建文本 VNode。
  • _s: 是 toString 的別名,用于將值轉換為字符串。

vue3 解析結果:

import { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", { id: "app" }, [
    _createElementVNode("p", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ]))
}
  • _openBlock: 開啟一個"block"區(qū)域,用于收集動態(tài)子節(jié)點。
  • _createElementBlock: 創(chuàng)建一個塊級虛擬 DOM 節(jié)點。
  • _createElementVNode: 創(chuàng)建一個普通虛擬 DOM 節(jié)點。
  • _toDisplayString: 將響應式數(shù)據(jù) _ctx.message 轉換為顯示字符串,或者處理 null/undefined 等值,確保它們能正確渲染為空白字符串。

vue2在線編譯:template-explorer.vuejs.org/。

vue3在線編譯:v2.template-explorer.vuejs.org/。

運行時+編譯(runtime-compiler) vs 僅運行時(runtime-only):

  1. 完整版(運行時+編譯):
    • 包含編譯模塊,可以寫 template 模版。
    • 體積較大(~30kb)。
  2. 僅運行時版本
    • 需要在打包時使用 vue-loader 進行編譯。
    • 體積較?。ā?0kb)。

平時開發(fā)項目推薦使用僅運行時(runtime-only)版本。

編譯后的特點:

  1. 虛擬DOM:渲染函數(shù)生成的是虛擬DOM節(jié)點(VNode)。
  2. 響應式綁定:渲染函數(shù)中的變量會自動建立依賴關系。
  3. 性能優(yōu)化:通過靜態(tài)節(jié)點標記減少不必要的更新。

4、v-show 和 v-if 的原理

簡單來說,v-if 內(nèi)部是通過一個三元表達式來實現(xiàn)的,而 v-show 則是通過控制 DOM 元素的 display 屬性來實現(xiàn)的。

v-if 源碼:

function genIfConditions (
    conditions: ASTIfConditions,
    state: CodegenState,
    altGen?: Function,
    altEmpty?: string
    ): string {
    if (!conditions.length) {
        return altEmpty || '_e()'
    }
    const condition = conditions.shift()
    if (condition.exp) {   // 如果有表達式
        return `(${condition.exp})?${ // 將表達式作為條件拼接成元素
        genTernaryExp(condition.block)
        }:${
        genIfConditions(conditions, state, altGen, altEmpty)
        }`
    } else {
        return `${genTernaryExp(condition.block)}` // 沒有表達式直接生成元素 像v-else
    }

    // v-if with v-once should generate code like (a)?_m(0):_m(1)
    function genTernaryExp (el) {
        return altGen
        ? altGen(el, state)
        : el.once
            ? genOnce(el, state)
            : genElement(el, state)
    }
}

v-show 源碼:

{
    bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
    const originalDisplay = el.__vOriginalDisplay =
        el.style.display === 'none' ? '' : el.style.display // 獲取原始顯示值
        el.style.display = value ? originalDisplay : 'none' // 根據(jù)屬性控制顯示或者隱藏
    }  
} 

5、v-if 和 v-for 哪個優(yōu)先級更高?為什么?

  • vue2 中 v-for 的優(yōu)先級比 v-if 高,它們作用于一個節(jié)點上會導致先循環(huán)后對每一項進行判斷,浪費性能。
  • vue3 中 v-if 的優(yōu)先級比 v-for 高,這就會導致 v-if 中的條件無法訪問 v-for 作用域名中定義的變量別名。
<li v-for="item in arr" v-if="item.visible">
  {{ item}}
</li>

以上代碼在 vue3 的編譯結果如下:

import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, toDisplayString as _toDisplayString, createCommentVNode as _createCommentVNode } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_ctx.item.visible)
    ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(_ctx.arr, (item) => {
        return (_openBlock(), _createElementBlock("li", null, _toDisplayString(item), 1 /* TEXT */))
      }), 256 /* UNKEYED_FRAGMENT */))
    : _createCommentVNode("v-if", true)
}

可以看出 vue3 在編譯時會先判斷 v-if,然后再走 v-for 的循環(huán),所以在 v-if 中自然就無法訪問 v-for 作用域名中定義的變量別名。

這樣的寫法在 vue3 中會拋出一個警告??,[Vue warn]: Property "item" was accessed during render but is not defined on instance,導致渲染失敗。

以上代碼在 vue2 還不能直接編譯,因為 vue2 的組件需要一個根節(jié)點,所以我們在外層加一個 div

<div>
  <li v-for="item in arr" v-if="item.visible">
    {{ item}}
  </li>
</div>

其編譯結果如下:

function render() {
  with(this) {
    return _c('div', _l((arr), function (item) {
      return (item.visible) ? _c('li', [_v("\n    " + _s(item) + "\n  ")]) :
        _e()
    }), 0)
  }
}

很明顯是先循環(huán) arr,然后每一項再用 item.visible 去判斷的,也印證了在 vue2 中, v-for 的優(yōu)先級高于 v-if。

所以不管是 vue2 還是 vue3,都不推薦同時使用 v-ifv-for,更好的方案是采用計算屬性,或者在外層再包裹一個容器元素,將 v-if 作用在容器元素上。

6、nextTick 的原理是什么?

6.1 Vue2 的 nextTick:

  • 首選微任務:
    • Promise.resolve().then(flushCallbacks):最常見,使用 Promise 創(chuàng)建微任務。
    • MutationObserver:如果 Promise 不可用,創(chuàng)建一個文本節(jié)點,修改其內(nèi)容觸發(fā) MutationObserver 的觀察器回調(diào)。
  • 回退宏任務:
    • setImmediate:如果環(huán)境支持 setImmediate,比如 node 環(huán)境,則會優(yōu)先使用 setImmediate 。
    • setTimeout(flushCallbacks, 0):最后使用定時器。

這里體現(xiàn)了優(yōu)雅降級的思想。

6.2 Vue3 的 nextTick:

  • 由于 Vue3 不再考慮 promise 的兼容性,所以 nextTick 的實現(xiàn)原理就是 promise.then 方法。

7、Vue.set 方法是如何實現(xiàn)的?

Vue2的實現(xiàn):在 Vue 2 中,Vue.set 的實現(xiàn)主要位于 src/core/observer/index.js 中:

export function set (target: Array | Object, key: any, val: any): any {
    // 1.如果是數(shù)組 Vue.set(array,1,100); 調(diào)用我們重寫的splice方法 (這樣可以更新視圖)
    if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, val)
        return val
    }
    // 2.如果是對象本身的屬性,則直接添加即可
    if (key in target && !(key in Object.prototype)) {
        target[key] = val
        return val
    }
    const ob = (target: any).__ob__
    // 3.如果是響應式的也不需要將其定義成響應式屬性
    if (!ob) {
        target[key] = val
        return val
    }
    // 4.將屬性定義成響應式的
    defineReactive(ob.value, key, val)
    // 5.通知視圖更新
    ob.dep.notify()
    return val
}

Vue3 中 set 方法已經(jīng)被移除,因為 proxy 天然彌補 vue2 響應式的缺陷。

8、Vue.use 是干什么的?原理是什么?

Vue.use 是用來使用插件的,我們可以在插件中擴展全局組件、指令、原型方法等。

Vue.use 源碼:

Vue.use = function (plugin: Function | Object) {
    // 插件不能重復的加載
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
        return this
    }
    // additional parameters
    const args = toArray(arguments, 1)
    args.unshift(this)  // install方法的第一個參數(shù)是Vue的構造函數(shù),其他參數(shù)是Vue.use中除了第一個參數(shù)的其他參數(shù)
    if (typeof plugin.install === 'function') { // 調(diào)用插件的install方法
        plugin.install.apply(plugin, args)  Vue.install = function(Vue,args){}
    } else if (typeof plugin === 'function') { // 插件本身是一個函數(shù),直接讓函數(shù)執(zhí)行
        plugin.apply(null, args) 
    }
    installedPlugins.push(plugin) // 緩存插件
    return this
}

9、介紹下 Vue 中的 mixin,Vue3 為何不再推薦使用它?

mixin 是 Vue 2 中一種復用組件邏輯的方式,允許將可復用的配置(data、methods、computed、lifecycle hooks 等)抽離成一個對象,然后通過 mixins: [] 合并到組件中。支持全局注入和局部注入。

  • 作用:抽離公共的業(yè)務邏輯
  • 原理:類似“對象的繼承”,當組件初始化時會調(diào)用 mergeOptions 方法進行合并,采用策略模式針對不同的屬性進行合并。如果混入的數(shù)據(jù)和本身組件中的數(shù)據(jù)沖突,會采用“就近原則”以組件的數(shù)據(jù)為準。

mixin 的優(yōu)點:

  • 復用邏輯(如表單驗證、權限判斷)。
  • 全局注入(如日志、埋點)。
  • 減少重復代碼。

mixin 中有很多缺陷:

  • 命名沖突問題:mixin 中的變量、函數(shù)名可能會與組件中的重名。
  • 依賴問題:
  • 數(shù)據(jù)來源問題:

vue3 不再推薦使用它的理由如下:

問題說明
1. 隱式依賴 & 數(shù)據(jù)來源不明確組件行為來自多個 mixin,難以追蹤 data、methods 是從哪里來的。
2. 命名沖突多個 mixin 可能定義同名 data、methods,合并規(guī)則復雜(同名 methods 后者覆蓋前者,data 合并為對象,同名 Key 后者覆蓋前者)。
3. 調(diào)試和維護困難父組件無法知道子組件內(nèi)部有哪些 mixin 注入的屬性,排查 bug 和調(diào)試困難。
4. 不利于 Tree-shaking打包時難以移除未使用的 mixin 代碼。
5. 與 Composition API 理念沖突Mixin 是“橫切關注點”,而 Composition API 強調(diào)顯式、可組合的邏輯。

Vue 3 推薦替代方案:Composition API + 可復用函數(shù)(Composables)。

特性MixinComposables
數(shù)據(jù)來源明確隱式顯式(import)
是否有命名沖突問題
邏輯封裝全局污染按需引入
Tree-shaking 支持
TypeScript 支持

對于全局混入(Global Mixin),Vue3 雖然提供了 app.mixin(),但不推薦,推薦使用:

  1. app.config.globalProperties。
  2. app.provide 在頂層提供數(shù)據(jù),組件通過 inject 方法消費數(shù)據(jù)。

10、介紹下 Vue.js 中的函數(shù)式組件、異步組件和遞歸組件

10.1 函數(shù)式組件(Functional Components)

函數(shù)式組件是一種輕量級、無狀態(tài)的組件形式。它們很像純函數(shù):接收 props,返回 虛擬 DOM(vnode)。函數(shù)式組件在渲染過程中不會創(chuàng)建組件實例 (也就是說,沒有 this),也不會觸發(fā)常規(guī)的組件生命周期鉤子。沒有響應式系統(tǒng)、生命周期和實例的開銷,函數(shù)式組件自然在渲染上更加高效和快速。

總而言之,函數(shù)式組件有無狀態(tài)、無this、無生命周期、性能更高等特點。

使用場景:適合簡單、靜態(tài)的 UI 元素,如列表項或包裝組件。

在 Vue 2 中,通過 functional: true 聲明;在 Vue 3 中,函數(shù)式組件更簡單,直接返回渲染函數(shù)。

Vue2 示例:

<template functional>
  <div>{{ props.msg }}</div>
</template>

或者 js 形式:

export default {
  functional: true,
  props: ['msg'],
  render(h, { props }) {
    return h('div', props.msg);
  }
};

Vue3 示例:

<script setup>
import { h } from 'vue';

const FunctionalComp = (props) => h('div', props.msg);
</script>

<template>
  <FunctionalComp msg="Hello Functional" />
</template>

10.2 異步組件(Async Components)

異步組件是一種懶加載(Lazy Loading)機制,用于按需加載組件代碼,優(yōu)化初始加載時間和性能。Vue 會動態(tài)導入組件,只有在使用時才下載和渲染,常用于路由或大型組件。

特點:

  • 通過 import() 動態(tài)加載,返回 Promise。
  • 支持加載中(loading)、錯誤(error)和超時(timeout)處理。
  • 在 Vue 3 中,使用 defineAsyncComponent 更規(guī)范,支持與 <Suspense> 結合(Vue 3 獨有,用于統(tǒng)一處理異步)。

Vue2 示例:

<script>
export default {
  components: {
    AsyncComp: () => import('./AsyncComp.vue')
  }
};
</script>

<template>
  <AsyncComp />
</template>

Vue3 示例:

<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'));
</script>
<template>
  <Suspense>
    <template #default>
      <AsyncComp />
    </template>
    <template #fallback>加載中...</template>
  </Suspense>
</template>

10.3 遞歸組件(Recursive Components)

遞歸組件是指組件內(nèi)部調(diào)用自身,用于處理樹形或嵌套數(shù)據(jù)結構,如菜單、樹視圖或評論回復。Vue 支持組件自引用,但需注意避免無限循環(huán)(通過條件終止遞歸)。

特點:

  • 組件需有名稱(name 選項),才能自引用。
  • 常結合 v-for 和 props 傳遞數(shù)據(jù)。

Vue 2 示例:

<template>
  <ul>
    <li v-for="item in tree" :key="item.id">
      {{ item.name }}
      <Tree v-if="item.children" :tree="item.children" />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'Tree',  // 必須有 name
  props: ['tree']
};
</script>

Vue 3 示例:

<script setup>
import { defineAsyncComponent } from 'vue';  // 可選:異步加載避免循環(huán)

const Tree = defineAsyncComponent(() => import('./Tree.vue'));  // 自引用
defineProps(['tree']);
</script>

<template>
  <ul>
    <li v-for="item in tree" :key="item.id">
      {{ item.name }}
      <Tree v-if="item.children" :tree="item.children" />
    </li>
  </ul>
</template>

11、Vue.js 中的 vue-loader 是什么?

Vue-loader 是一個專為 Vue.js 設計的 Webpack loader(加載器),其主要作用是將 Vue 的單文件組件(Single-File Components,簡稱 SFC,即 .vue 文件)轉換為可執(zhí)行的 JavaScript 模塊。 它允許開發(fā)者以一種結構化的方式編寫組件,將模板(template)、腳本(script)和樣式(style)封裝在同一個文件中,便于管理和維護。

核心功能:

  • 解析 SFC 文件:Vue-loader 會自動處理 .vue 文件中的三個部分:
    • template 部分:編譯為 Vue 的渲染函數(shù)(render function)。
    • script 部分:提取為組件的 JavaScript 邏輯,支持 ES 模塊和 TypeScript。
    • style部分:處理 CSS,支持預處理器(如 Sass、Less)并可選地應用 scoped(作用域樣式)或 CSS Modules。
  • 熱重載(Hot Module Replacement,HMR):在開發(fā)模式下,支持組件的熱更新,無需刷新頁面即可看到變化,提高開發(fā)效率。
  • 自定義塊(Custom Blocks):支持擴展,如添加 <docs> 或其他自定義標簽,用于文檔生成或其他工具集成。
  • 預處理器支持:無縫集成 Babel、PostCSS 等工具鏈。

12、Vue.extend 方法的作用?

Vue.extend方法可以作為基礎 Vue 構造器,創(chuàng)建一個“子類”。參數(shù)是一個包含組件選項的對象。

Vue2 示例:

var dialog = Vue.extend({
  template: "<div>{{hello}} {{world}}</div>",
  data: function () {
    return {
      hello: "hello",
      world: "world",
    };
  },
});
// 創(chuàng)建 dialog 實例,并手動掛載到一個元素上。
new dialog().$mount("#app");

注意:在 Vue.extend 中的 data 必須是一個函數(shù),要不然會報錯。

Vue3 示例:

Vue3 中不在使用 Vue.extend 方法,而是采用render方法進行手動渲染。

<!-- Modal.vue -->
<template>
  <div class="modal">這是一個彈窗</div>
</template>

<script>
export default {
  name: 'Modal',
}
</script>
<template>
  <div id="box"></div>
</template>

<script setup>
import { h, render, createApp, onMounted } from 'vue'
import Modal from './Modal.vue'

onMounted(() => {
  render(h(Modal), document.getElementById('box'));
})
</script>

13、keep-alive 的原理

<keep-alive> 是 Vue.js 的內(nèi)置組件,它的功能是在多個組件間動態(tài)切換時緩存被移除的組件實例,避免重復渲染和狀態(tài)丟失,提高性能。它是一個抽象組件(abstract: true),不會渲染到 DOM,也不會出現(xiàn)在組件樹中,而是通過插槽(slots)和自定義 render 函數(shù)實現(xiàn)緩存邏輯。

核心實現(xiàn)機制:

  1. 抽象組件與 Render 函數(shù):
  • <keep-alive> 通過 render 函數(shù)處理包裹的內(nèi)容(通常是動態(tài)組件,如 <component> 或 v-if 切換的組件)。
  • 在 render 中,它從插槽獲取子組件的 VNode(虛擬節(jié)點)。如果子組件有 key(推薦使用),則用 key 作為緩存標識;否則用組件的 tag 或 cid(組件 ID)。
  • 如果組件已緩存,直接返回緩存的 VNode(設置 vnode.componentInstance.keepAlive = true 以標記緩存狀態(tài));否則,渲染新實例并存入緩存。
  1. 緩存存儲:
  • 內(nèi)部使用一個對象(this.cache)作為緩存 Map,以 key 為鍵,值為 VNode 對象(包含組件實例)。
  • 當組件首次渲染時,存入緩存;切換回時,從緩存取出,避免重新創(chuàng)建實例和執(zhí)行 mounted 鉤子。
  1. LRU 緩存算法:
  • 支持 max 屬性設置最大緩存數(shù)量(Vue 2.5+)。
  • 使用 Least Recently Used(最近最少使用)算法:當緩存超出 max 時,刪除最久未訪問的組件(通過 this.keys 數(shù)組跟蹤訪問順序)。
  • 訪問組件時,將其 key 移到數(shù)組末尾(最近使用);超出時,刪除數(shù)組開頭的 key,并銷毀對應實例(調(diào)用 $destroy)。
  1. 過濾規(guī)則(include/exclude):
  • 通過 include(白名單)和 exclude(黑名單)屬性決定哪些組件緩存,支持字符串、正則、數(shù)組或函數(shù)。
  • 在 created 鉤子中,監(jiān)聽這些 prop 的變化,并調(diào)用 pruneCache 更新緩存(移除不匹配的組件)。
  1. 生命周期鉤子:
  • 緩存組件不會觸發(fā) destroyed/unmounted,而是使用 activated(激活時)和 deactivated(失活時)鉤子。
  • 這允許開發(fā)者在切換時管理狀態(tài)(如暫停定時器),而非完全銷毀。

注意事項:

  • 只緩存一個直接子組件(插槽內(nèi)容),不支持多個。
  • Vue 3 中原理類似,但優(yōu)化了 VNode 處理和 Composition API 支持。
  • 潛在問題:緩存過多導致內(nèi)存占用;需手動清理資源(如在 deactivated 中停止監(jiān)聽)。

14、Vue.js 中使用了哪些設計模式?

1. 觀察者模式 (Observer Pattern)

  • 描述:Vue 的響應式系統(tǒng)使用觀察者模式,通過 Proxy (Vue 3) 或 Object.defineProperty (Vue 2) 攔截對象屬性的 get/set 操作。當數(shù)據(jù)變化時,通知訂閱者(Watcher)更新視圖。
  • 應用:在 reactive() 函數(shù)中,返回 Proxy 對象,get 陷阱用于依賴收集 (track),set 陷阱用于觸發(fā)更新 (trigger)。
  • 優(yōu)勢:實現(xiàn)了細粒度的變更檢測,避免全局重渲染。

2. 發(fā)布-訂閱模式 (Publish-Subscribe Pattern)

  • 描述:Vue 的事件系統(tǒng)和響應式通知機制采用 Pub-Sub 模式。數(shù)據(jù)變化時發(fā)布事件,訂閱者(如組件渲染函數(shù))接收并響應。
  • 應用:在響應式系統(tǒng)中,trigger() 函數(shù)檢索訂閱者效果并調(diào)用它們;事件 API 如 emit和emit 和 emiton 也基于此。
  • 優(yōu)勢:解耦了數(shù)據(jù)生產(chǎn)者和消費者,支持異步更新。

3. 代理模式 (Proxy Pattern)

描述:Vue 3 的響應式系統(tǒng)直接使用 ES6 Proxy 作為代理層,攔截對象操作,實現(xiàn)透明的響應式。 應用:reactive() 返回 Proxy 對象,代理目標對象的訪問和修改。 優(yōu)勢:比 Vue 2 的 defineProperty 更強大,支持數(shù)組和 Map/Set 等類型。

4. 策略模式 (Strategy Pattern)

  • 描述:Vue 的虛擬 DOM diff 算法使用不同策略(如 key-based diff 或簡單 patch)來優(yōu)化更新。
  • 應用:在渲染過程中,根據(jù)節(jié)點類型選擇 diff 策略。
  • 優(yōu)勢:最小化 DOM 操作,提高渲染效率。

5. 單例模式(Singleton Pattern)

  • 描述:整個程序中有且僅有一個實例。
  • 應用:vuex 的 store 和插件。
  • 優(yōu)勢:全局唯一、節(jié)約資源、便于管理。

6. 工廠模式(Factory Pattern)

  • 描述:提供了一種創(chuàng)建對象的方式,使得創(chuàng)建對象的過程與使用對象的過程分離。
  • 應用:Vue2 中的組件創(chuàng)建,傳入?yún)?shù)給 createComponentInstance 就可以創(chuàng)建實例。
  • 優(yōu)勢:解藕,易于維護。

15、Vue.js 應用中常見的內(nèi)存泄漏來源有哪些?

  1. 未清理的事件監(jiān)聽器、定時器、動畫
<script setup>
import { onMounted, onUnmounted } from 'vue';

let timer = null;
let controller = null;
let raf = null;

onMounted(() => {
  // 定時器
  timer = setInterval(() => {}, 1000);
  // 動畫
  raf = requestAnimationFrame(() => {});
  // 事件監(jiān)聽
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  clearInterval(timer);
  cancelAnimationFrame(this.raf);
  window.removeEventListener('resize', handleResize);
});
</script>
  1. 未移除的第三方庫實例
<script setup>
import { onMounted, onUnmounted } from 'vue';

let chart = null;

onMounted(() => {
  chart = echarts.init(this.$refs.chart);
});

onUnmounted(() => {
  chart?.dispose();
});
</script>
  1. 事件總線(Event Bus)未解綁

vue2 用可以用 new Vue 全局創(chuàng)建一個事件總線實例,或者在組件中直接使用 this.$on、this.$emit、this.$off。

vue3 則需要借助第三庫,比如 mitt 來實現(xiàn)事件總線。

<script setup>
import { onMounted, onUnmounted } from 'vue';
import mitt from 'mitt';

// 創(chuàng)建事件總線實例
const emitter = mitt();

onMounted(() => {
  emitter.on('update', this.handler);
});

onUnmounted(() => {
  emitter.off('update', this.handler);
});
</script>

順便提一下, vue3 為啥去掉 $on、$emit、$off 這些 API,主要有以下原因:

  1. 設計理念的調(diào)整

Vue 3 更加注重組件間通信的明確性和可維護性。$on 這類事件 API 本質(zhì)上是一種 "發(fā)布 - 訂閱" 模式,容易導致組件間關系模糊(多個組件可能監(jiān)聽同一個事件,難以追蹤事件來源和流向)。Vue 3 推薦使用更明確的通信方式,如:

  • 父子組件通過 props 和 emit 通信
  • 跨組件通信使用 provide/inject 或 Pinia/Vuex 等狀態(tài)管理庫
  • 復雜場景可使用專門的事件總線庫(如 mitt
  1. 與 Composition API 的適配

Vue 3 主推的 Composition API 強調(diào)邏輯的封裝和復用,而 $on 基于選項式 API 的實例方法,與 Composition API 的函數(shù)式思維不太契合。移除后,開發(fā)者可以更自然地使用響應式變量或第三方事件庫來實現(xiàn)類似功能。

  1. 減少潛在問題
  • $on 容易導致內(nèi)存泄漏(忘記解綁事件)
  • 事件名稱可能沖突(全局事件總線尤其明顯)
  • 不利于 TypeScript 類型推斷,難以實現(xiàn)類型安全

4. 未清理的 Watcher

Vue 本身不會泄漏內(nèi)存,泄漏幾乎都來自開發(fā)者未清理的副作用。養(yǎng)成“創(chuàng)建即清理”的習慣,使用 beforeDestroy 或者 onUnmounted 集中清理,在使用 keep-alive 的組件中,視情況在 deactivated 鉤子中清理資源。

16、Vue.js 中的性能優(yōu)化手段有哪些?

16.1 數(shù)據(jù)相關

  • Vue2 中數(shù)據(jù)層級不易過深,合理設置響應式數(shù)據(jù);
  • Vue2 非響應式數(shù)據(jù)可以通過 Object.freeze()方法凍結屬性;
  • 合理使用 computed,利用其緩存能力提高性能。

16.2 組件相關

  • 控制組件粒度(Vue 采用組件級更新);
  • 合適場景可使用函數(shù)式組件(函數(shù)式組件開銷低);
  • 采用異步組件(借助構建工具的分包的能力,減少主包體積);
  • 在組件卸載或者非激活狀態(tài)及時清除定時器、DOM事件、事件總線、三方庫的實例等。
  • v-on 按需監(jiān)聽、使用動態(tài) watch 和及時銷毀 watch。

16.3 渲染相關

  • 合理設置 key 屬性;
  • v-show 和 v-if 的選取;
  • 使用防抖、節(jié)流、分頁、虛擬滾動、時間分片等策略;
  • 合理使用 keep-alive 、v-once、v-memo 進行邏輯優(yōu)化。

結語

以上是整理的 Vue 高級的高頻面試題,如有錯誤或者可以優(yōu)化的地方歡迎評論區(qū)指正,后續(xù)還會更新 Vuex 和 Vue-router 相關面試題。

到此這篇關于前端高頻面試題之Vue高級篇的文章就介紹到這了,更多相關前端Vue高頻面試題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • el-table渲染慢卡頓問題最優(yōu)解決方案

    el-table渲染慢卡頓問題最優(yōu)解決方案

    本文主要介紹了el-table渲染慢卡頓問題最優(yōu)解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • 如何解決el-checkbox選中狀態(tài)更改問題

    如何解決el-checkbox選中狀態(tài)更改問題

    這篇文章主要介紹了如何解決el-checkbox選中狀態(tài)更改問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 詳解vue computed的緩存實現(xiàn)原理

    詳解vue computed的緩存實現(xiàn)原理

    這篇文章主要介紹了vue computed的緩存實現(xiàn)原理,幫助大家更好的理解和學習使用vue,感興趣的朋友可以了解下
    2021-04-04
  • vue日歷組件的封裝方法

    vue日歷組件的封裝方法

    這篇文章主要為大家詳細介紹了vue封裝一個日歷組件的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue中transition組件在項目中運用小結

    vue中transition組件在項目中運用小結

    這篇文章主要介紹了vue中transition組件在項目中運用,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12
  • Vue實現(xiàn)hash模式網(wǎng)址方式(就是那種帶#的網(wǎng)址、井號url)

    Vue實現(xiàn)hash模式網(wǎng)址方式(就是那種帶#的網(wǎng)址、井號url)

    這篇文章主要介紹了Vue實現(xiàn)hash模式網(wǎng)址方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue實現(xiàn)移動端適方案的完整步驟

    vue實現(xiàn)移動端適方案的完整步驟

    現(xiàn)在的手機五花八門,造就了移動端窗口分辨率繁多的局面,在不同分辨率的屏幕下保持與UI圖一致的效果,就成了讓前端不得不頭疼的問題,下面這篇文章主要給大家介紹了vue實現(xiàn)移動端適方案的相關資料,需要的朋友可以參考下
    2022-10-10
  • 前端構建工具Webpack、Vite區(qū)別有哪些

    前端構建工具Webpack、Vite區(qū)別有哪些

    Webpack和Vite是兩種主流的前端構建工具,它們在功能、性能和使用場景上有所不同,Webpack提供豐富的功能和配置,適合大型復雜項目,但可能導致啟動和構建速度較慢,Vite基于ES模塊,支持快速的熱替換,適合小型或中等項目,需要的朋友可以參考下
    2024-10-10
  • Vue前端通過Get和Post方法調(diào)用后臺接口下載文件的應用實例

    Vue前端通過Get和Post方法調(diào)用后臺接口下載文件的應用實例

    Vue調(diào)用接口的具體實現(xiàn)方式有多種,這篇文章主要介紹了Vue前端通過Get和Post方法調(diào)用后臺接口下載文件的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-06-06
  • Vue文件如何轉換成base64并去除多余的文件類型前綴

    Vue文件如何轉換成base64并去除多余的文件類型前綴

    這篇文章主要介紹了Vue文件如何轉換成base64并去除多余的文件類型前綴問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03

最新評論

永丰县| 闽清县| 兴国县| 旬邑县| 安溪县| 钟山县| 隆安县| 久治县| 依安县| 冷水江市| 科技| 湘西| 房产| 瑞丽市| 洛南县| 武安市| 霍林郭勒市| 沁源县| 宁陕县| 延川县| 禹城市| 五常市| 房山区| 澄江县| 从化市| 大连市| 承德市| 德兴市| 黎平县| 高淳县| 南雄市| 梓潼县| 连江县| 枣阳市| 潞西市| 鄯善县| 东乌珠穆沁旗| 鄂尔多斯市| 老河口市| 呼伦贝尔市| 鄄城县|