IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue3响应式原理实现 -> 正文阅读

[JavaScript知识库]Vue3响应式原理实现

Vue3响应式原理实现

1、响应式的概念

比如对象的属性有一个初始化的值,有一段代码使用了这个值、那么在对象中的属性发生变化时,这段代码可以自动重新执行

2、响应式函数实现

把与对象属性相关函数 (下面称为对象的依赖) 放到一个数组中

如果属性发生变化会重新执行属性的依赖函数

let reactiveFns = []
function watchFn(fn) {
  reactiveFns.push(fn)
}

let obj = {
  name: 'lmw',
}
watchFn(function () {
  console.log('----------', obj.name)
})

// 如果修改了obj.name中的值 那么 会把与obj.name有关的函数再执行一遍
// 例如
obj.name = 'wml'
reactiveFns.forEach((fn) => {
  fn()
})

3、对依赖收集进行封装

上面写的代码只能收集name一个属性,但是在开发中所有对象不可能都只有一个属性,每一个属性就多写一个数组来收集这显然是不现实的,所以这里我们可以封装一个类来收集

class Depend {
  constructor() {
    // 用于存放依赖
    this.reactiveFns = []
  }
  addDepend(fn) {
    // 添加依赖的方法
    this.reactiveFns.push(fn)
  }
  notify() {
    // 执行依赖的方法
    this.reactiveFns.forEach((fn) => {
      fn()
    })
  }
}

// let reactiveFns = []

const depend = new Depend()

function watchFn(fn) {
  depend.addDepend(fn)
}

let obj = {
  name: 'lmw',
}
watchFn(function () {
  console.log('----------', obj.name)
})
obj.name = 'wml'
depend.notify()
// reactiveFns.forEach((fn) => {
//   fn()
// })

4、自动监听对象属性的变化

每次操作完对象属性的值时都要手动再执行一遍收集的依赖,这样很麻烦,所以我们可以用Proxy来监听这个对象

class Depend {
  constructor() {
    // 用于存放依赖
    this.reactiveFns = []
  }
  addDepend(fn) {
    // 添加依赖的方法
    this.reactiveFns.push(fn)
  }
  notify() {
    // 执行依赖的方法
    this.reactiveFns.forEach((fn) => {
      fn()
    })
  }
}

const depend = new Depend()

function watchFn(fn) {
  depend.addDepend(fn)
}

let obj = {
  name: 'lmw',
  age: 18,
}

const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, receiver) {
    Reflect.set(target, key, receiver)
    depend.notify()
  },
})

watchFn(function () {
  // console.log('----------', obj.name)
  console.log('----------', objProxy.name)
})
watchFn(function () {
  console.log('----------', objProxy.age)
})

// obj.name = 'wml'
objProxy.name = 'wml'
objProxy.age = 17

5、依赖收集管理

很显然如果代码这样我们不管改变对象的任何属性都是会执行一遍所有的依赖的,这并不是我们想看的,在这我们可以使用Map和WeakMap来对我们收集的依赖进行管理

let dependFn = null // 用于给Proxy中的set传入依赖函数
class Depend {
  constructor() {
    // 用于存放依赖
    this.reactiveFns = []
  }
  addDepend(fn) {
    // 添加依赖的方法
    this.reactiveFns.push(fn)
  }
  notify() {
    // 执行依赖的方法
    this.reactiveFns.forEach((fn) => {
      fn()
    })
  }
}

const depend = new Depend()

function watchFn(fn) {
  dependFn = fn
  fn()
  dependFn = null
}

let obj = {
  name: 'lmw',
  age: 18,
}

let info = {
  sex: '女',
}

const weakMap = new WeakMap()

function getDepend(target, key) {
  let map = weakMap.get(target)
  if (!map) {
    map = new Map()
    weakMap.set(target, map)
  }
  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }
  return depend
}

const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    const ownDepend = getDepend(target, key)
    ownDepend.addDepend(dependFn)
    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, receiver) {
    Reflect.set(target, key, receiver)
    const ownDepend = getDepend(target, key)
    ownDepend.notify()
  },
})

const infoProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    const ownDepend = getDepend(target, key)
    ownDepend.addDepend(dependFn)
    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, receiver) {
    Reflect.set(target, key, receiver)
    const ownDepend = getDepend(target, key)
    ownDepend.notify()
  },
})

watchFn(function () {
  console.log('----------', objProxy.name)
  console.log('----------', objProxy.name)
})
watchFn(function () {
  console.log('----------', objProxy.age)
})

watchFn(function () {
  console.log('++++++++++', infoProxy.sex)
})

console.log('---------------')
objProxy.name = 'wml'
objProxy.age = 17
infoProxy.sex = '男'

6、代码优化

1、每当我们多一个对象时就要有对应的Proxy,这样代码太过与臃肿,我们可以将返回Proxy代理的操作封装成一个函数

2、Proxy中的set每次添加依赖到ownDepend中都要传入dependFn参数,这样太麻烦,我们可以将这个方法封装到Depend类中

3、在收集依赖时如果依赖的函数调用了两次相同的属性那么会重复执行代码,这里可以采用使用Set的数据结构来储存依赖

let dependFn = null // 用于给Proxy中的set传入依赖函数
class Depend {
  constructor() {
    // 用于存放依赖
    this.reactiveFns = new Set()
  }
  // addDepend(fn) {
  //   // 添加依赖的方法
  //   this.reactiveFns.add(fn)
  // }
  addDepend() {
    // 添加依赖的方法
    if (dependFn) {
      this.reactiveFns.add(dependFn)
    }
  }
  notify() {
    // 执行依赖的方法
    this.reactiveFns.forEach((fn) => {
      fn()
    })
  }
}

const depend = new Depend()

function watchFn(fn) {
  dependFn = fn
  fn()
  dependFn = null
}

let obj = {
  name: 'lmw',
  age: 18,
}

let info = {
  sex: '女',
}

const weakMap = new WeakMap()

function reactice(obj) {
  return new Proxy(obj, {
    get: function (target, key, receiver) {
      const ownDepend = getDepend(target, key)
      ownDepend.addDepend()
      return Reflect.get(target, key, receiver)
    },
    set: function (target, key, receiver) {
      Reflect.set(target, key, receiver)
      const ownDepend = getDepend(target, key)
      ownDepend.notify()
    },
  })
}

function getDepend(target, key) {
  let map = weakMap.get(target)
  if (!map) {
    map = new Map()
    weakMap.set(target, map)
  }
  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }
  return depend
}

const objProxy = reactice(obj)

const infoProxy = reactice(info)

watchFn(function () {
  console.log('----------', objProxy.name)
})
watchFn(function () {
  console.log('----------', objProxy.age)
})

watchFn(function () {
  console.log('++++++++++', infoProxy.sex)
})

console.log('---------------')
objProxy.name = 'wml'
objProxy.age = 17
infoProxy.sex = '男'
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-08 13:54:34  更:2022-01-08 13:54:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:06:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码