您现在的位置是:首页 >技术杂谈 >手写一个迷你的 Vue 框架网站首页技术杂谈

手写一个迷你的 Vue 框架

Zain Lau 2023-07-10 12:00:03
简介手写一个迷你的 Vue 框架

响应式机制的主要功能就是,可以把普通的 JavaScript 对象封装成为响应式对象,拦截数据的获取和修改操作,实现依赖数据的自动化更新。

一个最简单的响应式模型,我们可以通过 reactive 或者 ref 函数,把数据包裹成响应式对象,并且通过 effect 函数注册回调函数,然后在数据修改之后,响应式地通知 effect 去执行回调函数即可。

Vue 的响应式是可以独立在其他平台使用的。比如你可以新建 test.js,使用下面的代码在 node 环境中使用 Vue 响应。以 reactive 为例,我们使用 reactive 包裹 JavaScript 对象之后,每一次对响应式对象 counter 的修改,都会执行 effect 内部注册的函数:


const {effect, reactive} = require('@vue/reactivity')

let dummy
const counter = reactive({ num1: 1, num2: 2 })
effect(() => {
  dummy = counter.num1 + counter.num2
  console.log(dummy)// 每次counter.num1修改都会打印日志
})
setInterval(()=>{
  counter.num1++
},1000)

import { effect } from '../effect'
import { reactive } from '../reactive'

describe('测试响应式', () => {
  test('reactive基本使用', () => {
    const ret = reactive({ num: 0 })
    let val
    effect(() => {
      val = ret.num
    })
    expect(val).toBe(0)
    ret.num++
    expect(val).toBe(1)
    ret.num = 10
    expect(val).toBe(10)
  })
})

export function reactive(target) {
  if (typeof target!=='object') {
    console.warn(`reactive  ${target} 必须是一个对象`);
    return target
  }

  return new Proxy(target, mutableHandlers);
}

const get = createGetter();
const set = createSetter();

function createGetter(shallow = false) {
  return function get(target, key, receiver) {
    const res = Reflect.get(target, key, receiver)
    track(target, "get", key)
    if (isObject(res)) {
      // 值也是对象的话,需要嵌套调用reactive
      // res就是target[key]
      // 浅层代理,不需要嵌套
      return shallow ? res : reactive(res)
    }
    return res
  }
}

function createSetter() {
  return function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver)
    // 在触发 set 的时候进行触发依赖
    trigger(target, "set", key)
    return result
  }
}
export const mutableHandles = {
  get,
  set,
};

const targetMap = new WeakMap()

export function track(target, type, key) {

  // console.log(`触发 track -> target: ${target} type:${type} key:${key}`)

  // 1. 先基于 target 找到对应的 dep
  // 如果是第一次的话,那么就需要初始化
  // {
  //   target1: {//depsmap
  //     key:[effect1,effect2]
  //   }
  // }
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 初始化 depsMap 的逻辑
    // depsMap = new Map()
    // targetMap.set(target, depsMap)
    // 上面两行可以简写成下面的
    targetMap.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    deps = new Set()
  }
  if (!deps.has(activeEffect) && activeEffect) {
    // 防止重复注册
    deps.add(activeEffect)
  }
  depsMap.set(key, deps)
}

响应式的主要功能就是可以把普通的 JavaScript 对象封装成为响应式对象,在读取数据的时候通过 track 收集函数的依赖关系,把整个对象和 effect 注册函数的依赖关系全部存储在一个依赖图中

在日常项目开发中也可以借鉴响应式的处理思路,使用通知的机制,来调用具体数据的操作和更新逻辑,灵活使用 effect、ref、reactive 等函数把常见的操作全部变成响应式数据处理,会极大的提高我们开发的体验和效率。

夜深了,没写完,有疑问私信我吧…

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。