携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
这期实现formily/reactive的一些api的源码,主要讲observable、autorun、define 核心思想是参考mobx,但我感觉更像vue3
formily/reactive
api
observable
可观察对象
reaction
订阅者,接收tracker函数,类似effect,执行时,函数内部会对里面的可观察对象进行依赖收集,当前reaction会跟对象属性进行绑定,当属性进行写操作时,reaction会被触发
订阅到派发订阅是个循环,每次tracker都会收集依赖,依赖变动触发tracker执行
antorun
自动执行的响应器,类似上面,接收tracker函数,当属性进行写操作时,tracker会被触发
源码
observable
使用
import { observable, autorun } from '@/@formily/reactive'
const values = {b:{c:d}}
const observableValues = observable(values)
observable.jsx
import { createObservable } from './internals';
import { MakeObservableSymbol } from './environment';
export function observable(target) {
return createObservable(null, null, target);
}
internals.jsx
import { isNormalType, isFn } from './checkers';
import { RawProxy, ProxyRaw, MakeObservableSymbol } from './environment';
import { baseHandler } from './handlers';
export const createObservable = (target, key, value) => {
//如果value不是对象,直接返回
if (typeof value !== 'object') return value;
const raw = ProxyRaw.get(value);
//如果能找到对应的原生对象,说明此原生对象已经创建过代理对象了
if (raw) {
return value;//说明它本身就是一个代理对象,可以直接返回
}
//如果value是个普通的对象话就可以创建响应式对象
if (isNormalType(value)) {
return createNormalProxy(value);
}
return value;
}
const createNormalProxy = (target) => {
// 创建代理对象
const proxy = new Proxy(target, baseHandler);
// 双向存map,{代理:对象},{对象:代理}
ProxyRaw.set(proxy, target);
RawProxy.set(target, proxy);
return proxy;
}
handlers.jsx
import { isObservable } from './externals';
import { RawProxy } from './environment'
import { createObservable } from './internals';
export const baseHandler = {
get(target, key) {
const result = target[key];//Reflect.get(target,key);
//如果此原生对象已经创建过代理对象了,直接返回
const observableResult = RawProxy.get(result);
if (observableResult) {
return observableResult;
}
//如果这个结果不是一个可观察对象,就返回它对应的可观察对象
//访问 {b:{c:d}} 的b,将{c:d}进行代理。深入代理
if (!isObservable(result)) {
return createObservable(target, key, result);
}
return result;
},
set(target, key, value) {
// 如果修改值为对象,则给加代理
const newValue = createObservable(target, key, value);
target[key] = newValue;
return true;
}
}
environment.jsx
//普通对象=>代理对象
export const RawProxy = new WeakMap();
//代理对象=>普通对象
export const ProxyRaw = new WeakMap();
checkers.jsx
const toString = Object.prototype.toString;
export const isPlainObject = (val) => toString.call(val) === '[object Object]';
export const isNormalType = (target) => {
return isPlainObject(target);
}
流程
判断值是否为对象,不是则返回原值
判断对象是否已经代理过,是的话返回其代理
若是普通对象,进行代理,用
map存储 对象和代理的映射关系在
get拦截中,如果拦截到属性结果的对象没代理过,给其代理上,也就是深入代理,set的时候也是
autorun
environment
// 全局正执行的tracker事件存放
export const ReactionStack = [];
// 属性 映射 tracker
export const RawReactionsMap = new WeakMap();
autorun
类似vue3的effect
import { ReactionStack } from './environment';
export function autorun(tracker) {
//reaction本质上是一个函数,当它观察到的对象和属性发生变化,此函数会重新执行
//同vue3类似,将当前tracker存放全局变量,执行tracker
const reaction = () => {
ReactionStack.push(reaction);
tracker();
ReactionStack.pop();
}
reaction();
}
handlers
import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey } from './reaction';
export const baseHandler = {
get(target, key) {
...
// 对象属性绑定 tracker
bindTargetKeyWithCurrentReaction({ target, key });
...
},
set(target, key, value) {
const newValue = createObservable(target, key, value);
target[key] = newValue;
// trigger,执行tracker更新数据
runReactionsFromTargetKey({ target, key });
return true;
}
}
reaction
import { ReactionStack, RawReactionsMap } from './environment';
/**
* 把某个对象的某个key和当前的reaction进行绑定
* @param {*} operation {target,key}
*/
export const bindTargetKeyWithCurrentReaction = (operation) => {
const { target, key } = operation;
//最后一个Reaction就是currentReaction
const currentReaction = ReactionStack[ReactionStack.length - 1];
if (currentReaction) {
addRawReactionsMap(target, key, currentReaction)
}
}
//weakMap对象{ map对象:{ 属性:[tracker...] }}
const addRawReactionsMap = (target, key, reaction) => {
//判断此target对象在RawReactionsMap里有没有值
const reactionsMap = RawReactionsMap.get(target);
if (reactionsMap) {
const reactionSet = reactionsMap.get(key);
if (reactionSet) {
reactionSet.add(reaction);
} else {
let reactionSet = new Set();
reactionSet.add(reaction);
reactionsMap.set(key, reactionSet);
}
return reactionsMap;
} else {
//ArraySet 元素唯1的数组
let reactionSet = new Set();//源码里作者自己封装了一个ArraySet
reactionSet.add(reaction);
const reactionsMap = new Map([[key, reactionSet]]);
RawReactionsMap.set(target, reactionsMap);
return reactionsMap;
}
}
//weakMap对象{ map对象:{ 属性:[tracker...] }} 拿出tracker执行
export const runReactionsFromTargetKey = (operation) => {
const { target, key } = operation;
runReactions(target, key);
}
function runReactions(target, key) {
const reactions = getReactionsFromTargetKey(target, key);
for (let reaction of reactions) {
reaction();
}
}
const getReactionsFromTargetKey = (target, key) => {
const reactionsMap = RawReactionsMap.get(target);
if (reactionsMap) {
return reactionsMap.get(key)
} else {
return new Set();
}
}
流程
类似
vue3
autorun(tracker),tracker是当函数内部可观察属性值变动时重新触发的函数在可观察代理拦截
get和set,将tracker放全局,执行tracker,触发访问可观察对象,触发get,在之中进行属性和tracker的绑定,当可观察对象属性值变化时,触发set,执行属性对应的tracker们
define
指定具体属性的响应式行为
使用
define(form, {
values: observable,
fields: observable
});
目前支持的所有 Annotation 有:
observable/observable.deep 定义深度劫持响应式属性
observable.box 定义 get/set 容器
observable.computed 定义计算属性
observable.ref 定义引用劫持响应式属性
observable.shallow 定义浅劫持响应式属性,只代理一层
action/batch 定义批处理方法
现在来实现 observable
environment
弄个 symbol标识
export const MakeObservableSymbol = Symbol("MakeObservableSymbol");
observable
import * as annotations from "./annotations";
//observable.shallow = annotations.shallow;
// 等于下面的文件里的observable,返回函数,有个属性MakeObservableSymbol,赋值maker,两个symbol
observable[MakeObservableSymbol] = annotations.observable;
annotations/observable
import { createAnnotation, createObservable } from '../internals';
import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey } from '../reaction';
// 返回函数,有个属性MakeObservableSymbol,赋值maker
export const observable = createAnnotation(
//maker 将对象转化成代理并劫持
({ target, key, value }) => {
//先把target对象的key属性变成可观察对象并存在store.value属性上
const store = {
value: createObservable(target, key, target[key])
}
function get() {
bindTargetKeyWithCurrentReaction({ target, key });
return store.value;
}
function set(value) {
value = createObservable(target, key, value);
store.value = value;
runReactionsFromTargetKey({ target, key });
}
Object.defineProperty(target, key, {
get, set, enumerable: true, configurable: false
});
return store.value;
}
);
model(入口)
import { isObservable, isAnnotation } from './externals';
import { getObservableMaker } from './internals';
export function define(target, annotations) {
//如果是代理对象了,返回
if (isObservable(target)) {
return target;
}
//{value:observable,fields: observable}
for (const key in annotations) {
// annotation:observable
const annotation = annotations[key];
//如果annotation是一个合法的注解的话才会进入 如observable
if (isAnnotation(annotation)) {
// 获取maker 将对象转化成代理并劫持 ok
getObservableMaker(annotation)({ target, key });
}
}
}
internals
export const isAnnotation = (target) => {
return target && target[MakeObservableSymbol];
}
// 返回函数,有个属性MakeObservableSymbol,赋值maker
export const createAnnotation = (maker) => {
const annotation = () => {};
if (isFn(maker)) {
annotation[MakeObservableSymbol] = maker;
}
return annotation;
};
// 如果是observable,拿到maker要两层
export const getObservableMaker = (target) => {
if (target[MakeObservableSymbol]) {
if (!target[MakeObservableSymbol][MakeObservableSymbol]) {
return target[MakeObservableSymbol];
}
return target[MakeObservableSymbol][MakeObservableSymbol];
}
//return annotation[MakeObservableSymbol];
};
流程
define(form, { values: observable, });
在之中,observable是注解,
定义
observable[MakeObservableSymbol] = annotations.observable;,只要判断注解是否存在MakeObservableSymbol就可以知道注解是否正确了
annotations.observable是一个函数,参数是maker函数(可以让对象转化为可观察对象),最终返回一个函数,这个函数的MakeObservableSymbol赋值为maker。
define时,先检测属性的注解observable是否正确,也就是检测observable[MakeObservableSymbol]是否存在,正确则,获取其maker函数,将该属性转化为 可观察对象。
observable.shallow
简单讲就是多了个和RawProxy一样的 RawShallowProxy,当对shallow对象进行观察时,将其 对象和代理存进去。
baseHandlers不变,当访问对象里的属性,值是对象时,会进行 createObservable
此时shallow对象会在RawShallowProxy其中找到,直接返回,不继续代理了。
# observable.jsx
observable.shallow = annotations.shallow
# annotations/shallow.jsx
// createObservable多了个true
export const shallow = createAnnotation( ({ target, key, value }) =>
{ const store = { value: createObservable(target, key, target[key], true),
}
# reactive\internals
export const createObservable = (target, key, value, shallow) => {
。。。
//拦截二层代理
if (target) {
const parentRaw = ProxyRaw.get(target) || target
const isShallowParent = RawShallowProxy.get(parentRaw)
if (isShallowParent) return value
}
if (shallow) return createNormalProxy(target, true)
}
const createNormalProxy = (target: any, shallow?: boolean) => {
const proxy = new Proxy(target, baseHandlers)
ProxyRaw.set(proxy, target)
if (shallow) {
RawShallowProxy.set(target, proxy)
} else {
RawProxy.set(target, proxy)
}
return proxy
}