12道vue高頻原理面試題,你能答出幾道
前言
本文分享 12 道 vue 高頻原理面試題,覆蓋了 vue 核心實(shí)現(xiàn)原理,其實(shí)一個(gè)框架的實(shí)現(xiàn)原理一篇文章是不可能說(shuō)完的,希望通過(guò)這 12 道問(wèn)題,讓讀者對(duì)自己的 Vue 掌握程度有一定的認(rèn)識(shí)(B 數(shù)),從而彌補(bǔ)自己的不足,更好的掌握 Vue ❤️
1. Vue 響應(yīng)式原理

核心實(shí)現(xiàn)類(lèi):
Observer : 它的作用是給對(duì)象的屬性添加 getter 和 setter,用于依賴(lài)收集和派發(fā)更新
Dep : 用于收集當(dāng)前響應(yīng)式對(duì)象的依賴(lài)關(guān)系,每個(gè)響應(yīng)式對(duì)象包括子對(duì)象都擁有一個(gè) Dep 實(shí)例(里面 subs 是 Watcher 實(shí)例數(shù)組),當(dāng)數(shù)據(jù)有變更時(shí),會(huì)通過(guò) dep.notify()通知各個(gè) watcher。
Watcher : 觀察者對(duì)象 , 實(shí)例分為渲染 watcher (render watcher),計(jì)算屬性 watcher (computed watcher),偵聽(tīng)器 watcher(user watcher)三種
Watcher 和 Dep 的關(guān)系
watcher 中實(shí)例化了 dep 并向 dep.subs 中添加了訂閱者,dep 通過(guò) notify 遍歷了 dep.subs 通知每個(gè) watcher 更新。
依賴(lài)收集
- initState 時(shí),對(duì) computed 屬性初始化時(shí),觸發(fā) computed watcher 依賴(lài)收集
- initState 時(shí),對(duì)偵聽(tīng)屬性初始化時(shí),觸發(fā) user watcher 依賴(lài)收集
- render()的過(guò)程,觸發(fā) render watcher 依賴(lài)收集
- re-render 時(shí),vm.render()再次執(zhí)行,會(huì)移除所有 subs 中的 watcer 的訂閱,重新賦值。
派發(fā)更新
- 組件中對(duì)響應(yīng)的數(shù)據(jù)進(jìn)行了修改,觸發(fā) setter 的邏輯
- 調(diào)用 dep.notify()
- 遍歷所有的 subs(Watcher 實(shí)例),調(diào)用每一個(gè) watcher 的 update 方法。
原理
當(dāng)創(chuàng)建 Vue 實(shí)例時(shí),vue 會(huì)遍歷 data 選項(xiàng)的屬性,利用 Object.defineProperty 為屬性添加 getter 和 setter 對(duì)數(shù)據(jù)的讀取進(jìn)行劫持(getter 用來(lái)依賴(lài)收集,setter 用來(lái)派發(fā)更新),并且在內(nèi)部追蹤依賴(lài),在屬性被訪問(wèn)和修改時(shí)通知變化。
每個(gè)組件實(shí)例會(huì)有相應(yīng)的 watcher 實(shí)例,會(huì)在組件渲染的過(guò)程中記錄依賴(lài)的所有數(shù)據(jù)屬性(進(jìn)行依賴(lài)收集,還有 computed watcher,user watcher 實(shí)例),之后依賴(lài)項(xiàng)被改動(dòng)時(shí),setter 方法會(huì)通知依賴(lài)與此 data 的 watcher 實(shí)例重新計(jì)算(派發(fā)更新),從而使它關(guān)聯(lián)的組件重新渲染。
一句話總結(jié):
vue.js 采用數(shù)據(jù)劫持結(jié)合發(fā)布-訂閱模式,通過(guò) Object.defineproperty 來(lái)劫持各個(gè)屬性的 setter,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)響應(yīng)的監(jiān)聽(tīng)回調(diào)
2. computed 的實(shí)現(xiàn)原理
computed 本質(zhì)是一個(gè)惰性求值的觀察者。
computed 內(nèi)部實(shí)現(xiàn)了一個(gè)惰性的 watcher,也就是 computed watcher,computed watcher 不會(huì)立刻求值,同時(shí)持有一個(gè) dep 實(shí)例。
其內(nèi)部通過(guò) this.dirty 屬性標(biāo)記計(jì)算屬性是否需要重新求值。
當(dāng) computed 的依賴(lài)狀態(tài)發(fā)生改變時(shí),就會(huì)通知這個(gè)惰性的 watcher,
computed watcher 通過(guò) this.dep.subs.length 判斷有沒(méi)有訂閱者,
有的話,會(huì)重新計(jì)算,然后對(duì)比新舊值,如果變化了,會(huì)重新渲染。 (Vue 想確保不僅僅是計(jì)算屬性依賴(lài)的值發(fā)生變化,而是當(dāng)計(jì)算屬性最終計(jì)算的值發(fā)生變化時(shí)才會(huì)觸發(fā)渲染 watcher 重新渲染,本質(zhì)上是一種優(yōu)化。)
沒(méi)有的話,僅僅把 this.dirty = true。 (當(dāng)計(jì)算屬性依賴(lài)于其他數(shù)據(jù)時(shí),屬性并不會(huì)立即重新計(jì)算,只有之后其他地方需要讀取屬性的時(shí)候,它才會(huì)真正計(jì)算,即具備 lazy(懶計(jì)算)特性。)
3. computed 和 watch 有什么區(qū)別及運(yùn)用場(chǎng)景?
區(qū)別
computed 計(jì)算屬性 : 依賴(lài)其它屬性值,并且 computed 的值有緩存,只有它依賴(lài)的屬性值發(fā)生改變,下一次獲取 computed 的值時(shí)才會(huì)重新計(jì)算 computed 的值。
watch 偵聽(tīng)器 : 更多的是「觀察」的作用,無(wú)緩存性,類(lèi)似于某些數(shù)據(jù)的監(jiān)聽(tīng)回調(diào),每當(dāng)監(jiān)聽(tīng)的數(shù)據(jù)變化時(shí)都會(huì)執(zhí)行回調(diào)進(jìn)行后續(xù)操作。
運(yùn)用場(chǎng)景
運(yùn)用場(chǎng)景:
當(dāng)我們需要進(jìn)行數(shù)值計(jì)算,并且依賴(lài)于其它數(shù)據(jù)時(shí),應(yīng)該使用 computed,因?yàn)榭梢岳?computed 的緩存特性,避免每次獲取值時(shí),都要重新計(jì)算。
當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí),應(yīng)該使用 watch,使用 watch 選項(xiàng)允許我們執(zhí)行異步操作 ( 訪問(wèn)一個(gè) API ),限制我們執(zhí)行該操作的頻率,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)。這些都是計(jì)算屬性無(wú)法做到的。
4. 為什么在 Vue3.0 采用了 Proxy,拋棄了 Object.defineProperty?
Object.defineProperty 本身有一定的監(jiān)控到數(shù)組下標(biāo)變化的能力,但是在 Vue 中,從性能/體驗(yàn)的性?xún)r(jià)比考慮,尤大大就棄用了這個(gè)特性(Vue 為什么不能檢測(cè)數(shù)組變動(dòng) )。為了解決這個(gè)問(wèn)題,經(jīng)過(guò) vue 內(nèi)部處理后可以使用以下幾種方法來(lái)監(jiān)聽(tīng)數(shù)組
push(); pop(); shift(); unshift(); splice(); sort(); reverse();
由于只針對(duì)了以上 7 種方法進(jìn)行了 hack 處理,所以其他數(shù)組的屬性也是檢測(cè)不到的,還是具有一定的局限性。
Object.defineProperty 只能劫持對(duì)象的屬性,因此我們需要對(duì)每個(gè)對(duì)象的每個(gè)屬性進(jìn)行遍歷。Vue 2.x 里,是通過(guò) 遞歸 + 遍歷 data 對(duì)象來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的監(jiān)控的,如果屬性值也是對(duì)象那么需要深度遍歷,顯然如果能劫持一個(gè)完整的對(duì)象是才是更好的選擇。Proxy 可以劫持整個(gè)對(duì)象,并返回一個(gè)新的對(duì)象。Proxy 不僅可以代理對(duì)象,還可以代理數(shù)組。還可以代理動(dòng)態(tài)增加的屬性。
5. Vue 中的 key 到底有什么用?
key 是給每一個(gè) vnode 的唯一 id,依靠 key,我們的 diff 操作可以更準(zhǔn)確、更快速 (對(duì)于簡(jiǎn)單列表頁(yè)渲染來(lái)說(shuō) diff 節(jié)點(diǎn)也更快,但會(huì)產(chǎn)生一些隱藏的副作用,比如可能不會(huì)產(chǎn)生過(guò)渡效果,或者在某些節(jié)點(diǎn)有綁定數(shù)據(jù)(表單)狀態(tài),會(huì)出現(xiàn)狀態(tài)錯(cuò)位。)
diff 算法的過(guò)程中,先會(huì)進(jìn)行新舊節(jié)點(diǎn)的首尾交叉對(duì)比,當(dāng)無(wú)法匹配的時(shí)候會(huì)用新節(jié)點(diǎn)的 key 與舊節(jié)點(diǎn)進(jìn)行比對(duì),從而找到相應(yīng)舊節(jié)點(diǎn).
更準(zhǔn)確 : 因?yàn)閹?key 就不是就地復(fù)用了,在 sameNode 函數(shù) a.key === b.key 對(duì)比中可以避免就地復(fù)用的情況。所以會(huì)更加準(zhǔn)確,如果不加 key,會(huì)導(dǎo)致之前節(jié)點(diǎn)的狀態(tài)被保留下來(lái),會(huì)產(chǎn)生一系列的 bug。
更快速 : key 的唯一性可以被 Map 數(shù)據(jù)結(jié)構(gòu)充分利用,相比于遍歷查找的時(shí)間復(fù)雜度 O(n),Map 的時(shí)間復(fù)雜度僅僅為 O(1),源碼如下:
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key;
const map = {};
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
6. 談一談 nextTick 的原理JS 運(yùn)行機(jī)制
JS 執(zhí)行是單線程的,它是基于事件循環(huán)的。事件循環(huán)大致分為以下幾個(gè)步驟:
- 所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
- 主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
- 一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開(kāi)始執(zhí)行。
- 主線程不斷重復(fù)上面的第三步。

