浅析vue2响应式原理

简介: 浅析vue2响应式原理

响应式:简而言之,就是当数据发生改变的时候,视图会重新渲染,更新为最新的值。

初识Object.defineProperty

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

Object.defineProperty(obj, prop, desc)
  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符

Object.defineProperty是 Vue 响应式系统的精髓。

Vue使用 Object.defineProperty 为对象中的每一个属性,设置 get 和 set 方法,进行数据劫持/监听;

get 值是一个函数,当属性被访问时,会触发 get 函数

set 值同样是一个函数,当属性被赋值时,会触发 set 函数

const data = {};
let name = 'Vueeee'
// 在data对象中定义name属性
Object.defineProperty(data,'name',{
  // 当访问data.name时自动调用此函数
  get(){
    console.log('🚀- 我是get');
    return name
  },
  // 当赋值data.name时自动调用此函数
  set(newValue){
    console.log('🚀- 我是set');
    name = newValue
    // vue会接着做一个视图重新渲染的操作
  }
})
// 调用了data.name属性的get方法
console.log(data.name)
// 调用了data.name属性的set方法
data.name = 'Hyyyyy'
console.log(data.name)

bcf22e3a65351f4e2e6877fb6e162fa.png

基本的响应式实现

上面说道可以使用Object.defineProperty来实现vue当中的响应式。 现在来使用Object.defineProperty来实现一个mini的vue响应式的例子

// 如果在vue中我们只需要这样书写,即是响应式数据。
export default {
    data() {
        return{
          name: 'Hyyy',
          age: 23,
        }
    }
}

那么vue是怎么做到呢?我们来写个简单的例子

// 首先模拟vue2中data
const data = {
  name: 'Hyyy',
  age: 23,
}
// 使data对象 变成响应式数据
observer(data)
// 为传入的对象做响应式
function observer(target){
  // 只处理对象
  if(typeof target !== 'object' || target === null) return target
  // 遍历,为对象中的每一个属性,设置 get 和 set 方法,进行数据劫持/监听
  for(let key in target){
    // 传入Object.defineProperty()方法 所需要的对象本身、key和value
    defineReactive(target, key, target[key])
  }
}
 // 为传入的对象做数据劫持/监听
function defineReactive(target, key, value){
  Object.defineProperty(target, key, {
    get(){
      return value
    },
    set(newValue){
      // 如果当前value不等于
      if(value !== newValue){
        value = newValue
        console.log('触发set,🔥更新视图操作')
      }
    }
  })
}
data.name = 'yHhhhhhhh'  

b426dd9afb2adc9f16400472efe250e.png

上面的例子就是如何使用Object.defineProperty()来实现一个定义mini响应式数据的过程。

vue的源码肯定更加复杂,会判断各种情况,但核心就是这样了.

处理值为复杂对象的情况

上面我们只处理了最简单的情况,对象中的属性只是数字、字符串

如果是复杂的对象又该如何呢?

const data = {
  name: 'Hyyy',
  age: 23,
  // 如果我们加上个对象
  friend: {
      friendName: 'xxx',
  }
}
function observer(){
    /* 之前代码 */
}
function defineReactive(){
    /* 之前代码 */
}
// 对新加入的friend属性中的name属性进行更改
data.friend.name = 'xxx2号

此时会发现控制台中并没有和上个demo一样,出现'触发set,🔥更新视图操作'这句log

4d33e7e3effa82b911ee6046c392816.png

此时如果我们在上面定义的defineReactive方法中console.log打印传来的key,会发现

function defineReactive(target, key, value){
  console.log(key) -> 只能打印出data对象中的 name、age、friend三个属性
  //并不能打印出friend属性中的firendName属性,所以我们其实是没有给对象中的属性做数据监听的
  Object.defineProperty(target, key, {
      /* 之前代码 */
  })
}
复制代码


d1107d0c51d68dd590d16393433c721.png

如何达到对象中的属性也可以监听到呢?简单,只需要在defineReactive()函数中加入一行observer(value)...

