手写vue3

文章目录

Vue3

vue3 中使用了 monorepo 的方式来管理各个模块,各个模块都各自为一个包,根目录下都存在一个 package.json,可以单独打包。npm 不能实现这种打包方式, yarn 可以。

reactivity

Vue3 的代理是通过 Proxy 实现的,因此无法兼容低版本浏览器。

导出一个 VueReactivity 对象,上面有四个方法:reactive, readonly, shallowReactive, shallowReadonly.

  1. 在 src 下新建 index.ts,用于导出模块。

  2. 在 src 下新建 reactive.ts,定义并导出四个方法后,可以想象到,四个方法都是通过 Proxy 来代理,可以通过同一个函数传入不同参数来生成,因此定义一个 createProxy 方法,传入 target 目标对象,readonly 是否可以可读,handler 表示 Proxy 代理的处理方法。之后从另一个叫做 baseHandlers.ts 的文件中导入 handler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { isObject } from '@vue/shared'
import { mutableHandlers, readonlyHandlers, shallowReactiveHandlers, shallowReadonlyHandlers } from 'baseHandlers'

const reactiveMap = new WeakMap()
const readonlyMap = new WeakMap()
const shallowMap = new WeakMap()
const shallowReadonlyMap = new WeakMap()

const reactive = createReactiveObject(, , reactiveMap)
const readonly = createReactiveObject(, , readonlyMap)
const shallowReactive = createReactiveObject(, , shallowMap)
const shallowReadonly = createReactiveObject(, , shallowReadonlyMap)

export function createReactiveObject(target, baseHandlers, proxyMap) {
if (!isObject(target)) {
return target
}

const existsProxy = proxyMap.get(target)
if (existsProxy) {
return existsProxy
}
const proxy = new Proxy(target, baseHandlers)
proxyMap.set(target, proxy)
return proxy
}
  1. 在 src 下新建 baseHandlers.ts,由于每个 Proxy 的 handler 存在 get 和 set,而上面四种方法又存在 shallow 和 readonly 两个布尔变量,因此有四种可能,同样可以定义一个函数根据传入的参数生成不同的 get / set。

Reflect.get(target, key, receiver) 的第三个参数,是传入的上下文对象,当 key 为 function 时,里面的 this 指向 receiver,否则指向 target。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// getter
const get = createGetter()
const readonlyGet = createGetter(true)
const shallowGet = createGetter(false, true)
const shallowReadonlyGet = createGetter(true, true)

// setter
const set = createSetter()

function createSetter() {
return function set(target, key, value, receiver) {
let res = Reflect.set(target, key, value, receiver)
// 触发更新
return res
}
}


// getter
function createGetter(isReadonly = false, isShallow = false) {
return function get(target, key, receiver) {
let res = Reflect.get(target, key, receiver)

if (!isReadonly) {

}

if (isShallow) {
return res
}

if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}

return res
}
}

export const mutableHandlers = {
get,
set
}
export const shallowHandlers = {
get: shallowGet,
set
}
export const readonlyHandlers = {
get: readonlyGet,
set
}
export const shallowReadonlyHandlers = {
get: shallowReadonlyGet,
set
}
  1. 在 src 下新建 effect.ts,导出一个 effect 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
export function effect(fn, options = {}) {
let effect = createReactiveEffect(fn, options)
if (!options.lazy) {
effect()
}
return effect
}

let uid = 0
let activeEffect
export function createReactiveEffect(fn, options) {
const effect = function () {
activeEffect = fn // Dep.target = watcher

fn()
}
effect.id = uid++ // 唯一标识,类比 Watcher
effect._isEffect = true // 用于标识这个函数是 effect 函数
effect.raw = fn // 把用户传入的函数保存到当前的 effect 上
effect.deps = [] // 把后续用来存放此 effect 对于哪些属性
effect.options = options
return effect
}

const targetMap = new WeakMap()
export function track(target, type, key) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map)
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
}
}

export function trigger(target) {
const depsMap = targetMap.get(target)
if (!depsMap)
return
const effects = depsMap.get(key)
effects && effects.forEach(effect => effect())
}
分享到:

评论完整模式加载中...如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理