vue.js数据响应式原理解析

目录
  • Object.defineProperty()
  • 定义 defineReactive 函数
  • 递归侦测对象的全部属性
  • 流程分析
  • observe 函数
  • Observer 类
  • 完善 defineReactive 函数
  • One More Thing

Object.defineProperty()

得力于 Object.defineProperty() 的特性,vue 的数据变化有别于 react 和小程序,是非侵入式的。详细介绍可以看 MDN 文档这里特别说明几点:

  • get / set 属性是函数,出于习惯会被称为 getter 函数 / setter 函数(Java,c++ 中都有这种惯例)
  • value 或 writable 和 get 或 set 是不能同时出现的,否则报错
  • 注意区别 Object.defineProperties()

定义 defineReactive 函数

Object.defineProperty() 在使用 getter 和 setter 的时候,要想实现属性的修改,需要借助一个变量周转,如下面的 value,这就很麻烦。

const obj = {}
let value
Object.defineProperty(obj, 'a', {
  enumerable: true,
  configurable: true,
  get() {
    console.log('getter')
    return value
  },
  set(newValue) {
    value = newValue
    console.log('setter', newValue)
  }
})

所以定义了 defineReactive 函数,方便去给对象增加一个响应式属性。这里创建一个闭包的环境:闭包一定要有内外两个函数,外面这个函数 defineReactive 的 value 就形成了闭包。

const obj = {}
function defineReactive(data, key, value) {
  // 如果只传了两个参数,则让 value 直接等于 data[key]
  if (arguments.length === 2) value = data[key]

  Object.defineProperty(data, key, {
    enumerable: true, // 可被枚举(for...in 或 Object.keys 方法)
    configurable: true, // 可被配置,比如删除
    get() {
      console.log('查看了' + key + '属性')
      return value
    },
    set(newValue) {
      console.log('修改了' + key + '属性')
      value = newValue
    }
  })
}

defineReactive(obj, 'a', 10)
console.log(obj.a)
obj.a = 11
console.log(obj.a)

得到的结果如下图:

递归侦测对象的全部属性

我们自己写一个能够侦测对象全部属性的库
新建 index.js 作为主入口文件,用于测试效果,我们 let 一个对象 obj,目标是通过把 obj 作为参数传给 observe 函数,即可实现对 obj 对象所有属性的查看与修改的监测。

// index.js
import observe from './observe.js'
let obj = {
  a: {
    m: {
      n: 1
    }
  },
  b: 2
}
observe(obj)

流程分析

observe 函数

observe 函数用于观察一个对象(value)的属性是否已被监测的(是否有 __ob__ 属性),如果不是则让其属性成为响应式的(通过 new Observer(value))。
注意:之所以起了 __ob__ 这么奇怪的变量名,是为了保证不会与对象的原有属性同名。

// observe.js
import Observer from './Observer.js'
export default (value) => {
  if (typeof value !== 'object') return
  if (value.__ob__ !== undefined) {
    // 暂时留空
  } else {
    new Observer(value)
  }
}

Observer 类

Observer 是一个类,一旦 new 了一个实例,则做 2 件事:

  • 给传入的 value(其实是个对象) 添加 __ob__ 属性,值为这次 new 的实例(也就是构造函数中的 this),因为希望 __ob__ 属性是不可被枚举的,所以用 def 函数处理。
  • 遍历 value 的属性,通过 defineReactive 函数将其变为响应式的
// Observer.js
import { def } from './utils.js'
import defineReactive from './defineReactive.js'

export default class Observer {
  constructor(value) {
    def(value, '__ob__', this, false)
    this.walk(value)
  }
  // 处理对象,让对象的属性变为响应式
  walk(value) {
    for (let key in value) {
      defineReactive(value, key)
    }
  }
}

def 函数定义如下:

export const def = (obj, key, value, enumerable) => {
  Object.defineProperty(obj, key, {
    value,
    enumerable,
    writable: true,
    configurable: true
  })
}

完善 defineReactive 函数

相较于前面定义的时候,在两个地方添加了 observe(value),从而实现了递归侦测对象的全部属性。这里的参数 value,就是已经被变为响应式的属性的值,这个值如果是个对象,也需要被侦测,所以也要被 observe。