主線程的執(zhí)行過(guò)程就是一個(gè) tick,而所有的異步結(jié)果都是通過(guò) “任務(wù)隊(duì)列” 來(lái)調(diào)度。 消息隊(duì)列中存放的是一個(gè)個(gè)的任務(wù)(task)。 規(guī)范中規(guī)定 task 分為兩大類(lèi),分別是 macro task 和 micro task,并且每個(gè) macro task 結(jié)束后,都要清空所有的 micro task。
for (macroTask of macroTaskQueue) {
// 1. Handle current MACRO-TASK
handleMacroTask();
// 2. Handle all MICRO-TASK
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
在瀏覽器環(huán)境中 :
常見(jiàn)的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate
常見(jiàn)的 micro task 有 MutationObsever 和 Promise.then
異步更新隊(duì)列
可能你還沒(méi)有注意到,Vue 在更新 DOM 時(shí)是異步執(zhí)行的。只要偵聽(tīng)到數(shù)據(jù)變化,Vue 將開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。
如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作是非常重要的。
然后,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。
Vue 在內(nèi)部對(duì)異步隊(duì)列嘗試使用原生的 Promise.then、MutationObserver 和 setImmediate,如果執(zhí)行環(huán)境不支持,則會(huì)采用 setTimeout(fn, 0) 代替。
在 vue2.5 的源碼中,macrotask 降級(jí)的方案依次是:setImmediate、MessageChannel、setTimeout
vue 的 nextTick 方法的實(shí)現(xiàn)原理:
- vue 用異步隊(duì)列的方式來(lái)控制 DOM 更新和 nextTick 回調(diào)先后執(zhí)行
- microtask 因?yàn)槠涓邇?yōu)先級(jí)特性,能確保隊(duì)列中的微任務(wù)在一次事件循環(huán)前被執(zhí)行完畢
- 考慮兼容問(wèn)題,vue 做了 microtask 向 macrotask 的降級(jí)方案
7. vue 是如何對(duì)數(shù)組方法進(jìn)行變異的 ?
我們先來(lái)看看源碼
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
break;
}
if (inserted) ob.observeArray(inserted);
// notify change
ob.dep.notify();
return result;
});
});
/**
* Observe a list of Array items.
*/
Observer.prototype.observeArray = function observeArray(items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
};
簡(jiǎn)單來(lái)說(shuō),Vue 通過(guò)原型攔截的方式重寫(xiě)了數(shù)組的 7 個(gè)方法,首先獲取到這個(gè)數(shù)組的ob,也就是它的 Observer 對(duì)象,如果有新的值,就調(diào)用 observeArray 對(duì)新的值進(jìn)行監(jiān)聽(tīng),然后手動(dòng)調(diào)用 notify,通知 render watcher,執(zhí)行 update
8. Vue 組件 data 為什么必須是函數(shù) ?
new Vue()實(shí)例中,data 可以直接是一個(gè)對(duì)象,為什么在 vue 組件中,data 必須是一個(gè)函數(shù)呢?
因?yàn)榻M件是可以復(fù)用的,JS 里對(duì)象是引用關(guān)系,如果組件 data 是一個(gè)對(duì)象,那么子組件中的 data 屬性值會(huì)互相污染,產(chǎn)生副作用。
所以一個(gè)組件的 data 選項(xiàng)必須是一個(gè)函數(shù),因此每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝。new Vue 的實(shí)例是不會(huì)被復(fù)用的,因此不存在以上問(wèn)題。
9. 談?wù)?Vue 事件機(jī)制,手寫(xiě)$on,$off,$emit,$once
Vue 事件機(jī)制 本質(zhì)上就是 一個(gè) 發(fā)布-訂閱 模式的實(shí)現(xiàn)。
class Vue {
constructor() {
// 事件通道調(diào)度中心
this._events = Object.create(null);
}
$on(event, fn) {
if (Array.isArray(event)) {
event.map(item => {
this.$on(item, fn);
});
} else {
(this._events[event] || (this._events[event] = [])).push(fn);
}
return this;
}
$once(event, fn) {
function on() {
this.$off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.$on(event, on);
return this;
}
$off(event, fn) {
if (!arguments.length) {
this._events = Object.create(null);
return this;
}
if (Array.isArray(event)) {
event.map(item => {
this.$off(item, fn);
});
return this;
}
const cbs = this._events[event];
if (!cbs) {
return this;
}
if (!fn) {
this._events[event] = null;
return this;
}
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break;
}
}
return this;
}
$emit(event) {
let cbs = this._events[event];
if (cbs) {
const args = [].slice.call(arguments, 1);
cbs.map(item => {
args ? item.apply(this, args) : item.call(this);
});
}
return this;
}
}
10. 說(shuō)說(shuō) Vue 的渲染過(guò)程

