• 售前

  • 售后

热门帖子
入门百科

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

[复制链接]
爸证欢 显示全部楼层 发表于 2021-10-25 19:54:21 |阅读模式 打印 上一主题 下一主题
这篇文章主要是给不相识大概没打仗过 vue 相应式源码的小伙伴们看的,其主要目标在于能对 vue 的相应式原理有个基本的认识和相识,如果在面试中被问到此类标题,可以或许知道面试官想让你回答的是什么?「PS:文中如有不对的地方,接待小伙伴们指正」

相应式的理解


相应式顾名思义就是数据变化,会引起视图的更新。这篇文章主要分析 vue2.0 中对象和数组相应式原理的实现,依赖收集和视图更新我们留在下一篇文章分析。

在 vue 中,我们所说的相应式数据,一样平常指的是数组范例和对象范例的数据。vue 内部通过 Object.defineProperty 方法对对象的属性举行挟制,数组则是通过重写数组的方法实现的。下面我们就简单实现一下。
起首我们界说一个需要被拦截的数据
  1. const vm = new Vue({
  2. data () {
  3.   return {
  4.    count: 0,
  5.    person: { name: 'xxx' },
  6.    arr: [1, 2, 3]
  7.   }
  8. }
  9. })
  10. let arrayMethods
  11. function Vue (options) { // 这里只考虑对 data 数据的操作
  12. let data = options.data
  13. if (data) {
  14.   data = this._data = typeof data === 'function' ? data.call(this) : data
  15. }
  16. observer (data)
  17. }
  18. function observer(data) {
  19. if (typeof data !== 'object' || data === null) {
  20.   return data
  21. }
  22. if (data.__ob__) { // 存在 __ob__ 属性,说明已经被拦截过了
  23.   return data
  24. }
  25. new Observer(data)
  26. }
复制代码
这里的 arrayMethods、Observer 、 __ob__的实现和作用请继续往下看
实现 Observer 类

  1. class Observer {
  2. constructor (data) {
  3.   Object.defineProperty(data, '__ob__', { // 在 data 上定义 __ob__ 属性,在数组劫持里需要用到
  4.    enumerable: false, // 不可枚举
  5.    configurable: false, // 不可配置
  6.    value: this // 值是 Observer 实例
  7.   })
  8.   if (Array.isArray(data)) { // 对数组进行拦截
  9.    data.__proto__ = arrayMethods // 原型继承
  10.    this.observerArray(data)
  11.   } else { // 对象进行拦截
  12.    this.walk(data)
  13.   }
  14. }
  15. walk (data) {
  16.   const keys = Object.keys(data)
  17.   for(let i = 0; i < keys.length; i++) {
  18.    const key = keys[i]
  19.    defineReactive(data, key, data[key])
  20.   }
  21. }
  22. observerArray (data) { // 拦截数组中的每一项
  23.   data.forEach(value => observer(value))
  24. }
  25. }
复制代码
对象的拦截


对象的挟制需要注意的几点:
      
  • 遍历对象,如果值还是对象范例,需要重新调用 observer 观测方法  
  • 如果设置的新值是对象范例,也需要被拦截
  1. // 处理对象的拦截
  2. function defineReactive(data, key, value) {
  3. observer(value) // 如果 value 值仍是对象类型,需要递归劫持
  4. Object.defineProperty(data, key, {
  5.   get() {
  6.    return value
  7.   },
  8.   set(newValue){
  9.    if (newValue === value) return
  10.    value = newValue
  11.    observer(newValue) // 如果设置 newValue 值也是对象类型,需要被劫持
  12.   }
  13. })
  14. }
复制代码
数组的挟制


数组的挟制需要注意的几点:
      
  • 数组是使用函数挟制(切片编程)的思想,对数据举行拦截的  
  • 数组里新增加的值,如果是对象范例,也需要被重新拦截
  1. const oldArrayPrototype = Array.prototype
  2. arrayMethods = Object.create(oldArrayPrototype)
  3. const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // 能够改变原数组的方法
  4. methods.forEach(method => {
  5. arrayMethods[methods] = function (...args) {
  6.   const result = oldArrayPrototype[methods].call(this, ...args)
  7.   const ob = this.__ob__ // this 就是调用改方法的数组
  8.   let inserted; // 数组新增的项的集合,需要再对其进行拦截
  9.   switch(methods) {
  10.    case 'push':
  11.    case 'unshift':
  12.     inserted = args
  13.    case 'splice':
  14.     inserted = args.slice(2) // 因为 splice 第二个参数后面的才是新增的
  15.   }
  16.   if (inserted) {
  17.    ob.observerArray(inserted)
  18.   }
  19.   return result
  20. }
  21. })
复制代码
原理总结


在面试中,如果我们需要手写 vue 的相应式原理,上面的代码足矣。但是我们通过学习 vue 的源码,如果在面试中可以或许给出以下加以总结性的回答更能得到面试官的青睐。

vue 2.0 源码的相应式原理:
      
  • 由于使用了递归的方式对对象举行拦截,以是数据层级越深,性能越差  
  • 数组不使用 Object.defineProperty 的方式举行拦截,是由于如果数组项太多,性能会很差  
  • 只有界说在 data 里的数据才会被拦截,后期我们通过 vm.newObj = 'xxx' 这种在实例上新增的方式新增的属性是不会被拦截的  
  • 改变数组的索引和长度,不会被拦截,因此不会引起视图的更新  
  • 如果在 data 上新增的属性和更改数组的索引、长度,需要被拦截到,可以使用 $set 方法  
  • 可以使用 Object.freeze 方法来优化数据,进步性能,使用了此方法的数据不会被重写 set 和 get 方法
vue 3.0 源码相应式原理:
      
  • 3.0 版本中使用了 proxy 代替了 Object.defineProperty ,其有13中拦截方式,不需要对对象和数组分别举行处理,也无需递归举行拦截,这也是其提升性能最大的地方  
  • vue 3.0 版本相应式原理的简单实现
  1. const handler = {
  2. get (target, key) {
  3.   if (typeof target[key] === 'object' && target[key] !== null) {
  4.    return new Proxy(target[key], handler)
  5.   }
  6.   return Reflect.get(target, key)
  7. },
  8. set (target, key, value) {
  9.   if(key === 'length') return true
  10.   console.log('update')
  11.   return Reflect.set(target, key, value)
  12. }
  13. }
  14. const obj = {
  15. arr: [1, 2, 3],
  16. count: { num: 1 }
  17. }
  18. // obj 是代理的目标对象, handler 是配置对象
  19. const proxy = new Proxy(obj, handler)
复制代码
到此这篇关于详解实现vue的数据相应式原理的文章就介绍到这了,更多相干vue 数据相应式内容请搜刮草根技能分享从前的文章或继续欣赏下面的相干文章盼望大家以后多多支持草根技能分享!

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作