1. 程式人生 > 其它 >Vue原理概述及Object.defineProperty

Vue原理概述及Object.defineProperty

技術標籤:Vue

主要考察什麼

  • 考察重點, 而不是考察細節,二八原則
  • 和使用相關的原理, 例如vdom, 模板渲染
  • 整體流程是否全面? 熱門技術是否有深度?

原理

  • 元件化
  • 響應式
  • vdom和diff
  • 模板編譯
  • 渲染過程(開放題目, 全面)
  • 前端路由

元件化

MVVM模型 資料驅動檢視

  • 不再操作dom, 只需要去更改資料, 框架會自動更改檢視

如何理解MVVM

元件化, 資料驅動檢視
view <-> viewModel <-> Model

響應式

  • 元件data的資料一旦變化, 檢視就會立刻更新

如何知道data變化?

  • 核心API: Object.defineProperty
  • 如何實現響應式, 監聽物件/監聽陣列
  • Object.defineProperty的缺點, Vue3.0啟用Proxy

Proxy有相容性問題, 且無法polyfill

Object.defineProperty的基本用法

const data = {}
const name = 'zhangsan'
//核心API
Object.defineProperty(data,"name",{
    get: fucntion(){
        console.log('get')
        return name
    },
    set: function(newVal){
        console.
log('set') name = newVal } }) console.log(data.name) //get zhangsan data.name = 'lisi' //set

Object.defineProperty實現響應式

監聽物件, 監聽資料, 深度監聽

function defineReactive(target,key,value){
    //深度監聽
    observer(value)
    
    //程式碼類似上述核心API
    Object.defineProperty(target,key,{
        get: fucntion
(){ console.log('get') return value }, set: function(newVal){ console.log('set') if(newVal !== value){ //設定新值, 深度監聽 observer(newVal) value = newVal //觸發檢視更新 updateView() } } }) } function obeserver(target){ if(typeof target !== 'object' || target === null){ //不是物件或陣列 return target } //重新定義各個屬性 for(let key in target){ defineReactive(target, key, target[key]) } } //外層 const data = { score : 90, name: 'zhangsan', info:{ age:20, sex:'female' } } observer(data) //還有一種情況 data.score = {math:90} //對新值再次更新 data.score.math = 80 //這種情況下, 就需要新值也要被監聽起來, 更改時才能觸發檢視更新

缺點

  • 深度監聽, 需要遞迴到底, 一次性計算量大

是否可以不一次性計算, 需要用到的時候再進行計算, 分多次遞迴完

  • Object.defineProperty無法監聽新增/刪除屬性, 所以需要Vue.set, Vue.delete 做這件事情
data.x = 'xxx'
delete data.info
  • 無法監聽陣列的變化, 需要特殊處理

vue中如何監聽陣列的變化

//重新定義陣列原型
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[methodName].call(this,...arguments)
    }
})

在上一節observer()函式中, 對陣列進行特殊判斷

function obeserver(target){
    if(typeof target !== 'object' || target === null){
        //不是物件或陣列
        return target
    }
    //不能這樣直接修改Array.prototype, 會汙染全域性的Array原型
    //Array.prototype.push = function(){
    //    updateView()
    //    ...
    //}
    if(Array.isArray(target)){
        //將原型屬性指向arrProto
        //那麼執行那幾個函式時就會進行檢視更新
        target.__proto__ = arrProto
    }
    
    //重新定義各個屬性
    for(let key in target){
        defineReactive(target, key, target[key])
    }
}