調(diào)用 compile 函數(shù),生成 render 函數(shù)字符串 ,編譯過(guò)程如下:
- parse 函數(shù)解析 template,生成 ast(抽象語(yǔ)法樹(shù))
- optimize 函數(shù)優(yōu)化靜態(tài)節(jié)點(diǎn) (標(biāo)記不需要每次都更新的內(nèi)容,diff 算法會(huì)直接跳過(guò)靜態(tài)節(jié)點(diǎn),從而減少比較的過(guò)程,優(yōu)化了 patch 的性能)
- generate 函數(shù)生成 render 函數(shù)字符串
調(diào)用 new Watcher 函數(shù),監(jiān)聽(tīng)數(shù)據(jù)的變化,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),Render 函數(shù)執(zhí)行生成 vnode 對(duì)象
調(diào)用 patch 方法,對(duì)比新舊 vnode 對(duì)象,通過(guò) DOM diff 算法,添加、修改、刪除真正的 DOM 元素
11. 聊聊 keep-alive 的實(shí)現(xiàn)原理和緩存策略
export default {
name: "keep-alive",
abstract: true, // 抽象組件屬性 ,它在組件實(shí)例建立父子關(guān)系的時(shí)候會(huì)被忽略,發(fā)生在 initLifecycle 的過(guò)程中
props: {
include: patternTypes, // 被緩存組件
exclude: patternTypes, // 不被緩存組件
max: [String, Number] // 指定緩存大小
},
created() {
this.cache = Object.create(null); // 緩存
this.keys = []; // 緩存的VNode的鍵
},
destroyed() {
for (const key in this.cache) {
// 刪除所有緩存
pruneCacheEntry(this.cache, key, this.keys);
}
},
mounted() {
// 監(jiān)聽(tīng)緩存/不緩存組件
this.$watch("include", val => {
pruneCache(this, name => matches(val, name));
});
this.$watch("exclude", val => {
pruneCache(this, name => !matches(val, name));
});
},
render() {
// 獲取第一個(gè)子元素的 vnode
const slot = this.$slots.default;
const vnode: VNode = getFirstComponentChild(slot);
const componentOptions: ?VNodeComponentOptions =
vnode && vnode.componentOptions;
if (componentOptions) {
// name不在inlcude中或者在exlude中 直接返回vnode
// check pattern
const name: ?string = getComponentName(componentOptions);
const { include, exclude } = this;
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
// 獲取鍵,優(yōu)先獲取組件的name字段,否則是組件的tag
const key: ?string =
vnode.key == null
? // same constructor may get registered as different local components
// so cid alone is not enough (#3269)
componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : "")
: vnode.key;
// 命中緩存,直接從緩存拿vnode 的組件實(shí)例,并且重新調(diào)整了 key 的順序放在了最后一個(gè)
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
// make current key freshest
remove(keys, key);
keys.push(key);
}
// 不命中緩存,把 vnode 設(shè)置進(jìn)緩存
else {
cache[key] = vnode;
keys.push(key);
// prune oldest entry
// 如果配置了 max 并且緩存的長(zhǎng)度超過(guò)了 this.max,還要從緩存中刪除第一個(gè)
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
}
// keepAlive標(biāo)記位
vnode.data.keepAlive = true;
}
return vnode || (slot && slot[0]);
}
};
原理
- 獲取 keep-alive 包裹著的第一個(gè)子組件對(duì)象及其組件名
- 根據(jù)設(shè)定的 include/exclude(如果有)進(jìn)行條件匹配,決定是否緩存。不匹配,直接返回組件實(shí)例
- 根據(jù)組件 ID 和 tag 生成緩存 Key,并在緩存對(duì)象中查找是否已緩存過(guò)該組件實(shí)例。如果存在,直接取出緩存值并更新該 key 在 this.keys 中的位置(更新 key 的位置是實(shí)現(xiàn) LRU 置換策略的關(guān)鍵)
- 在 this.cache 對(duì)象中存儲(chǔ)該組件實(shí)例并保存 key 值,之后檢查緩存的實(shí)例數(shù)量是否超過(guò) max 的設(shè)置值,超過(guò)則根據(jù) LRU 置換策略刪除最近最久未使用的實(shí)例(即是下標(biāo)為 0 的那個(gè) key)
- 最后組件實(shí)例的 keepAlive 屬性設(shè)置為 true,這個(gè)在渲染和執(zhí)行被包裹組件的鉤子函數(shù)會(huì)用到,這里不細(xì)說(shuō)
LRU 緩存淘汰算法
LRU(Least recently used)算法根據(jù)數(shù)據(jù)的歷史訪問(wèn)記錄來(lái)進(jìn)行淘汰數(shù)據(jù),其核心思想是“如果數(shù)據(jù)最近被訪問(wèn)過(guò),那么將來(lái)被訪問(wèn)的幾率也更高”。