function defineReactive(target, key, value){
  // 深度观察,只需要将当前对象传给observer,也做个监听就好了
  observer(value)
  Object.defineProperty(target, key, {
      /* 之前代码 */
  })
复制代码

173ea6cffb07ce52d1dd44d2cc5d8e3.png

但还没有完!!!

如果我们将data对象中的属性,赋值为一个新的对象,那这个对象还是没有受到监听的...

例如

const data = {
   age: 23,
    /* 之前代码 */
}
function observer(){
    /* 之前代码 */
}
function defineReactive(){
    /* 之前代码 */
}
data.age = { number: 23} // 会触发一次更新
//此时如果我们..
data.age.number = 21  // 又更改了,想想中会再触发一次更新

但并没有,只有data.age = { number: 23}触发了更新,因为我们没有监听data.age.number

e1fb1f22c1209882015fa3462e8874c.png

所以我们在set的时候,也就是data.age = { number: 23} 的时候,也要在set方法中对新传来的对象{number: 23}进行监听

set(newValue){
  // 加上这句!!!
  observer(newValue)
  /* 之前代码 */
  /* 之前代码 */
  /* 之前代码 */
  /* 之前代码 */
}
  /* 之前代码 */
  /* 之前代码 */
data.age = { number: 23} // 会触发一次更新
data.age.number = 21  // 再触发一次更新

158d454a2c43cddf55e2227314d3204.png

也就是我们对对象中的对象属性也要进行深度的监听,才能在数据改变的时候及时更新。

所以这也是我们再使用Object.defineProperty()做响应式的一个问题,即使我们数据是个层级很深的对象,他也会在一开始对所有数据不断的深度监听,直到他是个普通的值为止。

所以Vue3中, 改用了Proxy来解决,Proxy就会在使用到这个数据的时候,才会去做这个监听的过程。

除了上述问题,如果我们做如下操作:

delete data.某属性
data.新属性 = 'xxx'

并不会被响应到,因为Object.defineProperty()是没法处理属性删除与属性新增的。

所以在vue中删除我们会使用Vue.delete,新增我们会使用Vue.set

算是Object.defineProperty()的一个弱点,我们要记一下。



目录
相关文章
|
24天前
|
JavaScript 前端开发 开发者
Vue.js 框架大揭秘:响应式系统、组件化与路由管理,震撼你的前端世界!
【8月更文挑战第27天】Vue.js是一款备受欢迎的前端JavaScript框架,以简洁、灵活和高效著称。本文将从三个方面深入探讨Vue.js:响应式系统、组件化及路由管理。响应式系统为Vue.js的核心特性,能自动追踪数据变动并更新视图。例如,通过简单示例代码展示其响应式特性:`{{ message }}`,当`message`值改变,页面随之自动更新。此外,Vue.js支持组件化设计,允许将复杂界面拆分为独立且可复用的组件,提高代码可维护性和扩展性。如创建一个包含标题与内容的简单组件,并在其他页面中重复利用。
48 3
|
1月前
|
JavaScript 算法 编译器
vue3 原理 实现方案
【8月更文挑战第15天】vue3 原理 实现方案
30 1
|
23天前
|
JavaScript 开发者
vue学习之响应式数据绑定
响应式数据绑定
26 0
|
18天前
|
存储 JavaScript 前端开发
Vue 3的响应式系统是如何工作的呢
【9月更文挑战第3天】Vue 3的响应式系统是如何工作的呢
22 4
|
18天前
|
缓存 JavaScript API
介绍一下Vue 3的响应式系统
【9月更文挑战第3天】介绍一下Vue 3的响应式系统
33 3
|
19天前
|
缓存 JavaScript 容器
vue动态组件化原理
【9月更文挑战第2天】vue动态组件化原理
31 2
|
23天前
|
缓存 JavaScript 前端开发
[译] Vue.js 内部原理浅析
[译] Vue.js 内部原理浅析
|
1月前
|
JavaScript 前端开发 开发者
Vue学习之--------深入理解Vuex、原理详解、实战应用(2022/9/1)
这篇文章详细介绍了Vuex的基本概念、使用场景、安装配置、基本用法、实际应用案例以及注意事项,通过一个数字累加器的实战示例,帮助开发者深入理解Vuex的原理和应用。
|
1月前
|
JavaScript API
Vue学习之--------列表排序(ffilter、sort、indexOf方法的使用)、Vue检测数据变化的原理(2022/7/15)
这篇博客文章讲解了Vue中列表排序的方法,使用`filter`、`sort`和`indexOf`等数组方法进行数据的过滤和排序,并探讨了Vue检测数据变化的原理,包括Vue如何通过setter和数组方法来实现数据的响应式更新。
Vue学习之--------列表排序(ffilter、sort、indexOf方法的使用)、Vue检测数据变化的原理(2022/7/15)
|
1月前
|
JavaScript
Vue学习之--------列表渲染、v-for中key的原理、列表过滤的实现(2022/7/13)
这篇博客文章详细介绍了Vue中列表渲染的基础知识、`v-for`指令的使用、`key`的原理和列表过滤的实现。通过代码实例和测试效果,展示了如何遍历数组和对象、使用`key`属性优化渲染性能,以及如何实现列表的动态过滤功能。
Vue学习之--------列表渲染、v-for中key的原理、列表过滤的实现(2022/7/13)