在 Vue 中如何实现数据的变化监测 ?

Vue2.0 源码 --- 数据的变化监测


Object 数据实现变化监测

数据可观测:知道什么时候数据被读取和改写 。vue2.0  中对象数据实现数据可观测,基于 Object.defineProperty 方法去实现 ,新版 vue3.0 中的变化这里不做讨论。

  • 【对象单个属性】设置为可观测
let obj = {}
let val = 'wuhao'
Object.defineProperty(obj, 'name', {
  enumerable: true,
  configurable: true,
  get(){
    console.log('name 属性被读取了')
    return val
  },
  set(newVal){
    console.log('name 属性被修改了')
    val = newVal
  }
})
  • 【对象所有属性】设置为可观测
/**
 * Observer类会通过递归的方式把一个对象的所有属性都转化成可观测对象
 */
export class Observer {
  constructor (value) {
    this.value = value
    // 给value新增一个__ob__属性,值为该value的Observer实例,相当于为value打上标记,表示它已经被转化成响应式了,避免重复操作
    def(value,'__ob__',this)
    if (Array.isArray(value)) {
      // 当value为数组时的逻辑
      // ...
    } else {
      this.walk(value)
    }
  }

  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
}
/**
 * 使一个对象转化成可观测对象
 * @param { Object } obj 对象
 * @param { String } key 对象的key
 * @param { Any } val 对象的某个key的值
 */
function defineReactive (obj,key,val) {
  // 如果只传了obj和key,那么val = obj[key]
  if (arguments.length === 2) {
    val = obj[key]
  }
  if(typeof val === 'object'){
      new Observer(val)
  }
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      console.log(`${key}属性被读取了`);
      return val;
    },
    set(newVal){
      if(val === newVal){
          return
      }
      console.log(`${key}属性被修改了`);
      val = newVal;
    }
  })
}


// 使用测试
let obj = new Observer({
  'name':'wuhao',
  'age':22
})

依赖收集:给每个数据都建一个依赖数组(因为一个数据可能被多处使用),谁依赖了这个数据我们就把谁放入这个依赖数组中,那么当这个数据发生变化的时候,我们就去它对应的依赖数组中,把每个依赖都通知一遍,触发视图更新。这个过程就是依赖收集。

  • 【依赖管理器】 Dep 
export default class Dep {
  constructor () {
    this.subs = []
  }

  addSub (sub) {
    this.subs.push(sub)
  }
  // 删除一个依赖
  removeSub (sub) {
    remove(this.subs, sub)
  }
  // 添加一个依赖
  depend () {
    if (window.target) {
      this.addSub(window.target)
    }
  }
  // 通知所有依赖更新
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

/**
 * Remove an item from an array
 */
export function remove (arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}
  • 在 getter 中【收集依赖】,在 setter 中【通知依赖更新】
function defineReactive (obj,key,val) {
  if (arguments.length === 2) {
    val = obj[key]
  }
  if(typeof val === 'object'){
    new Observer(val)
  }
  const dep = new Dep()  //实例化一个依赖管理器,生成一个依赖管理数组dep
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      dep.depend()    // 在getter中收集依赖
      return val;
    },
    set(newVal){
      if(val === newVal){
          return
      }
      val = newVal;
      dep.notify()   // 在setter中通知依赖更新
    }
  })
}
  • 【确定依赖】的  Watcher 
export default class Watcher {
  constructor (vm,expOrFn,cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = parsePath(expOrFn)
    this.value = this.get()
  }
  get () {
    window.target = this;
    const vm = this.vm
    let value = this.getter.call(vm, vm)
    window.target = undefined;
    return value
  }
  update () {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
  }
}

/**
 * Parse simple path.
 * 把一个形如'data.a.b.c'的字符串路径所表示的值,从真实的data对象中取出来
 * 例如:
 * data = {a:{b:{c:2}}}
 * parsePath('a.b.c')(data)  // 2
 */
const bailRE = /[^\w.$]/
export function parsePath (path) {
  if (bailRE.test(path)) {
    return
  }
  const segments = path.split('.')
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}

总结:

  1. Data 通过 observer 转换成了 getter/setter 的形式来追踪变化。
  2. 当外界通过 Watcher 读取数据时,会触发 getter 从而将 Watcher 添加到依赖中。
  3. 当数据发生变化时,会触发 setter,从而向 Dep 中依赖(即 Watcher )发送通知。
  4. Watcher 接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。

注意:

我们通过 Object.defineProperty 方法实现了对  object 数据的可观测,但此方法仅仅只能观测到 object 数据的取值及设置值,当我们向  object 数据里添加一对新的  key/value 或删除一对已有的  key/value 时,它是无法观测到的,导致当我们对 object 数据添加或删除值时,无法通知依赖,无法驱动视图进行响应式更新。Vue 中,为了解决这一问题,Vue 增加了两个 全局  API: Vue.set 和  Vue.delete


Array 数据实现变化监测 

。。。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue,我们可以使用watch选项来监测数据变化监测的对象可以是data已存在的值、函数、methods的函数名,以及computed的值。当监测数据发生变化时,相应的方法会被触发执行。具体的写法有以下几种: 1. 监测data已存在的值: ```javascript watch: { a: function(newVal, oldVal) { // 执行相关操作 console.log(newVal, oldVal); } } ``` 2. 监测函数: ```javascript watch: { data() { console.log('数据发生变化'); } } ``` 3. 监测methods的函数名: ```javascript watch: { data: 'changeData' // 值为methods的方法名 }, methods: { changeData(curVal, oldVal) { console.log(curVal, oldVal); } } ``` 4. 监测computed的值: ```javascript watch: { computedValue: function(newVal, oldVal) { console.log(newVal, oldVal); } } ``` 需要注意的是,watch监听的对象也可以是包括选项的对象,具体写法根据需求进行调整。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue watch关于对象内的属性监听](https://download.csdn.net/download/weixin_38555616/13667758)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vue的watch使用 监听数据变化](https://blog.csdn.net/zsq199771/article/details/125481011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值