前言
接着上一篇《Vue 3 Pre-Alpha / vue-next 源码学习之 vue/reactivity/baseHandlers.ts》我们学习了使用 Proxy
实现的 reactive
构造出来的响应式的对象, new Proxy(target, handler)
中的基础 handler
(baseHandlers)。这一篇,我们将来介绍另外一种 handler
叫 collectionHandlers。collectionHandler 比 baseHandler 提供了更加丰富的 OperationTypes
(set、add、delete、clear、get、has、iterate) 中更齐全的操作的监听响应处理。
源码分析
1 | import { toRaw, reactive, readonly } from './reactive' |
2 | import { track, trigger } from './effect' |
3 | import { OperationTypes } from './operations' |
4 | import { LOCKED } from './lock' |
5 | import { isObject, capitalize, hasOwn } from '@vue/shared' |
7 | const toReactive = (value: any) => (isObject(value) ? reactive(value) : value) |
8 | const toReadonly = (value: any) => (isObject(value) ? readonly(value) : value) |
11 | function get(target: any, key: any, wrap: (t: any) => any): any { |
12 | target = toRaw(target) |
14 | const proto: any = Reflect.getPrototypeOf(target) |
15 | track(target, OperationTypes.GET, key) |
16 | const res = proto.get.call(target, key) |
21 | function has( this : any, key: any): boolean { |
22 | const target = toRaw( this ) |
24 | const proto: any = Reflect.getPrototypeOf(target) |
25 | track(target, OperationTypes.HAS, key) |
26 | return proto.has.call(target, key) |
30 | function size(target: any) { |
31 | target = toRaw(target) |
32 | const proto = Reflect.getPrototypeOf(target) |
33 | track(target, OperationTypes.ITERATE) |
34 | return Reflect.get(proto, 'size' , target) |
38 | function add( this : any, value: any) { |
40 | const target = toRaw( this ) |
41 | const proto: any = Reflect.getPrototypeOf( this ) |
42 | const hadKey = proto.has.call(target, value) |
43 | const result = proto.add.call(target, value) |
47 | trigger(target, OperationTypes.ADD, value, { value }) |
49 | trigger(target, OperationTypes.ADD, value) |
56 | function set( this : any, key: any, value: any) { |
58 | const target = toRaw( this ) |
59 | const proto: any = Reflect.getPrototypeOf( this ) |
60 | const hadKey = proto.has.call(target, key) |
61 | const oldValue = proto.get.call(target, key) |
62 | const result = proto.set.call(target, key, value) |
63 | if (value !== oldValue) { |
66 | const extraInfo = { oldValue, newValue: value } |
68 | trigger(target, OperationTypes.ADD, key, extraInfo) |
70 | trigger(target, OperationTypes.SET, key, extraInfo) |
74 | trigger(target, OperationTypes.ADD, key) |
76 | trigger(target, OperationTypes.SET, key) |
84 | function deleteEntry( this : any, key: any) { |
85 | const target = toRaw( this ) |
86 | const proto: any = Reflect.getPrototypeOf( this ) |
87 | const hadKey = proto.has.call(target, key) |
88 | const oldValue = proto.get ? proto.get.call(target, key) : undefined |
90 | const result = proto. delete .call(target, key) |
94 | trigger(target, OperationTypes.DELETE, key, { oldValue }) |
96 | trigger(target, OperationTypes.DELETE, key) |
103 | function clear( this : any) { |
104 | const target = toRaw( this ) |
105 | const proto: any = Reflect.getPrototypeOf( this ) |
106 | const hadItems = target.size !== 0 |
107 | const oldTarget = target instanceof Map ? new Map(target) : new Set(target) |
109 | const result = proto.clear.call(target) |
113 | trigger(target, OperationTypes.CLEAR, void 0, { oldTarget }) |
115 | trigger(target, OperationTypes.CLEAR) |
122 | function createForEach(isReadonly: boolean) { |
123 | return function forEach( this : any, callback: Function, thisArg?: any) { |
124 | const observed = this |
125 | const target = toRaw(observed) |
126 | const proto: any = Reflect.getPrototypeOf(target) |
127 | const wrap = isReadonly ? toReadonly : toReactive |
128 | track(target, OperationTypes.ITERATE) |
132 | function wrappedCallback(value: any, key: any) { |
133 | return callback.call(observed, wrap(value), wrap(key), observed) |
135 | return proto.forEach.call(target, wrappedCallback, thisArg) |
140 | function createIterableMethod(method: string | symbol, isReadonly: boolean) { |
141 | return function ( this : any, ...args: any[]) { |
142 | const target = toRaw( this ) |
143 | const proto: any = Reflect.getPrototypeOf(target) |
145 | method === 'entries' || |
146 | (method === Symbol.iterator && target instanceof Map) |
147 | const innerIterator = proto[method].apply(target, args) |
148 | const wrap = isReadonly ? toReadonly : toReactive |
149 | track(target, OperationTypes.ITERATE) |
155 | const { value, done } = innerIterator.next() |
159 | value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), |
166 | [Symbol.iterator]() { |
173 | function createReadonlyMethod( |
177 | return function ( this : any, ...args: any[]) { |
180 | const key = args[0] ? `on key "${args[0]}" ` : `` |
182 | `${capitalize(type)} operation ${key}failed: target is readonly.`, |
186 | return type === OperationTypes.DELETE ? false : this |
188 | return method.apply( this , args) |
193 | const mutableInstrumentations: any = { |
195 | return get( this , key, toReactive) |
205 | forEach: createForEach( false ) |
208 | const readonlyInstrumentations: any = { |
210 | return get( this , key, toReadonly) |
216 | add: createReadonlyMethod(add, OperationTypes.ADD), |
217 | set: createReadonlyMethod(set, OperationTypes.SET), |
218 | delete : createReadonlyMethod(deleteEntry, OperationTypes.DELETE), |
219 | clear: createReadonlyMethod(clear, OperationTypes.CLEAR), |
220 | forEach: createForEach( true ) |
224 | const iteratorMethods = [ 'keys' , 'values' , 'entries' , Symbol.iterator] |
225 | iteratorMethods.forEach(method => { |
226 | mutableInstrumentations[method] = createIterableMethod(method, false ) |
227 | readonlyInstrumentations[method] = createIterableMethod(method, true ) |
230 | function createInstrumentationGetter(instrumentations: any) { |
231 | return function getInstrumented( |
233 | key: string | symbol, |
237 | hasOwn(instrumentations, key) && key in target ? instrumentations : target |
238 | return Reflect.get(target, key, receiver) |
242 | export const mutableCollectionHandlers: ProxyHandler<any> = { |
243 | get: createInstrumentationGetter(mutableInstrumentations) |
246 | export const readonlyCollectionHandlers: ProxyHandler<any> = { |
247 | get: createInstrumentationGetter(readonlyInstrumentations) |
总结
至此,我们已把 vue-reactivity 这个 package 整个实现响应式数据的代码包都过了一遍,通过其源码,我们可以学习到不少东西,这些东西及知识点基本上在我们写业务系统的时候是没有机会用上场的。但现在 ES 6 甚至 ES 7 大环境都开始支持起来了。我们基本上很多新东西都可以尝试在业务系统上面用起来了。新的特性,我们可以根据场景有选择的去用上。比如 Typescript,目前 React / Vue 的 boilerplate 项目 / cl 基本已经提供了 Typescript 版本的模版。接下来,将要来探索一下 Vue Next 的其他 package 吧。
阅读:
3,396
作者: 博主
Talk is cheap, show me the code!
查看博主的所有文章