Vue响应式原理 -【1】

9 篇文章 0 订阅
6 篇文章 0 订阅

模拟Vue响应式原理

重点回顾:

数据驱动:数据响应式、双向绑定、数据驱动

数据响应式:数据模型仅仅是普通的JavaScript对象,而当我们修改数据时,视图会进行更新,避免了繁琐的DOM操作( Vue 内部封装了复杂的 DOM 操作),提高开发效率;

双向绑定:包含数据响应式,数据改变,视图改变,反之亦然;Vue 中我们可以使用 v-module 在表单元素上创建数据双向绑定;

数据驱动是 Vue 最独特的特性之一:开发过程中仅需要关注数据本身,不需要关心数据是如何渲染到视图;

数据响应式的核心原理:

Vue 2.x:当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

// DOM
<div id="app">
    hello
 </div>
// defineProperty 如何实现vue的数据双向绑定
// 模拟 Vue 中的 data 选项
let data = {
  msg: 'hello'
}
// 模拟 Vue 的实例
let vm = {}
// 数据劫持(劫持 vm 对象的单个成员):当访问或者设置 vm 中的成员的时候,做一些干预操作
// defineProperty 有三个参数:param1:对象;param2:为vm对象添加的属性;param3:属性描述符
Object.defineProperty(vm, 'msg', {
  // 可枚举(可遍历)
  enumerable: true,
  // 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
  configurable: true,
  // 访问器:当获取值的时候执行
  get () {
    console.log('get: ', data.msg)
    return data.msg
  },
  // 设置器:当设置值的时候执行
  set (newValue) {
    console.log('set: ', newValue)
    if (newValue === data.msg) {
      return
    }
    data.msg = newValue
    // 数据更改,更新 DOM 的值
    document.querySelector('#app').textContent = data.msg
  }
})

// 测试:当 vm.msg 改变时,视图中的数据随之改变
vm.msg = 'Hello World'
// defineProperty 劫持 data 内部多个成员
// 遍历 data 对象的所有属性
Object.keys(data).forEach(key => {
  // 把 data 中的属性,转换成 vm 的 setter/setter
  Object.defineProperty(vm, key, {
    enumerable: true,
    configurable: true,
    get () {
      console.log('get: ', key, data[key])
      return data[key]
    },
    set (newValue) {
      console.log('set: ', key, newValue)
      if (newValue === data[key]) {
        return
      }
      data[key] = newValue
      // 数据更改,更新 DOM 的值
      document.querySelector('#app').textContent = data[key]
    }
  })
})

Vue 3.x:直接监听对象,而非属性。 ES6 中新增,IE 不支持,性能由浏览器优化

// 模拟 Vue 中的 data 选项
    let data = {
      msg: 'hello',
      count: 0
    }

    // 模拟 Vue 实例
    // Proxy 代理整个对象,它是构造函数,接收两个参数
    // param1:代理的对象data
    // param2:对象,成员为执行代理行为的函数
    let vm = new Proxy(data, {
      // 执行代理行为的函数
      // 当访问 vm 的成员会执行
      // target 代理目标对象 ;key 代理属性
      get (target, key) {
        console.log('get, key: ', key, target[key])
        return target[key]
      },
      // 当设置 vm 的成员会执行
      // newValue 新的值
      set (target, key, newValue) {
        console.log('set, key: ', key, newValue)
        if (target[key] === newValue) {
          return
        }
        target[key] = newValue
        document.querySelector('#app').textContent = target[key]
      }
    })

    // 测试
    vm.msg = 'Hello World'
    console.log(vm.msg)

总结:vue2.x 中的响应式原理核心是object.definproperty(),数据劫持实例对象的属性,遍历实例对象内部属性,利用get与set方法,当数据发生变化,更新 data 内部数据与 DOM;vue3.x 中更新了数据响应式原理的核心方法,使用了Proxy代理对象,监听整个对象,无需遍历,简化代码,内部同样使用get与set方法,但是不支持 IE 浏览器。

参考链接:https://cn.vuejs.org/v2/guide/reactivity.html

发布订阅模式和观察者模式:

发布/订阅模式:

订阅者

发布者

信号中心

vue的自定义事件:$on $emit

// 自定义事件
const vm = new Vue()

// 注册事件(可定义多个) => 订阅消息
// $on 接收两个参数:事件名称 事件处理函数
vm.$on('eventHandle', function () {
  console.log('事件处理函数1');
})
vm.$on('eventHandle', function () {
  console.log('事件处理函数2');
})

// 触发事件 => 发布消息
vm.$emit('eventHandle')

兄弟组件的通信过程:发布订阅模式

// 事件中心
const eventHub = new Vue()

// ComponentA: 发布者
addTodo:function(){
  // 发布消息(调用事件)
  eventHub.$emit('add-todo', { text: this.newTodoText })
  this.newTodoText = ''

}

// ComponentB: 订阅者
created: function(){
  // 订阅消息(注册事件)
  eventHub.$on('add-todo',this.addTodo)
}

模拟 Vue 自定义事件的实现:此处简单实现,忽略传参的情况

// 事件触发器
class EventHandle {
  constructor(){
    // 定义一个空对象,以键值对的形式存储注册的事件名称与事件处理函数,Object.create(null) 创建对象不会创建对象的原型,提升性能
    this.subs = Object.create(null)
  }
  // 注册事件
  $on(eventType, handler){
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler)
  }
  // 触发事件
  $emit(eventType){
    if(this.subs[eventType]) this.subs[eventType].forEach(handler => {handler()})
  }
}

// 测试
const eh = new EventHandle()
eh.$on('click',() => {
  console.log('自定义事件处理函数1');
})
eh.$on('click',() => {
  console.log('自定义事件处理函数2');
})
eh.$emit('click')

观察者模式:

与发布订阅模式的区别是没有事件中心,只有发布者与订阅者,并且发布者要知道订阅者的存在;观察者模式中,订阅者又叫做观察者,所有的订阅者自身都有一个 update 方法(当事件发生的时候,具体要做的事情,当事件发生的时候,会调用所有的 update 方法),在 Vue 的响应式机制中,当数据变化时,会调用观察者的 update 方法,update 方法去更新视图;观察者模式中订阅者的 update 方法是由发布者调用,发布者又叫做目标,发布者内部记录所有的订阅者,当事件发生时,是由发布者去通知所有的订阅者

img

实现简单地观察者模式,忽略传参的情况:

// 模拟实现观察者模式
// 订阅者-观察者
class Watcher {
  update () {
    console.log('update方法执行了');
  }
}

// 发布者-目标
class Dep {
  constructor(){
    // 记录所有的订阅者
    this.subs = []
  }
  // 添加订阅者
  addSub (watcher) {
    if(watcher && watcher.update){
      this.subs.push(watcher)
    }
  }
  // 发布通知
  notify () {
    this.subs.forEach(subs => {
      subs.update()
    })
  }
}

// 测试
const dep = new Dep()
const watcher = new Watcher()

dep.addSub(watcher)
dep.notify()

总结:

img

下篇:https://blog.csdn.net/m0_50729201/article/details/114487557

结语:以上内容全学习时手敲记录,无复制粘贴,全原创,希望可以给各位小伙伴带来收获,如有错误的地方或有疑问欢迎留言,感谢阅读!

祝各位前端程序猿前程似锦,一路向北!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值