简单实现 vue 中的响应式系统

简介: 简单实现 vue 中的响应式系统

1. 什么是响应式?

响应式最基本的理解就是可以自动响应数据变化的代码机制。

比如:下面这段代码,初始化了一个m变量,第2行代码使用了m,那么在m有一个新值得时候,第2行代码要可以自动重新执行。

示例1:

let m = 20
console.log(m)

m = 40

对象的响应式

// 响应式对象
let obj = {
    name: 'yjw'
}

// 打印obj.name
coneole.log(obj.name)

// 改变obj是应该重新打印obj.name
obj.name = 'null'

在vue中最直接的体现就是watch、reactive函数了

watch: {
    data(){
        console.log('data发生了变化')
    }
}

2. vue2中实现

// 保存当前需要收集的响应式函数
let activeReactiveFn = null

class Depend {
  constructor() {
  //  使用Set来保存依赖函数, 而不是数组[]
    this.reactiveFns = new Set()
  }
  notify() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
  depend() {
    if(activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }
}

// WeakMap({key(对象): value}), key是个对象,弱引用(当将key设置为null时,key被垃圾回收机制回收,对应的value也会被回收)
const targetMap = new WeakMap()
// 封装一个获取depend函数
function getDepend(target, key) {
// 根据target对象获取map的过程
  let map = targetMap.get(target)
  if(!map) {
    map = new Map()
    targetMap.set(target, map)
  }
 // 根据key获取depend对象
  let depend = map.get(key)
  if(!depend) {
    depend = new Depend()
    map.set(key, depend)
  }
  return depend
}

// 封装一个响应式的函数
function watchEffect(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

function reactive(obj) {
    const keys = Object.keys(obj)
      keys.forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj, key, {
          get() {
            // 做依赖收集
            const depend = getDepend(target, key)
            depend.depend()
            return value
          },
          set(newValue) {
            value = newValue
            // 监听对象变化做出响应
            const depend = getDepend(target, key)
            depend.notify()
          } 
        })
      })
}

测试

const objProxy = reactive({
  name: 'yjw',
  age: 22
})
watchEffect(function() {
  console.log('模拟watch1----, name属性发生变化')
  objProxy.name
  console.log('do Somethings1')
})
watchEffect(function() {
  console.log('模拟watch2----,age属性发生变化')
  objProxy.age
  console.log('do Somethings2')
})

console.log('-----------------------------')
objProxy.name = 'coder'
objProxy.age = 18

3.vue3中实现

只要把reactive中Object.defineProperty实现换成Proxy就行了

// 保存当前需要收集的响应式函数
let activeReactiveFn = null

class Depend {
  constructor() {
  //  使用Set来保存依赖函数, 而不是数组[]
    this.reactiveFns = new Set()
  }
  notify() {
    this.reactiveFns.forEach(fn => {
      fn()
    })
  }
  depend() {
    if(activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }
}

const targetMap = new WeakMap()
// 封装一个获取depend函数
function getDepend(target, key) {
// 根据target对象获取map的过程
  let map = targetMap.get(target)
  if(!map) {
    map = new Map()
    targetMap.set(target, map)
  }
 // 根据key获取depend对象
  let depend = map.get(key)
  if(!depend) {
    depend = new Depend()
    map.set(key, depend)
  }
  return depend
}

// 封装一个响应式的函数
function watchEffect(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      // 根据target.key获取对应的depend
      // 做依赖收集
      const depend = getDepend(target, key)
      depend.depend()
      return Reflect.get(target, key, receiver)
    },
    set(target, key, newValue , receiver) {
      Reflect.set(target, key, newValue, receiver)
      // 监听对象变化做出响应
      const depend = getDepend(target, key)
      depend.notify()
    }
  })
}

测试

const objProxy = reactive({
  name: 'yjw',
  age: 22
})
watchEffect(function() {
  console.log('模拟watch1----, name属性发生变化')
  objProxy.name
  console.log('do Somethings1')
})
watchEffect(function() {
  console.log('模拟watch2----,age属性发生变化')
  objProxy.age
  console.log('do Somethings2')
})

console.log('-----------------------------')
objProxy.name = 'coder'
objProxy.age = 18
相关文章
|
20天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
110 1
|
1月前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
存储 JavaScript 前端开发
介绍一下Vue的核心功能
介绍一下Vue的核心功能
|
2月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
56 1
vue学习第一章
|
2月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
52 1
|
2月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
46 1
vue学习第四章
|
2月前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
40 1
vue学习第7章(循环)
|
2月前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
47 1
vue学习第九章(v-model)