美文网首页react & vue & angular
深入理解Vue数据响应式

深入理解Vue数据响应式

作者: 茜Akane | 来源:发表于2023-01-04 21:31 被阅读0次

Vue数据响应式主要研究的是 Vue 构造选项中 data 属性的特性

深入响应式 官方文档 网址: https://cn.vuejs.org/v2/guide/reactivity.html

1. getter 与 setter

首先我们需要理解 ES6 的 getter 与 setter 语法。

// 创建一个对象,得到姓名
let obj1 = {
  姓: "高",
  名: "丽丽",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};
console.log(obj1.姓名());    // 高丽丽
// 姓名后面的括号不能删掉,因为它是函数

// 姓名不要括号也能得出值
let obj2 = {
  姓: "高",
  名: "丽丽",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};
console.log(obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数。

// 姓名可以被写
let obj3 = {
  姓: "高",
  名: "丽丽",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(name){
    this.姓 = name[0]
    this.名 = name.slice(1)
  },
  age: 18
};
obj3.姓名 = '张宇'
console.log(`姓 ${obj3.姓},名 ${obj3.名}`)
// 将obj3打印出来后发现 姓名:(...) 是个伪属性。
// 总结:setter 就是这样用的。用 obj.x = xxx 触发 set 函数

get语法将对象属性绑定到查询该属性时将被调用的函数。
使用get语法时应注意以下问题:

  • 可以使用数值或字符串作为标识;
  • 必须不带参数;
  • 它不能与另一个 get或具有相同属性的数据条目同时出现在一个对象字面量中

当尝试设置属性时,set语法将对象属性绑定到要调用的函数。
使用 set 语法时请注意:

  • 它的标识符可以是数字或字符串;
  • 它必须有一个明确的参数;
  • 在对象字面量中,不能为一个已有真实值的变量使用 set ,也不能为一个属性设置多个 set。

2. Object.defineProperty() 方法

该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

let _hair = null

Object.defineProperty(obj3, 'hair', {
  get() {
    return _hair
  },
  set(color) {
    _hair = color
  }
})
ojb3.hair = 'black'
console.log(ojb3.hair)

小结
关于 Object.defineProperty() 方法

  • 可以给对象添加属性value
  • 可以给对象添加getter / setter
  • getter / setter用于对属性的读写进行监控

其他属性
描述符默认值汇总
拥有布尔值的键 configurableenumerablewritable 的默认值都是 false
属性值和函数的键 value、get 和 set 字段的默认值为 undefined
在 Vue 实例创建的时候,Vue会将 data 数据变为添加了getter setter 的数据属性。

3. 模拟 vue 的数据代理原理

需求一:用 Object.defineProperty 定义 n

let data1 = {}
Object.defineProperty(data1, 'n', {
  value: 0
})
console.log(`需求一:${data1.n}`)    // 需求一:0 

需求二:n 不能小于 0 ,比如 data2.n = -1 应该无效,但 data2.n = 1 有效

let data2 = {}

data2._n = 0 // _n 用来存储 n 的值

Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n = value
  }
})

console.log(`需求二:${data2.n}`)    // 
需求二:0
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)    // 需求二:0 设置为 -1 失败 
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)    // 需求二:1 设置为 1 成功 

但是需求二中 如果直接修改 data2._n 属性也是无法拦截的,因此还要改进。
需求三:使用代理模式

let data3 = proxy({data:{n:0}})  // 括号里是 匿名对象,无法访问
function proxy({data} /* 解构赋值 */){
    const obj = {}
    //   这里的n理论上应该遍历data的所有key,这里简化了
    Object.defineProperty(obj,'n',{
        get(){
            return data.n
        },
        set(value){
            if(value < 0) return
            data.n = value
        }
    })
    return obj    // obj就是代理对象
}
data3.n = -1     // 这里触发了data3 的 setter 函数 不会赋值为负数
console.log(data3.n)    //   0 

在上面的代理中,若 proxy 函数的参数是有名称的对象(可访问),那么它的值还是会被修改。
需求四:使用代理的加强版——用户修改原始对象也能拦截

let myData = {n:0}
let data4 = proxy({data:myData})

function proxy({data}){
    //   这里的n理论上应该遍历data的所有key,这里简化了
    let value = data.n
    delete data.n     // 这行可以不写  因为下面创建的n属性会被覆盖
    Object.defineProperty(data,'n',{
        get(){
            return value
        },
        set(newValue){
            if(newValue < 0)return
            value = newValue
        }
    })

    // 上面这几句会监听 data 对象数据的变化

    const obj = {}
    Object.defineProperty(obj,'n',{
        get(){
            return data.n
        },
        set(value){
            if(value < 0)return
            data.n = value
        }
    })

    return obj
}
myData.n = -5
console.log(myData.n);    //   0  被监听拦截
data4.n = -6
console.log(data4.n);    //   0  被代理拦截