// defineReactive.js
import observe from './observe.js'

export default function defineReactive(data, key, value) {
  if (arguments.length === 2) value = data[key]

  // 注意这里不是传 key 而是传 value,因为 key 只是一个字符串,value 才是 key 指向的对象
  observe(value)
  // 让 data 的 key 属性变为响应式属性
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log('查看了' + key + '属性')
      return value
    },
    set(newValue) {
      console.log('修改了' + key + '属性')
      value = newValue
      // 修改的属性也需要被观察,如果是对象需要被侦测
      observe(newValue)
    }
  })
}

至此,在 index.js 传入 observe 的 obj 的每个属性都是响应式的了

// index.js
...省略前面的代码
obj.a.m = {
  y: 8
}
console.log(obj.a.m.y)

测试结果如下:

One More Thing

普通对象也是有 getter 和 setter 的:

  • get propertyName(){} 用来得到当前属性值的回调函数
  • set propertyName(){} 用来监视当前属性值变化的回调函数
  • 下面的代码中,属性 a 称为“数据属性”,它只有一个简单的值;属性b这种用 getter 和 setter 方法定义的属性称为“存取器属性”。
var num= {
    a: 2,
    get b(){
        return 2
    }
}

存取器属性定义为一个或两个与属性同名的函数,这个函数定义不使用 function 关键字,而是使用 get 或 set,也没有使用冒号将属性名和函数体分开。

