• vue.js 源代码学习笔记 ----- observe


    参考 vue 2.2.6版本

    /* @flow */
    //引入订阅者模式
    import Dep from './dep'
    
    import { arrayMethods } from
    './array' import { def, isObject, isPlainObject, hasProto, hasOwn, warn, isServerRendering } from '../util/index' const arrayKeys = Object.getOwnPropertyNames(arrayMethods) /** * By default, when a reactive property is set, the new value is * also converted to become reactive. However when passing down props, * we don't want to force conversion because the value may be a nested value * under a frozen data structure. Converting it would defeat the optimization.

    默认新的值也会被definePropty, 但是有些情况是不希望被转换的, 比如 frozen的属性, 实际不需要监听, 如果强制转换, 会破坏做的性能优化, 所以这里监听做成可配置
    */ export const observerState = { shouldConvert: true, isSettingProps: false } /** * Observer class that are attached to each observed * object. Once attached, the observer converts target * object's property keys into getter/setters that * collect dependencies and dispatches updates.

    每个监听对象在Observer里面被监听, 对象属性进入 getter setter方法后, 会收集依赖项 和 触发更新也的回调方法
    */ export class Observer { value: any; dep: Dep; vmCount: number; // number of vms that has this object as root $data constructor (value: any) { this.value = value
     
      // 监听的是 obj = {a:{}}, 这里的观察是整个对象 obj, 而不是某个属性的变化

    /*
        举个列子

    data: { a:{b:{c:1}} } var w1 = this.$watch('a.b.c', fn1);

    初始化的时候, 会把这个data生成 6 个dep, 其中三个是这个方法生成的, 另外三个是下面那个defineReact方法生成的
        前三个对应的值分别是 {a:{b:{c:1}}} {b:{c:1}} {c:1}
        后三个对应的值分别是 data.a a.b b.c

        上面的 w1 这个watcher收集依赖的时候, 会有5个依赖, 分别是 data.a || {b:{c:1}} || a.b || {c:1} || b.c 对应的dep
        
    在wathc a.b.c 的时候就会依次去取值 data.a => a.b => b.c, 收集这个三个属性依赖就可以了, 至于为什么要收集前三个对象依赖,
    还没有搞清楚, 可能在一些地方会在这三个对象ob中挂载一些东西, 以便发布一些东西.
    */
      this.dep = new Dep() 
      
    this.vmCount = 0

    //value增加一个 属性 __ob__ , 值就是 observer def(value,
    '__ob__', this)
    if (Array.isArray(value)) {

       //有__proto__的浏览器才能监控数组的push pop 方法, 如果没有, 则不能监控 const augment
    = hasProto ? protoAugment : copyAugment
    augment(value, arrayMethods, arrayKeys)
       
    //监控数组的每一项 
    this.observeArray(value) } else { this.walk(value) } } /** * Walk through each property and convert them into * getter/setters. This method should only be called when * value type is Object.
    循环每一个对象属性, 逐个监控
    */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** * Observe a list of Array items.
    监控数组每个元素, 如果元素也是数组, 又走数组监控的分支
    */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } } // helpers /** * Augment an target Object or Array by intercepting * the prototype chain using __proto__
    利用 __proto__ 截断原有的原型链
      相当于
    arrA.__proto__ = arrayMethods
    这样的话 , 用户使用 arrA.push('haha');
    就会沿着 __proto__找到 arrayMethods 属性 __proto__ 的push方法, 因为这个方法被检测了, 所以会触发set方法, 最终触发 notify()
    */ function protoAugment (target, src: Object) { /* eslint-disable no-proto */ target.__proto__ = src /* eslint-enable no-proto */ } /** * Augment an target Object or Array by defining * hidden properties. *通过定义隐藏属性来加强一个目标对象 或者 数组 /* istanbul ignore next */ function copyAugment (target: Object, src: Object, keys: Array<string>) { for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] def(target, key, src[key]) } } /** * Attempt to create an observer instance for a value, * returns the new observer if successfully observed, * or the existing observer if the value already has one.
    如果监控的值已经存在, 返回这个订阅者
    */ export function observe (value: any, asRootData: ?boolean): Observer | void { if (!isObject(value)) { return } let ob: Observer | void

    //如果这个值有__ob__, 直接返回 if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if (
       //如果不是服务器 而且 能被监控, 而且是一个数组或者对象, 而且这个对象不是vue实例 observerState.shouldConvert
    && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) {
    //根据值来创建ob ob
    = new Observer(value) } if (asRootData && ob) { ob.vmCount++ } return ob } /** * Define a reactive property on an Object.
    为属性添加监控主方法
    */ export function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function ) {

    //而这个dep是 observe下的某一个属性, 比如监听的是 obj = {a:{}}, 这里的dep是obj的属性 a const dep
    = new Dep()  
    //获取Object的get set函数  const property
    = Object.getOwnPropertyDescriptor(obj, key)
    if (property && property.configurable === false) { return } // cater for pre-defined getter/setters const getter = property && property.get const setter = property && property.set
    //如果val也是一个对象, 而且监控成功, 返回 一个observe实例 let childOb
    = observe(val)
    Object.defineProperty(obj, key, { enumerable:
    true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val
    if (Dep.target) { dep.depend() if (childOb) {
        
    childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } } return value }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = observe(newVal) dep.notify() } }) } /** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist.
    对象添加一个属性, 如果新的属性, 触发通知, 数组的$set 方法 其实就是通过splice方法触发的页面变化
    */ export function set (target: Array<any> | Object, key: any, val: any): any { if (Array.isArray(target) && typeof key === 'number') { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val } if (hasOwn(target, key)) { target[key] = val return val } const ob = (target : any).__ob__ if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ) return val } if (!ob) { target[key] = val return val } defineReactive(ob.value, key, val) ob.dep.notify() return val } /** * Delete a property and trigger change if necessary.
    Vue.$remove 方法触发页面变化的来源
    */ export function del (target: Array<any> | Object, key: any) { //判断如果是数组
    if (Array.isArray(target) && typeof key === 'number') { target.splice(key, 1) return } const ob = (target : any).__ob__
    //避免删除vue实例
    if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid deleting properties on a Vue instance or its root $data ' + '- just set it to null.' ) return } if (!hasOwn(target, key)) { return }
    //如果是对象
    delete target[key] if (!ob) { return }
    //触发通知 ob.dep.notify() }
    /** * Collect dependencies on array elements when the array is touched, since * we cannot intercept array element access like property getters. */ function dependArray (value: Array<any>) { for (let e, i = 0, l = value.length; i < l; i++) { e = value[i] e && e.__ob__ && e.__ob__.dep.depend() if (Array.isArray(e)) { dependArray(e) } } }
  • 相关阅读:
    面向对象的分析与设计
    Django的ORM补充
    JDBC数据库连接池
    Python 中的深浅拷贝
    智能机系统分析
    hyperf框架学习
    HTTP协议知识
    百度知道有关php抓取问题
    awk之FS的指定
    从DELPHI到JAVA[delphi]
  • 原文地址:https://www.cnblogs.com/dhsz/p/7063886.html
Copyright © 2020-2023  润新知