简单实现 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
相关文章
|
1月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
7天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
1月前
|
JavaScript 前端开发 开发者
Vue是如何劫持响应式对象的
Vue是如何劫持响应式对象的
29 1
|
1月前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
1月前
|
存储 JavaScript 前端开发
介绍一下Vue的核心功能
介绍一下Vue的核心功能
|
1月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
57 0
|
7月前
|
JavaScript API
【vue实战项目】通用管理系统:api封装、404页
【vue实战项目】通用管理系统:api封装、404页
80 3
|
7月前
|
人工智能 JavaScript 前端开发
毕设项目-基于Springboot和Vue实现蛋糕商城系统(三)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
|
7月前
|
JavaScript Java 关系型数据库
毕设项目-基于Springboot和Vue实现蛋糕商城系统(一)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
197 0
|
7月前
|
JavaScript 前端开发 API
Vue3+Vite+TypeScript常用项目模块详解
现在无论gitee还是github,越来越多的前端开源项目采用Vue3+Vite+TypeScript+Pinia+Elementplus+axios+Sass(css预编译语言等),其中还有各种项目配置比如eslint 校验代码工具配置等等,而我们想要进行前端项目的二次开发,就必须了解会使用这些东西,所以作者写了这篇文章进行简单的介绍。
154 0
Vue3+Vite+TypeScript常用项目模块详解