问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

解析vue响应式原理

发布网友 发布时间:2023-02-02 17:14

我来回答

1个回答

热心网友 时间:2024-08-20 16:36

import Dep from './dep'

import VNode from '../vdom/vnode'

import { arrayMethods } from './array'

import {

  def,

  warn,

  hasOwn,

  hasProto,

  isObject,

  isPlainObject,

  isPrimitive,

  isUndef,

  isValidArrayIndex,

  isServerRendering

} from '../util/index'

const arrayKeys = Object.getOwnPropertyNames(arrayMethods)

/**

* In some cases we may want to disable observation inside a component's

* update computation.

*/

export let shouldObserve: boolean = true

export function toggleObserving (value: boolean) {

  shouldObserve = value

}

/**

* Observer class that is attached to each observed

* object. Once attached, the observer converts the target

* object's property keys into getter/setters that

* collect dependencies and dispatch updates.

*/

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()

    this.vmCount = 0

    def(value, '__ob__', this)

    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) {

    for (let i = 0, l = items.length; i < l; i++) {

      observe(items[i])

    }

  }

}

// helpers

/**

* Augment a target Object or Array by intercepting

* the prototype chain using __proto__

*/

function protoAugment (target, src: Object) {

  /* eslint-disable no-proto */

  target.__proto__ = src

  /* eslint-enable no-proto */

}

/**

* Augment a target Object or Array by defining

* hidden properties.

*/

/* istanbul ignore next */

function copyAugment (target: Object, src: Object, keys: Array) {

  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) || value instanceof VNode) {

    return

  }

  let ob: Observer | void

  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {

    ob = value.__ob__

  } 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

}

/**

* Define a reactive property on an Object.

*/

export function defineReactive (

  obj: Object,

  key: string,

  val: any,

  customSetter?: ?Function,

  shallow?: boolean

) {

  const dep = new Dep()

  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

  if ((!getter || setter) && arguments.length === 2) {

    val = obj[key]

  }

  let childOb = !shallow && 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 !== 'proction' && 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()

    }

  })

}

/**

* Set a property on an object. Adds the new property and

* triggers change notification if the property doesn't

* already exist.

*/

export function set (target: Array | Object, key: any, val: any): any {

  if (process.env.NODE_ENV !== 'proction' &&

    (isUndef(target) || isPrimitive(target))

  ) {

    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)

  }

  if (Array.isArray(target) && isValidArrayIndex(key)) {

    target.length = Math.max(target.length, key)

    target.splice(key, 1, val)

    return val

  }

  if (key in target && !(key in Object.prototype)) {

    target[key] = val

    return val

  }

  const ob = (target: any).__ob__

  if (target._isVue || (ob && ob.vmCount)) {

    process.env.NODE_ENV !== 'proction' && 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.

*/

export function del (target: Array | Object, key: any) {

  if (process.env.NODE_ENV !== 'proction' &&

    (isUndef(target) || isPrimitive(target))

  ) {

    warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)

  }

  if (Array.isArray(target) && isValidArrayIndex(key)) {

    target.splice(key, 1)

    return

  }

  const ob = (target: any).__ob__

  if (target._isVue || (ob && ob.vmCount)) {

    process.env.NODE_ENV !== 'proction' && 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) {

  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)

    }

  }

}

深圳网站建设www.sz886.com
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
QQ视频过期了怎么恢复正常呢? 有没有酒友买了曜隐今年那款牛年纪念版酱酒啊,想问一下这个纪念版的包装... 买过曜隐酱酒的朋友觉得他们的包装怎么样呀,看起来高端显品位吗?_百 ... 最近收到客户送的一瓶曜隐酱酒,觉得这个酒的瓶盖很有特色哇,大家知道... 我的用Psiloc irRemote不知道这么用 TOSHIBA东芝55U3800C PRO液晶电视推荐 电视设置了每天12点定时关机,有一天11点就提前手动关电视了,定时设置需 ... 变频器维修,维修变频器电路板 建行帐号指的是什么 癌症临床表现 癌症的症状有哪些 烟鬼有毒吗 虎皮鹦鹉幼鸟怎么养? 广电机顶盒能通过ap链接吗 装2个SATA的硬盘怎么设置主盘从盘呢 洋楼和高层有什么区别 高层洋房小高层的区别是什么 石家庄火车站一公里内干净便宜的酒店住宿? 石家庄新火车站附近干净又便宜的酒店有吗? 石家庄火车站附近的连锁酒店好像是八十九块钱一间,求酒店名称 卖王者荣耀账号没有删除qq里的信息 事业单位10级是什么级别 事业单位专业技术岗十级到八级年限 姜粉能给青菜施肥吗 曲妥珠有进化吗? YHY银手镯,钢印是有刻着YH99,不知道什么意思 找回删除的微信联系人 抽油烟机止逆阀 中间有弹簧 并且是可以两头接的 淘宝上哪里有 或者就是... 暖气逆止阀加热时铛铛响怎么回事 韩信励志成为将军算不算有志者事竟成? 哪款女士香水味道浓烈,时间持久。 除了红毒 苹果6备忘录怎么变成黑色? 你好,我这边有个铁件用来水镀的,为什么会起泡点,而且很多? u盘文件夹内容为空怎么办 24格计时器怎么调声音大小 word里怎么去掉方框里面的小格子呢? 洗菜盆台下盆安装方法是什么 哈利波特闪回咒有加成吗 长安univ中控台是哪里 荣威汽车的中控台在哪里找 07年明锐中控主机在哪里 20年轩逸的中控个人中心在哪里 汽车中控网坏了怎么维修 猪牛羊和鸡鸭鹅的肠子里装的都是什么,为什么有的人不吃动物的肠子?_百 ... 猪大肠里面的疙瘩能吃吗? 在丽江机场 求推荐旅行路线 从丽江机场开始,想在一周内游玩香格里拉、丽江古城、拉市海、束河古城... 丽江游,11号晚上到丽江机场,想在丽江待2天,有哪些景点值得一去,怎么... 短信幻灯片是什么意思 正威科技集团有限公司入注文安北部科技新区了吗 win10,桌面背景自定义的图片放上去太大了,怎么调整图片的大小啊