本文作者:qiaoqingyi

简述Vue的响应式原理(vue的响应式原理描述)

qiaoqingyi 2023-04-04 853

本篇文章给大家谈谈简述Vue的响应式原理,以及vue的响应式原理描述对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

解析vue响应式原理

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 !== 'production' 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 !== 'production'

    (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 !== '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.

*/

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

  if (process.env.NODE_ENV !== 'production'

    (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 !== '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) {

  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)

    }

  }

}

深圳网站建设

vue2响应式原理总结

vue组件实例化时,会对data属性深度遍历(遇到数组或者对象)为每一个属性添加数据劫持。数据劫持就是使用Object.defineProperty(de fai in pro pu tei)方法添加get/set方法。

在这个过程中会实例化一个Dep类。

1.在get拦截器里触发dep实例的depend方法,进行依赖收集,实质是在dep的实例属性sub数组中添加依赖这个属性的watcher实例。

2.在set拦截器里触发dep实例的notify方法,对收集到的所有依赖派发更新,(watcher的update方法)

vue组件实例化时会实例化一个渲染watcher,渲染watcher实例化过程会做两件事情。

1.创建vnode,在这个过程中,访问了data属性,触发了get方法,完成了依赖收集。

2.触发了子组件的实例化,子组件实例化又会重复上述数据劫持的过程。

这个过程就是对组件树的深度遍历。

结合组件生命周期来看整个过程,父组件会先触发created钩子,子组件后触发created钩子。而子组件mouted钩子会先执行,父组件的mouted钩子后执行。

分步骤记忆

1、实现页面不刷新的原理

2、页面视图刷新的原理

实现页面不刷新

1.hash

2.history

3.abstract:支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

1.hash(哈希模式),#符号后边是浏览器行为,在改变的时候不对页面进行刷新(重新请求URL)(监听hashChange事件)

2.history模式,H5新增了pushState,replaceState连个新API,可以修改历史记录却不会使浏览器刷新页面。

视图更新原理

其原理就是vue的响应式更新dom的原理,m = v

m是数据,也就是在vue-router install时在根组件(root vue component)添加了_route属性,在匹配到对应路由后更新了_route属性值,继而触发了该属性值的渲染watcher,在继而触发dom更新。

两种模式的不同

1.部署时,history模式需要服务端处理所有可能的路径(例如配置nginx的配置文件),防止出现404。哈希模式则不需要。

2.URL表示不同。

v-model指令就是 v-bind:value 和 @input 的语法糖。

它即可以支持原生表单元素,也可以支持自定义组件

在自定义组件中其实际是这样的:

它的实现通过自定义render函数, 缓存了 vnode

Vue 在更新 DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到队列中一次。在缓冲时会去除重复数据避免不必要的计算和 DOM 操作。

$nextTick(cb) 目的是在DOM 更新完成后传入的回调函数再被调用。

vue2数据响应式原理

vue2响应式原理由 Observer 类, Dep 类和 Watcher 类互相调用实现, Observer 类是把一个普通的object类变成每一层都能相应的类, Dep 类的作用是添加,移除,通知和收集订阅者, Watcher 类是订阅者,主要功能是把当数据改变的时候,去调用回调函数,修改dom节点

那么是怎么实现响应式的呢,首先是一个函数,要先转换为可响应的,那就需要用到 Observer 类

这个 observe 函数就是对 Observer 类做多了一层封装

而 Observer 类是通过 Object.defineProperty 来监控数据的获取和改变的

关键在于 defineReactive 方法,这个方法是对 Object.defineProperty 做了一层封装,并且对对象的每一层做递归调用,实现了每一层都有响应监控

但是是怎么知道现在要保存哪一个 Watcher 实例到订阅者数组里面的呢?其实就是用了这个 Dep.target , Dep.target 相当于 window.target ,全局只有一个,全局也能访问

首先得先讲一讲 Watcher 类,我们先回到上面的index.js,对象要让 Watcher 类进行监听,而 Watcher 有3个参数,第一个是监听的对象,第二个是监听的属性,比如 a.b.c.d ,第三个是属性改变后触发的回调函数

先来讲一下 parsePath ,这个在工具类里,作用是访问 a.b.c.d 这种链式属性

首先是触发了 Watcher 的 get() 方法,把当前实例保存在了 Dep.target 里面

然后在调用 parsePath 获取属性值的过程中,会挨个访问响应对象的属性,就会触发相应的 getter ,我们回到 defineReactive.js ,可以发现这时候相应属性的 getter 就会把 Dep.target 也就是相应的 Watcher 的实例保存在了 Dep 类的订阅者数组里面

最后,在改变属性的时候,相应属性的 setter 就会通知之前已经保存的订阅者数组,遍历触发回调

能说说vue的响应式原理吗?

Vue 是一个 MVVM 框架,核心是双向数据绑定,VM(视图模型)是作为 V(视图) 和 M(模型)的桥梁。下面是对 Vue 响应式(双向数据绑定)的理解,如果错误尽请指出,一起交流,共同进步。