综上所述,Vue创建实例时对 data 的更改就包含了这个原理。

let data5 = proxy({data:myData})   // 类似于
const vm = new Vue(data:{n:0})   // Vue对于数据的更改原理正是上面解释的那样

小结
vm = new Vue({data: myData}) 会让 vm 成为 myData 的代理,并且对 myData 的所有属性监控,当 myData 里的属性变化就可以调用 render(data) 来更新页面。

4. Vue data属性存在的问题

4.1 Object.defineProperty的问题

Object.defineProperty(obj, 'n', {...}) 

必须要有一个'n',才能监听、代理 obj.n 。如果没有写'n'的话 Vue会给出警告,无法监听后面添加的属性。

new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1;
    }
  }
}).$mount("#app");

Vue监听不了一开始就不存在的 obj.b (undefine/null),所以页面不会显示。

4.2 解决方法——Vue.set()

可以直接在 obj 里添加 {b:undefined} ,但是实际上页面中有许多元素不可能一一添加,并且会使代码很难看,那么就要用到 Vue.set (或 this.$set)
使用 Vue.set() 方法的作用

  1. 新增 key
  2. 自动创建代理和监听(如果没有创建过)
  3. 触发视图更新(但并不会立刻更新)
new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
      <button @click="addB">add b</button>
    </div>
  `,
  methods: {
    setB() {
      // this.obj.b = 1; //再次刷新后,页面中会显示 1 
      Vue.set(this.obj, "b", 1);  // 也可以 this.$set(this.obj, "b", 1);
    },
    addB() {
      this.obj.b++;
    }
  }
}).$mount("#app");

4.3 数组的变更方法

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      //this.array[3] = "d";  //页面中不会显示 'd' 
      // 这个数组可以理解为array: [0:"a", 1:"b", 2:"c"]
      // 等下,你为什么不用 this.array.push('d')
    }
  }
}).$mount("#app");

Vue 也不能检测新增到新增了下表,我们也不会每次修改的时候都要使用 Vue.set 或者 this.$set 。

数组的篡改方法
所以当数组传给Vue时,数组的这七个方法会被篡改覆盖,文档中叫做变更方法,这些方法会自动对数组新增项添加对应的监听,并且会更新视图。
变更方法的实现
变更方法实际上就是在Vue实例上加了一层原型链,同名的放大会被最底层的原型覆盖掉,这就实现了篡改。
ES6的写法:以 push 方法为例(模拟实现,并非源码)
class VueArray extends Array{
    push(...arsgs){
        const oldLength = this.length    //  this就是当前数组
        super.push(...arsgs)
        console.log('我被篡改了')
        for(let i = oldLength; i < this.length; i++){
            Vue.set(this,i,this[i])  // 将每个新增的 key 都告诉 Vue 实例
        }
    }
}

相关文章

  • 深入理解Vue数据响应式

    Vue数据响应式主要研究的是 Vue 构造选项中 data 属性的特性 深入响应式 官方文档 网址: https:...

  • VUE双向绑定原理(深入响应式原理)

    vue官网-深入响应式原理 深入响应式原理

  • 学习vue的响应式 mvvm -01 数据响应式

    理解VUE的设计思想:VUE的核心是MVVM MVVM框架的三要素:数据响应式 模板引擎以及渲染 数据响应式:监听...

  • 前端面试题【Day02】

    本篇绪论 1,Vue响应式原理 1,Vue响应式原理 在vue实例中声明的数据就是响应式的。响应式:数据发生改变,...

  • vue响应式原理

    谈谈阅读了vue深入响应式原理后的理解 1.响应式原理 在生成vue实例时,为对传入的data进行遍历,使用Obj...

  • vue双向绑定原理

    理解vue的设计思想 MVVM模式 MVVM框架的三要素:数据响应式、模版引擎及其渲染 数据响应式:监听数据变化并...

  • Vue的34道题

    1、如何理解MVVM原理? MVVM的实现原理 2、响应式数据的原理是什么? 响应式数据与数据依赖基本原理vue双...

  • vue2中computed原理

    要理解这个,首先要理解vue2的数据响应式原理,因为computed这个API的实现是建立在数据响应式的基础之上的...

  • 深入理解Vue的数据响应式

    【目录】一、Vue对data做了什么?二、数据响应式三、Vue.set和this.$set 【正文】在学习Vue的...

  • [学习vue]源码解析02

    今天学习的主要目标: vue的环境搭建 掌握源码学习方法 vue初始化过程剖析 深入理解数据响应式项目地址:htt...

网友评论

    本文标题:深入理解Vue数据响应式

    本文链接:https://www.haomeiwen.com/subject/aknajrtx.html