浅析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()的一个弱点,我们要记一下。



目录
相关文章
|
10天前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
29 9
|
13天前
|
API
vue3知识点:响应式数据的判断
vue3知识点:响应式数据的判断
22 3
|
2月前
|
缓存 JavaScript 前端开发
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
该文章全面覆盖了Vue.js从基础知识到进阶原理的48个核心知识点,包括Vue CLI项目结构、组件生命周期、响应式原理、Composition API的使用等内容,并针对Vue 2与Vue 3的不同特性进行了详细对比与讲解。
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
|
17天前
|
缓存 JavaScript UED
优化Vue的响应式性能
【10月更文挑战第13天】优化 Vue 的响应式性能是一个持续的过程,需要不断地探索和实践,以适应不断变化的应用需求和性能挑战。
25 2
|
21天前
|
JavaScript 前端开发
Vue 2 和 Vue 3 之间响应式区别
10月更文挑战第7天
30 2
|
25天前
|
JavaScript 前端开发 网络架构
如何使用Vue.js构建响应式Web应用
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用
|
25天前
|
JavaScript 前端开发
如何使用Vue.js构建响应式Web应用程序
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用程序
|
26天前
|
JavaScript UED
Vue双向数据绑定的原理
【10月更文挑战第7天】
|
1月前
|
JavaScript 前端开发 数据安全/隐私保护
前端技术分享:使用Vue.js构建响应式表单
【10月更文挑战第1天】前端技术分享:使用Vue.js构建响应式表单
|
13天前
|
JavaScript 前端开发 API
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
21 0