vue面试一些原理题

89 阅读2分钟

vue的响应

image.png

image.png


//触发更新视图

function updateView(val){
    
   console.log(`触发更新视图的数据${val}`)
}



//挟持对象属性

/**
 * 
 * @param {*} targe 监听的对象,比如data
 * @param {*} key 挟持的属性 比如 name
 * @param {*} value 挟持的属性对应的值 ,比如‘张飒’
 */

function defineReactive(targe,key,value){
    //如果value是对象的话,直接Object.defineProperty是不行的,要深度递归

    observer(value)
    //重要api

    Object.defineProperty(targe,key,{
        get(){
            return value
        },
        set(newValue){
           
            if(newValue!== value){
                //如果newValue是个对象的话,直接修改是不行的,要深度递归
                observer(newValue)
                value = newValue

                updateView(value)
            }
        }
    })
   
}


/**
 * 监听数据,调用后,数据就是相应式
 * @param {*} targe:要监听的数据
 */
function observer(targe){
   
   if(typeof targe !== 'object' || targe === null) {
    return targe
   }
   //重新定义个个属性
   for(let key in targe){
    defineReactive(targe,key,targe[key])
   }
}

const data= {
    name:"张飒",
    age:20,
    info:{
        address:'beijing'
    }
}
//设置数据响应式
observer(data)

data.name = {
    aa:'bb'
};
data.info.address='上海';
data.name.aa ='cc'

1、定义监听数组的原型

我们都知道,在 JS 中,任何对象都有原型,而我们的目的是通过重写数组原型上方法(push、pop等)实现监听,而作为库或是框架,我们都不应该去改变全局原型上的任何原生方法或者属性,污染全局环境,所以,这里分3步:

第一步:创建一个对象,将数组的原型赋值给该对象
const oldArrayProperty = Array.prototype
第二步:创建新对象,原型指向该对象
const arrProperty = Object.create(oldArrayProperty)
第三步:重写该对象上的方法
arrProperty.push = function(){} ...
arrProperty.pop = function(){} ...


// 重新定义数组原型,加入触发更新的机制 
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向oldArrayProperty
const arrProperty = Object.create(oldArrayProperty) 

//优化写法 
const methods = ['push','pop','shift','unshift','splice']
methods.forEach(method => { arrProperty[method] = function(){ 
updateView()
// Array.prototype[method].call(this, ...arguments)等同下面的写法
arrProperty[method].call(this, ...arguments)

} })


2、将需要监听的数组的原型指向自定义的特殊原型

对原来的 observe 进行修改,加入数组判断,如果是数组则修改该数组的原型,至此,数组监听完成,下面是 observe 修改后代码以及测试例子

// 监听对象属性
function observe(target){
  if(typeof target !== 'object' || target === null) {
    // 不是数组或对象
    return target
  }
  // 如果是数组则修改该数组的原型
  if(Array.isArray(target)){
    target.__proto__ = arrProperty
    return
  }

  // 重新定义属性
  for(let key in target) {
    defineReactive(target, key, target[key])
  }
}

// 测试数据
const data = {
  myCars: ['Bugatti','Koenigsegg']
}

// 监听数据
observe(data)

// 测试
data.myCars.push('AE86') // (监听成功)输出 --> 数据更新