vue[源码]你不知道的observe!

observe工厂函数

在之前的源码initdata函数中最后一句

observe(data, true /* asRootData */)复制代码

调用了observe函数观察数据

export function observe (value: any, asRootData: ?boolean): Observer | void { 
 if (!isObject(value) || value instanceof VNode) { 
    return //判断是否是一个对象或者VNode  
}
  let ob: Observer | void  //定义变量ob 保存observe实例  
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { 
   ob = value.__ob__ //检测目标对象是否含有_ob_,并且 __ob__ 属性应该是Observer的实例            作用是为了避免重复观察同一个数据对象
  } else if (
 //对数据对象进行观测    
shouldObserve &&    !isServerRendering() &&   
 (Array.isArray(value) || isPlainObject(value)) &&   
 Object.isExtensible(value) &&    !value._isVue  ) {  
  ob = new Observer(value) 
 } 
 if (asRootData && ob) {  
  ob.vmCount++  }  return ob
}复制代码

看下else..if..第一个条件shouldObserve 必须为true

export let shouldObserve: boolean = true //初始化默认设置为true
export function toggleObserving (value: boolean) {
 // 接收一个布尔值 改变true false  shouldObserve = value}复制代码

第二个条件!isServerRendering() 必须ture

let _isServer //定义一个变量
export const isServerRendering = () => {
  if (_isServer === undefined) {
    /* istanbul ignore if */    
if (!inBrowser && !inWeex && typeof global !== 'undefined') { 
     // detect presence of vue-server-renderer and avoid     
 // Webpack shimming the process      
_isServer = global['process'] && global['process'].env.VUE_ENV === 'server'  
  } else { 
     _isServer = false    
}  
}  return _isServer}复制代码

这个函数作用是为了判断是否为服务端渲染

第三个条件判断是否为数组和纯对象

第四个判断对象是可拓展性的...接下来就不说了 相信应该能看懂

ob = new Observer(value)复制代码

最后创建一个observer实例


observe构造函数

export class Observer 
{  
value: any;  
dep: Dep; 
 vmCount: number; // number of vms that have this object as root $data
  constructor (value: any) {
    this.value = value //属性引用了数据对象   
    this.dep = new Dep() //保存了一个新创建的 Dep 实例对象    
    this.vmCount = 0 //实例属性设置为0    
    def(value, '__ob__', this) //为数据对象定义了一个 __ob__ 属性,这个属性的值就是当前 Observer 实例对象,
    其中 def 函数其实就是 Object.defineProperty 函数的简单封装,
之所以这里使用 def 函数定义 __ob__ 属性是因为这样可以定义不可枚举的属性,
这样后面遍历数据对象的时候就能够防止遍历到 __ob__ 属性    
if (Array.isArray(value)) { 
//判断是否为数组      
if (hasProto) {
        protoAugment(value, arrayMethods)      
} else {
        copyAugment(value, arrayMethods, arrayKeys)      
}      this.observeArray(value)    
} else { 
//纯对象的情况      this.walk(value)    
}  
}
  /**   * Walk through all properties 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])    
}  
}
  /**   * Observe a list of Array items.   */  
observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])    
}  
}
}
复制代码

walk 实例对象方法遍历了变量获得了可枚举的属性,

每个属性调用了defineReactive函数:

export function defineReactive ( 
 obj: Object,
  key: string,
  val: any,  
 customSetter?: ?Function,  shallow?: boolean) {
  const dep = new Dep() //定义一个dep常量接收Dep实例对象 
//getOwnPropertyDescriptor方法返回指定对象上一个自有属性对应的属性描述
  const property = Object.getOwnPropertyDescriptor(obj, key)  
if (property && property.configurable === false) {
     return //判断是否可配置  
}
  // cater for pre-defined getter/setters  
const getter = property && property.get 
//保存 property 对象的 get 和 set函数  
const setter = property && property.set  
if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]//当只传递两个参数时,说明没有传递第三个参数 val,
那么此时需要根据 key 主动去对象上获取相应的值,即执行 if 语句块内的代码:val = obj[key]  
}
  let childOb = !shallow && observe(val)  
Object.defineProperty(obj, key, {
//函数重新定义属性的 setter/getter,这会导致属性原有的 set 和 get 方法被覆盖,
所以要将属性原有的 setter/getter 缓存,并在重新定义的 set 和 get 方法中调用缓存的函数,
从而做到不影响属性的原有读写操作。    
enumerable: true,    
configurable: true,    
get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val 
//判断是否存在getter?直接调用该函数:使用val      
if (Dep.target) {
//Dep.target 中保存的值就是要被收集的依赖(观察者)
        dep.depend() 
//行 dep 对象的 depend 方法将依赖收集到 dep中        
if (childOb) {
//大概意思是收集的依赖的触发时机是在使用 $set 或 Vue.set 给数据对象添加新属性时触发          
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 //新旧值判断处理 后面判断应该是为了处理NaN      
}      /* eslint-enable no-self-compare */     
 if (process.env.NODE_ENV !== 'production' && customSetter) { 
       customSetter()
       //环境判断和customSetter函数判断(作用:用来打印辅助属性)      
}     
 // #7981: for accessor properties without setter     
 if (getter && !setter) return     
 if (setter) {
  //正确设置属性值        setter.call(obj, newVal)      
} else { 
       val = newVal      
}     
 childOb = !shallow && observe(newVal)     
 dep.notify()//深度观测 依赖收集    
}  
})
}复制代码

defineReactive 函数的核心就是 将数据对象的数据属性转换为访问器属性,即为数据对象的属性设置一对 getter/setter,但其中做了很多处理边界条件的工作defineReactive 接收五个参数,但是在 walk 方法中调用 defineReactive 函数时只传递了前两个参数,即数据对象和属性的键名





转载于:https://juejin.im/post/5ca5b4555188251041397735

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值