到此这篇关于vue.js数据响应式原理解析的文章就介绍到这了,更多相关vue.js响应式原理 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue.js响应式数据的简单实现方法(一看就会)

    目录 引言 基本概念 副作用函数 响应式数据 响应式数据的基本实现 实现思路 初步实现尝试 完善响应系统 泛化副作用函数名 修复漏洞 总结 引言 在Vue.js之中,Vue会自动跟踪JavaScript状态变化并在状态发生改变时响应式地更新DOM,这就是Vue.js的两大核心功能之一——响应性,是每一个Vue.js框架使用者必须熟练掌握的的功能.而得益于Vue.js自身支持的声明式渲染,Vue.js的学习成本大大降低,就算是一个前端领域的小白,只要能看懂并简单使用基本的HTML.JavaScri

  • Vue.js中provide/inject实现响应式数据更新的方法示例

    vue.js官方文档:https://cn.vuejs.org/v2/api/#provide-inject 首先假设我们在祖辈时候传入进来是个动态的数据,官方不是说如果你传入了一个可监听的对象,那么其对象还是可响应的么? parent父页面: export default { provide() { return { foo: this.fonnB } }, data(){ return { fonnB: 'old word '} } created() { setTimeout(()=>{

  • vue.js数据响应式原理解析

    目录 Object.defineProperty() 定义 defineReactive 函数 递归侦测对象的全部属性 流程分析 observe 函数 Observer 类 完善 defineReactive 函数 One More Thing Object.defineProperty() 得力于 Object.defineProperty() 的特性,vue 的数据变化有别于 react 和小程序,是非侵入式的.详细介绍可以看 MDN 文档,这里特别说明几点: get / set 属性是函数

  • 详解实现vue的数据响应式原理

    这篇文章主要是给不了解或者没接触过 vue 响应式源码的小伙伴们看的,其主要目的在于能对 vue 的响应式原理有个基本的认识和了解,如果在面试中被问到此类问题,能够知道面试官想让你回答的是什么?「PS:文中如有不对的地方,欢迎小伙伴们指正」 响应式的理解 响应式顾名思义就是数据变化,会引起视图的更新.这篇文章主要分析 vue2.0 中对象和数组响应式原理的实现,依赖收集和视图更新我们留在下一篇文章分析. 在 vue 中,我们所说的响应式数据,一般指的是数组类型和对象类型的数据.vue 内部通过

  • 深入理解Vue的数据响应式

    1. ES语法的getter和setter 在开始了解 Vue 的数据响应式原理前应该先搞清楚 ES语法 中的 getter 和 setter 方法的具体用法. getter和setter 方法是以 get 和 set 关键字来为对象添加虚拟属性的一种方式.这种属性其实并不真实存在,而是以取值函数 getter 和存值函数 setter 来模拟的一种属性.目的是对某个属性设置存值函数和取值函数,拦截该属性的存取行为,以便于对该属性的存取做一些限定处理.如下所示(以下代码来源于 mdn) gett

  • vue数据响应式原理知识点总结

    vue2.0数据响应式原理 对象 Obect.defineproperty 定义对象的属性mjm defineproperty 其实不是核心的为一个对象做数据双向绑定,而是去给对象做属性标签,设置一系列操作权限,只不过属性里的get和set实现了响应式 var ob = { a: 1, b: 2 } //1-对象 2-属性 3-对于属性的一系列配置 Object.defineProperty(ob, 'a' , { //a对象则是ob的绝对私有属性,,默认都是true writable: fal

  • Vue深入讲解数据响应式原理

    目录 响应式是什么 如何实现数据响应式 实现对象属性拦截 通用的劫持方案 总结 响应式是什么 简而言之就是数据变页面变 如何实现数据响应式 在Javascript里实现数据响应式一般有俩种方案,分别对应着vue2.x 和 vue3.x使用的方式,他们分别是: 对象属性拦截 (vue2.x) Object.defineProperty 对象整体代理 (vue3.x) Proxy 其中对象属性拦截,道理都是相通的 实现对象属性拦截 字面量对象定义 let data = { name:'小兰同学' }

  • 稍微学一下Vue的数据响应式(Vue2及Vue3区别)

    什么是数据响应式 从一开始使用 Vue 时,对于之前的 jq 开发而言,一个很大的区别就是基本不用手动操作 dom,data 中声明的数据状态改变后会自动重新渲染相关的 dom. 换句话说就是 Vue 自己知道哪个数据状态发生了变化及哪里有用到这个数据需要随之修改. 因此实现数据响应式有两个重点问题: 如何知道数据发生了变化? 如何知道数据变化后哪里需要修改? 对于第一个问题,如何知道数据发生了变化,Vue3 之前使用了 ES5 的一个 API Object.defineProperty Vue

  • 一文彻底搞懂Vue的MVVM响应式原理

    目录 前言 Vue的MVVM原理 创建一个html示例 在MVue.js中创建MVue入口 创建Compile 1.处理元素节点compileElement(child) 2.处理文本节点compileText(child) 3.实现compileUtil指令处理 更新器Updater更新数据 实现数据观察者Observer 数据依赖器Dep 观察者Watcher 实现视图驱动数据驱动视图 总结 前言 这些天都在面试,每当我被面试官们问到Vue响应式原理时,回答得都很肤浅.如果您在回答时也只是停

  • 浅谈Vue 数据响应式原理

    前言 Vue的数据响应主要是依赖了Object.defineProperty(),那么整个过程是怎么样的呢?以我们自己的想法来走Vue的道路,其实也就是以Vue的原理为终点,我们来逆推一下实现过程. 本文代码皆为低配版本,很多地方都不严谨,比如 if(typeof obj === 'object')这是在判断obj是否为为一个对象,虽然obj也有可能是数组等其他类型的数据,但是本文为了简便,就直接这样写来表示判断对象,对于数组使用Array.isArray(). 改造数据 我们先来尝试写一个函数

  • 详解Vue3的响应式原理解析

    目录 Vue2响应式原理回顾 Vue3响应式原理剖析 嵌套对象响应式 避免重复代理 总结 Vue2响应式原理回顾 // 1.对象响应化:遍历每个key,定义getter.setter // 2.数组响应化:覆盖数组原型方法,额外增加通知逻辑 const originalProto = Array.prototype const arrayProto = Object.create(originalProto) ;['push', 'pop', 'shift', 'unshift', 'splic

  • vue.js响应式原理解析与实现

    从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.今天,就我们就来一步步解析vue.js响应式的原理,并且来实现一个简单的demo. 首先,先让我们来了解一些基础知识. 基础知识 Object.defineProperty es5新增了Object.defineProperty这个api,它可以允许我们为对象的属性来设定gette

随机推荐