1. 組件化基礎(chǔ)
1.1 如何理解 VUE 的 MVVM 模型?
知識點: 數(shù)據(jù)驅(qū)動試圖
- 傳統(tǒng)組件快耿,只是靜態(tài)渲染,更新還要依賴于操作 DOM
- 數(shù)據(jù)驅(qū)動試圖-Vue MVVM
-
數(shù)據(jù)驅(qū)動試圖-React setState
image.png
2. 響應(yīng)式
- 組件 data 的數(shù)據(jù)一旦變化,立刻觸發(fā)試圖的更新
- 實現(xiàn)數(shù)據(jù)驅(qū)動試圖的第一步
- 考察 vue 原理的第一題
vue2 中實現(xiàn)數(shù)據(jù)驅(qū)動視圖的主要 api ES5: Object.defineProperty蛔外?
// 觸發(fā)更新視圖
function updateView() {
console.log('視圖更新')
}
// 重新定義數(shù)組原型
const oldArrayProperty = Array.prototype
// 創(chuàng)建新對象,原型指向 oldArrayProperty 溯乒,再擴展新的方法不會影響原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 觸發(fā)視圖更新
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments)
}
})
// 重新定義屬性,監(jiān)聽起來
function defineReactive(target, key, value) {
// 深度監(jiān)聽
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度監(jiān)聽
observer(newValue)
// 設(shè)置新值
// 注意矛纹,value 一直在閉包中,此處設(shè)置完之后光稼,再 get 時也是會獲取最新的值
value = newValue
// 觸發(fā)更新視圖
updateView()
}
}
})
}
// 監(jiān)聽對象屬性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是對象或數(shù)組
return target
}
// 污染全局的 Array 原型
// Array.prototype.push = function () {
// updateView()
// ...
// }
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
// 重新定義各個屬性(for in 也可以遍歷數(shù)組)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 準(zhǔn)備數(shù)據(jù)
const data = {
name: 'zhangsan',
age: 20,
info: {
address: '北京' // 需要深度監(jiān)聽
},
nums: [10, 20, 30]
}
// 監(jiān)聽數(shù)據(jù)
observer(data)
// 測試
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增屬性或南,監(jiān)聽不到 —— 所以有 Vue.set
// delete data.name // 刪除屬性,監(jiān)聽不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度監(jiān)聽
data.nums.push(4) // 監(jiān)聽數(shù)組
Tips:
- 可以看出來 vue2 在對象監(jiān)聽的時候采够,
問題一:復(fù)雜數(shù)據(jù)監(jiān)聽需要遞歸到底冰垄,一次性的計算量很大;
問題二:對象的新增和刪除都無法監(jiān)聽到逝薪,所以vue2 推出了 Vue.set 和 Vue.delete 來實現(xiàn)對象的新增和刪除的監(jiān)聽蝴罪;
問題三:數(shù)組的監(jiān)聽需要通過 原型鏈來實現(xiàn);
vue3 中實現(xiàn)數(shù)據(jù)驅(qū)動視圖的主要 api ES6:Proxy要门?
// 創(chuàng)建響應(yīng)式
function reactive(target = {}) {
if (typeof target !== 'object' || target == null) {
// 不是對象或數(shù)組,則返回
return target
}
// 代理配置
const proxyConf = {
get(target, key, receiver) {
// 只處理本身(非原型的)屬性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get', key) // 監(jiān)聽
}
const result = Reflect.get(target, key, receiver)
// 深度監(jiān)聽
// 性能如何提升的询微?
return reactive(result)
},
set(target, key, val, receiver) {
// 重復(fù)的數(shù)據(jù)狂巢,不處理
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 // 是否設(shè)置成功
},
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
}
// 測試數(shù)據(jù)
const data = {
name: 'zhangsan',
age: 20,
info: {
city: 'beijing',
a: {
b: {
c: {
d: {
e: 100
}
}
}
}
}
}
const proxyData = reactive(data)
3. vdom 和 diff 算法
- vdom 是實現(xiàn) vue 和 react 的重要基石
- vdom 是一個熱門話題,也是面試中的熱門問題
背景: Dom 操作是非常耗時的藻雌,但是 js 的操作是很快的胯杭,jquery 自己控制dom操作,vue 和 react 是數(shù)據(jù)驅(qū)動試圖做个,如何有效控制 Dom 操作?
解決思路:
- 有了一定的復(fù)雜度顽频,想減少計算次數(shù)比較難太闺;
- 能不能把計算,更多的轉(zhuǎn)移為js計算省骂;
- vdom- 用 js 模擬Dom 結(jié)構(gòu),計算出最小的變更怠惶,操作Dom;(最大程度的避免無意義的變更)
image.png
diff 算法
image.png
image.png
image.png
image.png
4. 模版編譯(熱門技術(shù)的深度)
前置知識,with 語法
image.png
image.png
image.png
image.png
vue模版被編譯成什么?