Vue响应式原理核心是 数据劫持,采用 ES5 的 object.defineproperty 的 getter 和 setter 方法。从一个例子出发:

首先,在Vue初始化阶段,通过 observer 对 data 中的属性进行递归的劫持,包括 name、job_ undergo、a、b等

在 get阶段也就是初始化视图时,为每一个劫持的属性分配一个 依赖收集器,主要收集当前属性的观察者对象,例子中 name 属性在模板中有两处被使用,那么 name 属性的依赖收集器中就存放两个观察者对象

当点击按钮时,将 name 修改为 lisi 时,会触发 observer 的 setter 函数,将 value 更新为 lisi 最新值,然后通知依赖收集器数据发生了更新。

依赖收集就是发布订阅模式,依赖收集器会通知所有的观察者对象,当前name 属性有两个观察者对象。

观察者对象调用对应的回调函数进行相关的处理和DOM更新

以上是纯响应式原理的分析和总结,下面配一张流程图:

Vue3.0 响应式原理

Vue3 使用 Proxy 对象重写响应式系统,这个系统主要有以下几个函数来组合完成的:

1、reactive:

接收一个参数,判断这参数是否是对象。不是对象则直接返回这个参数,不做响应式处理

创建拦截器对象 handler, 设置 get/set/deleteProperty

get

收集依赖(track)

返回当前 key 的值。

如果当前 key 的值是对象,则为当前 key 的对象创建拦截器 handler, 设置 get/set/deleteProperty

如果当前的 key 的值不是对象,则返回当前 key 的值

set

设置的新值和老值不相等时,更新为新值,并触发更新(trigger)

deleteProperty

当前对象有这个 key 的时候,删除这个 key 并触发更新(trigger)

返回 Proxy 对象

2、effect: 接收一个函数作为参数。作用是:访问响应式对象属性时去收集依赖

3、track:

接收两个参数:target 和 key

如果没有 activeEffect,则说明没有创建 effect 依赖

如果有 activeEffect,则去判断 WeakMap 集合中是否有 target 属性,

WeakMap 集合中没有 target 属性,则 set(target, (depsMap = new Map()))

WeakMap 集合中有 target 属性,则判断 target 属性的 map 值的 depsMap 中是否有 key 属性

depsMap 中没有 key 属性,则 set(key, (dep = new Set()))

depsMap 中有 key 属性,则添加这个 activeEffect

4、trigger:

判断 WeakMap 中是否有 target 属性

WeakMap 中没有 target 属性,则没有 target 相应的依赖

WeakMap 中有 target 属性,则判断 target 属性的 map 值中是否有 key 属性,有的话循环触发收集的 effect()

vue数组响应式原理

vue2中Object.defineProperty响应式只对对象有效,对数组无效,所以对数组做额外处理。我们知道,会改变数组本身的方法只有7个:sort, push, pop, slice, splice, shift, unshift,所以可以通过重写这些方法来达到数组响应式

解决方案:

1. 找到数组原型

2. 覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新

3. 将得到的新的原型设置到数组实例原型上

4. 对数组内部元素实现响应式

// 实现数组响应式// 1. 替换数组原型中7个方法constoriginalProto=Array.prototype// 克隆体原数组原型constarrayProto=Object.create(originalProto)// 可修改数组的7个方法 , 'sort'constchangeMethods=['push','pop','shift','unshift','slice','splice','sort']//  2. 在克隆的原型上,覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新changeMethods.forEach(method={arrayProto[method]=function(){// 进行原始操作originalProto[method].apply(this,arguments)// 覆盖操作:增加更新通知console.log(`数组正在执行${method}方法`);}})// 对象响应化functiondefineReactive(obj,key,value){Object.defineProperty(obj,key,{get(){console.log('获取'+key);returnvalue},set(newVal){if(newVal!==value){// console.log(newVal);// console.log(JSON.stringify(obj[key]));console.log(`正在改变${key}值:从${obj[key]}变为${newVal}`)value=newVal}}})}functionobserver(obj){// 不是对象或者为null,不做响应式,结束if(typeofobj!=='object'||obj===null)return;// 如果是数组,修改其实例的原型if(Array.isArray(obj)){// 3. 将得到的新的原型设置到数组实例原型上obj.__proto__=arrayProto// 4. 对数组内的元素,同样进行响应化for(leti=0;iobj.length;i++){// console.log(obj[i]);observer(obj[i])}// 如果是对象}else{Object.keys(obj).forEach(key={console.log(obj,key,obj[key]);defineReactive(obj,key,obj[key])})}}obj=[{a:1},2,7,5,3]observer(obj)obj.push(4)// 数组正在执行push方法obj.pop()// 数组正在执行pop方法obj[0].a=2// 获取a    // 正在改变a值:从1变为2obj.sort()// 数组正在执行sort方法console.log(obj);// [ 2, 3, 5, 7, { a: [Getter/Setter] } ]console.log(obj[4].a);// 获取a  // 2

链接

简述Vue的响应式原理(vue的响应式原理描述)

关于简述Vue的响应式原理和vue的响应式原理描述的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

阅读
分享