深入了解Vue 2响应式原理,并手写一个简单的Vue

本文深入探讨Vue 2的响应式原理,如何使用Object.defineProperty实现数据的getter/setter,以及手写实现简单Vue2响应式,包括双向绑定、简单Compiler和响应式更新。文中通过例子展示了数据变化如何驱动DOM更新,并介绍了Vue中Observer、Watcher和Dep的角色。
摘要由CSDN通过智能技术生成

1. Vue 2的响应式原理

Vue.js 一个核心思想是数据驱动。所谓数据驱动是指视图是由数据驱动生成的,对视图的修改,不会直接操作 DOM,而是通过修改数据。vue.js里面只需要改变数据,Vue.js通过Directives指令去对DOM做封装,当数据发生变化,会通知指令去修改对应的DOM,数据驱动DOM的变化,DOM是数据的一种自然的映射。vue.js还会对View操作做一些监听(DOM Listener),当我们修改视图的时候,vue.js监听到这些变化,从而改变数据。这样就形成了数据的双向绑定。
Vue 2官方文档中这样解释其响应式原理:

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

在这里插入图片描述

2. Object.defineProperty

这里需要说明的是,在Vue 2中Vue基于Object.defineProperty来实现Vue的响应时更新,在Vue 3中,Vue是通过Proxy代理一个对象来实现Vue的响应式更新的。
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

var vm = new Vue({
   
  data:{
   
    a:1
  }
})

// `vm.a` 是响应式的

vm.b = 2
// `vm.b` 是非响应式的

我们在初始化Vue实例的时候,会遍历Vue的data,并递归式的为每一个data数据执行Object.defineProperty使其拥有响应性,但是,如果我们为vm添加一个属性(如上文中的vm.b),这是就需要主动调用Object.defineProperty来使该属性具有响应性。

3. 手写实现Vue2 响应式原理

3.1 使用Object.defineProperty实现简单的双向绑定

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>手写实现Vue的响应式更新</title>
</head>
<body>
  <div id="app">
    <input id="input" type="text">
    <span id="content"></span>
  </div>
</body>
<script type="text/javascript">
  let data = {
   
  }
  Object.defineProperty(data, 'text', {
   
    set: function (v) {
   
      this.value = v	//当我们为data.text赋值时,实际上更改的是this.value,
      					//这里的this.value是我们添加的一个变量,用于保存data.text的内容,
      					//也可以是任意的其他变量,比如this.a = v,效果也是一样的。
      console.log('set', this.value)
      document.getElementById('content').innerText = this.value
    },
    get: function () {
   
      console.log('get', this.value)
      return this.value	//当我们获取data.text时,其实也是获取到this.value的值
    }
  })
  //所以,如果我们通过相同的方法为data再增加一个name属性,执行data.name=3,修改的也是this.value的值
  //所以这时候我们获取data.text的值时,得到的也是3。如果希望data的属性有独立的值,我们可以使用闭包,如下:
  // !function (obj, property, value) {
   
  //   Object.defineProperty(obj, property, {
   
  //     set: function (v) {
   
  //       value = v	//当我们为data.text赋值时,实际上更改的是this.value,
  //       //这里的this.value是我们添加的一个变量,用于保存data.text的内容,
  //       //也可以是任意的其他变量,比如this.a = v,效果也是一样的。
  //       console.log('set', value)
  //       document.getElementById('content').innerText = value
  //     },
  //     get: function () {
   
  //       console.log('get', value)
  //       return value	//当我们获取data.text时,其实也是获取到this.value的值
  //     }
  //   })
  // } (data, 'text')
  document.getElementById('input').addEventListener('input', function (e) {
   
    data.text = e.target.value
  })
</script>
</html>

请添加图片描述

控制台输出:
在这里插入图片描述
上面操作直接使用了DOM操作改变了文本节点的值,而且是在知道id的情况下,使用document.getELementById()获取到响应的文本节点,然后修改文本节点的值。

封装成一个框架,肯定不能使用这种做法。所以需要一个可以解析DOM并且能修改DOM中相应变量的模块。

3.2 实现简单Compiler

  1. 首先,获取文本中真实的DOM节点
  2. 然后,分析节点的类型
  3. 最后
Vue 2中,Vue基于Object.defineProperty来实现Vue响应式更新。当我们创建一个Vue实例时,通过将data对象中的属性转化为getter和setter,当属性被访问或修改时,Vue能够捕获到,并触发相应的更新操作。具体而言,Vue响应式对象中创建了一个Dep(依赖)实例,每个属性都有一个对应的Dep实例,用来存储依赖于该属性的Watcher(观察者)实例。当属性被访问时,会将对应的Watcher实例添加到Dep的依赖列表中,当属性被修改时,会通知Dep实例,进而触发Watcher实例的更新。 要手写Vue 2的响应式原理,可以参考以下步骤: 1. 创建一个Observer类,用来将data对象转化为响应式对象。在Observer类的构造函数中,遍历data对象的所有属性,使用Object.defineProperty将每个属性转化为getter和setter。 2. 在getter中,收集依赖,将当前的Watcher实例添加到Dep实例的依赖列表中。 3. 在setter中,触发依赖更新,通知Dep实例的所有Watcher实例进行更新。 4. 创建一个Dep类,用来管理依赖列表。Dep类具有添加依赖、移除依赖和通知依赖更新的功能。 5. 创建一个Watcher类,用来表示一个观察者。Watcher类具有update方法,当被观察的属性发生改变时,将被观察者的update方法添加到异步更新队列中。 6. 创建一个Compile类,用来编译模板。Compile类中的compile方法可以将模板中的指令解析成对应的更新函数,并创建Watcher实例。 7. 创建一个Vue类,作为整个框架的入口。在Vue类的构造函数中,将data对象转化为响应式对象,并创建Compile实例进行模板编译。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值