vue2核心原理(简易) - $set和$del笔记

643 阅读1分钟

前言

  • 本章项目地址
  • 主讲$set
  • vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时, 不会更新视图 这个时候可以使用$set(删除可以用$del)
  • 其实每个对象或者数组, 都加了个dep, 在数据劫持时, 对数据的值也要进行劫持(递归劫持数据), 如果是对象或者数据 将返回本身的Observer实例 再在getter中, 如果返回有数据, 让对象或 者数组dep收集watcher
  • 如果添加了数据 就会通知target.__ob__.dep.notify更新视图
  • 数组的添加和删除用的都是splice方法
  • $set$del源码

示例

<div id="app">
    {{obj}} - {{arr}}
</div>

<script>
  var vm = new Vue({
      data: {
          obj: {n: 1},
          arr: [0]
      },
  })

  vm.$mount('#app')
  setTimeout(() => {
      vm.$set(vm.obj, 'xxxx', 123456)
      vm.$set(vm.arr, 1, 100)
  }, 2000)
</script>

正题

$set$del方法

import { set, del } from './observer/index'
Vue.prototype.$set = set
Vue.prototype.$del = del
/**
 * @description set
 */
export function set(target, key, value) {
    // 数组采用splice方法
    if (Array.isArray(target)) {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, value)
        return value
    }

    // 如果对象本身上已有 返回
    if (target.hasOwnProperty(key)) {
        target[key] = value
        return value
    }

    const ob = target.__ob__

    // 如果数据没有劫持 就是个普通对象 直接赋值 返回
    if (!ob) {
        target[key] = value
        return value
    }

    // 数据进行劫持
    defineReactive(ob.value, key, value)

    // 发布
    ob.dep.notify()
    return value
}

/**
 * @description del
 */
export function del (target, key) {
    if (Array.isArray(target)) {
        target.splice(key, 1)
        return
    }

    const ob = target.__ob__

    if (!target.hasOwnProperty(key)) return

    delete target[key]

    if (!ob) return

    ob.dep.notify()
}

observe

class Observer {
    constructor(data) {
        this.value = data
        // 看这里
        this.dep = new Dep()
        Object.defineProperty(data, '__ob__', {
          value: this,
          enumerable: false
        })
        // 其他 ....
}

/**
 * @description 劫持对象数据
 */
function defineReactive(data, key, value) {
    // 看这里 这里可以获取实例
    let childOb = observe(value)
	
    let dep = new Dep()

    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
            if (Dep.target) {
                dep.depend()

                // 看这里 数组或者对象收集watcher
                if (childOb) {
                    childOb.dep.depend()

                    // 多层数组[[[]]] 
                    if (Array.isArray(value)) { dependArray(value) }

                }

            }


            return value
        },
        set(newValue) {
            if (newValue !== value) {
                observe(newValue)
                value = newValue
                dep.notify()
            }
        }
    })
}

export function observe(data) {
    if (!isObject(data)) return
    
    if (data.__ob__) return data.__ob__
	// 对象和数组都可返回实例
    return new Observer(data)
}