一、大廠必考原理
1.組件化和MVVM
2.回應式原理
3.vdom和diff演算法
4.模板編譯
5.組件渲染程序
6.前端路由
1.組件化基礎=>(MVVM模型)
傳統組件,知識靜態渲染,更新依賴于操作DOM
資料驅動視圖 - Vue MVVM
MVVM是Model-View-ViewModel縮寫,也就是把MVC中的Controller演變成ViewModel,Model代表資料模型,View代表UI組件,ViewModel是View和Model層的橋梁,資料會系結到ViewModel層并自動將資料渲染到頁面中,視圖變化的時候通知viewModel層更新資料
2.Vue回應式原理的實作
組件data的資料一旦改變,立馬觸發視圖的更新,
核心API -- Object.defineProperty
Object.defineProperty有缺點(Vue3啟用Proxy)
Proxy的兼容性不太好,且無法使用polyfill
Object.defineProperty基本用法

Object.defineProperty實作回應式
- 監聽物件,監聽陣列
- 復雜物件,深度監聽
Object.defineProperty的缺點
- 深度監聽需要遞回到底,一次性計算量大
- 無法監聽新增屬性、洗掉屬性(要使用Vue.set Vue.delete)
- 無法原生監聽陣列,需要特殊處理
總結;
1.核心API -- Object.defineProperty
2.如何監聽物件(深度監聽),如何監聽陣列
3.缺點
// 觸發更新視圖
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) // 監聽陣列
3.虛擬DOM(vdom)和diff演算法
- DOM操作非常耗費性能
- 以前用jQuery,可以自行控制DOM操作時機,手動調整
- vue和react都是資料驅動試圖,如何有效控制DOM操作?
解決方案——vdom
- 有一定的復雜度,想減少計算次數比較難
- 難不能把計算,更多的轉移為JS計算?因為JS執行比較快
- vdom——用JS模擬DOM結構,計算出最小的變更,操作DOM
面試題:用JS模擬DOM元素
包含三部分:標簽tag,附著在標簽上的屬性、樣式、事件props,子元素children

通過snabbdom 學習vdom
- vue3重寫了vdom的代碼,優化了性能
- 但vdom的理念不變,面試考點不變
h函式、vnode資料結構、patch函式
vdom總結
- 用js模擬DOM結構(vnode)
- 新舊vnode對比,得出最小的更新范圍,最后更新DOM
- 資料驅動視圖的模式下,有效控制DOM操作
diff演算法
兩個數做diff,如這里的vdom diff

vnode ->patch ->new vnode
樹diff的時間復雜度O(n^3)
- 第一,遍歷tree1;第二,遍歷tree2
- 第三,排序
- 1000個節點,要計算1億次,演算法不可用
優化時間復雜度到O(n)
- 只比較同一層級,不跨級比較
- tag不相同,直接刪掉重建,不再深度比較
- tag和key,兩者都相同,則認為是相同的節點,不再深度比較


diff演算法總結
- patchVnode
- addVnodes removeVnodes
- updateChildren(key的重要性)
vdom和diff總結
- 細節不重要,updateChildren更新程序也不重要,不要深究
- vnode核心概念很重要:h vnode patch diff key 等
- vnode的存在價值更重要:資料驅動試圖,控制DOM操作
4.模板編譯


with語法


模板編譯

總結
vue中使用render代替template

總結
5.組件渲染更新程序
vue原理的三大知識點

組件渲染/更新程序
初次渲染程序
第二步是因為,執行render函式會觸發getter操作

更新程序

觸發setter,看是修改的data是否在getter中已經被監聽,如果是,就執行render函式
patch的diff演算法,會計算出最小差異,更新在DOM上
完整流程圖

模板編譯完,生成render函式,執行render函式生成vnode (虛擬DOM的樹)
執行render函式的時候會touch getter,即執行函式的時候回觸發Data里的getter
觸發的時候就會收集依賴,即在模板中出發了哪個變數的getter就會把哪個給觀察起來(watcher)
在修改Data的時候,看這個Data是否是之前作為依賴被觀察起來的
如果是,就重新出發re-render,重新渲染,重新生成vdom tree,重新touch
異步渲染
1.$nextTick:
vue是異步渲染,$nextTick會待Dom渲染完之后呼叫
頁面渲染時會將data的修改做整合,多次data修改只會渲染一次
2.匯總data的修改,一次性更新試圖
3.減少DOM操作次數,提高性能
6.前端路由原理
網頁url組成部分

hash的特點

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hash test</title>
</head>
<body>
<p>hash test</p>
<button id="btn1">修改 hash</button>
<script>
// hash 變化,包括:
// a. JS 修改 url
// b. 手動修改 url 的 hash
// c. 瀏覽器前進、后退
window.onhashchange = (event) => {
console.log('old url', event.oldURL)
console.log('new url', event.newURL)
console.log('hash:', location.hash)
}
// 頁面初次加載,獲取 hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash)
})
// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user'
})
</script>
</body>
</html>
H5 history
- 用url規范的路由,但跳轉時不重繪頁面
- history.pushState
- window.onpopstate

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>history API test</title>
</head>
<body>
<p>history API test</p>
<button id="btn1">修改 url</button>
<script>
// 頁面初次加載,獲取 path
document.addEventListener('DOMContentLoaded', () => {
console.log('load', location.pathname)
})
// 打開一個新的路由
// 【注意】用 pushState 方式,瀏覽器不會重繪頁面
document.getElementById('btn1').addEventListener('click', () => {
const state = { name: 'page1' }
console.log('切換路由到', 'page1')
history.pushState(state, '', 'page1') // 重要!!
})
// 監聽瀏覽器前進、后退
window.onpopstate = (event) => { // 重要!!
console.log('onpopstate', event.state, location.pathname)
}
// 需要 server 端配合,可參考
// https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90
</script>
</body>
</html>
總結

兩者選擇

to c的要是不需要管seo、搜索引擎也不需要用H5 history 簡單一點就好
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/304953.html
標籤:其他
上一篇:js個人總結1
下一篇:Python基礎之數字化大屏