keep-alive 的實(shí)現(xiàn)正是用到了 LRU 策略,將最近訪問(wèn)的組件 push 到 this.keys 最后面,this.keys[0]也就是最久沒(méi)被訪問(wèn)的組件,當(dāng)緩存實(shí)例超過(guò) max 設(shè)置值,刪除 this.keys[0]
12. vm.$set()實(shí)現(xiàn)原理是什么?
受現(xiàn)代 JavaScript 的限制 (而且 Object.observe 也已經(jīng)被廢棄),Vue 無(wú)法檢測(cè)到對(duì)象屬性的添加或刪除。
由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)屬性執(zhí)行 getter/setter 轉(zhuǎn)化,所以屬性必須在 data 對(duì)象上存在才能讓 Vue 將它轉(zhuǎn)換為響應(yīng)式的。
對(duì)于已經(jīng)創(chuàng)建的實(shí)例,Vue 不允許動(dòng)態(tài)添加根級(jí)別的響應(yīng)式屬性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套對(duì)象添加響應(yīng)式屬性。
那么 Vue 內(nèi)部是如何解決對(duì)象新增屬性不能響應(yīng)的問(wèn)題的呢?
export function set(target: Array<any> | Object, key: any, val: any): any {
// target 為數(shù)組
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改數(shù)組的長(zhǎng)度, 避免索引>數(shù)組長(zhǎng)度導(dǎo)致splice()執(zhí)行有誤
target.length = Math.max(target.length, key);
// 利用數(shù)組的splice變異方法觸發(fā)響應(yīng)式
target.splice(key, 1, val);
return val;
}
// target為對(duì)象, key在target或者target.prototype上 且必須不能在 Object.prototype 上,直接賦值
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
// 以上都不成立, 即開(kāi)始給target創(chuàng)建一個(gè)全新的屬性
// 獲取Observer實(shí)例
const ob = (target: any).__ob__;
// target 本身就不是響應(yīng)式數(shù)據(jù), 直接賦值
if (!ob) {
target[key] = val;
return val;
}
// 進(jìn)行響應(yīng)式處理
defineReactive(ob.value, key, val);
ob.dep.notify();
return val;
}
- 如果目標(biāo)是數(shù)組,使用 vue 實(shí)現(xiàn)的變異方法 splice 實(shí)現(xiàn)響應(yīng)式
- 如果目標(biāo)是對(duì)象,判斷屬性存在,即為響應(yīng)式,直接賦值
- 如果 target 本身就不是響應(yīng)式,直接賦值
- 如果屬性不是響應(yīng)式,則調(diào)用 defineReactive 方法進(jìn)行響應(yīng)式處理
后記
如果你和我一樣喜歡前端,也愛(ài)動(dòng)手折騰,歡迎關(guān)注我一起玩耍啊~ ❤️
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
這篇文章主要介紹了關(guān)于VUE的面試題(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-16- 這篇文章主要介紹了Vue.js的高級(jí)面試題(附答案),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2020-01-13
- 這篇文章主要介紹了Vuex面試題匯總(推薦),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-15
- 這篇文章主要介紹了web前端面試中關(guān)于VUE的面試題(含答案),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)2019-11-08
這篇文章主要介紹了面試必備的13道可以舉一反三的Vue面試題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)2019-08-05- 這篇文章主要介紹了Vue面試題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-07-01
- 這篇文章主要介紹了Vue 高頻基礎(chǔ)面試題,在前端面試中經(jīng)常會(huì)遇到,今天小編特意整理分享到腳本之家平臺(tái),需要的朋友可以參考下2020-02-12



