import { getType, bindContext, uuid } from "./util"; export class Observe {} export type autorunFn = () => void export type DestoryFn = autorunFn export type GetDestory = (fn: autorunFn) => DestoryFn export type CurrentFn = (fn: GetDestory) => autorunFn const defaultCurrent = () => () => {} // observer数据get时,进行依赖手机 let currentFn: CurrentFn = defaultCurrent /** * 依赖 * @class Depends */ export class Depends { list: autorunFn[] = [] /** * 手机当前依赖 * @memberof Depends */ collect() { if (currentFn !== defaultCurrent) { currentFn((fn) => { // 获取依赖函数,并返回移除依赖函数 !this.list.includes(fn) && this.list.push(fn) return () => { this.list = this.list.filter(i => i !== fn) } }) } } /** * 执行收集的依赖 * @memberof Depends */ run() { this.list.forEach(fn => fn()) } } class ObserveId { static id = 0 id = uuid() + ObserveId.id++ keys = [] } function addObserverId(newObj) { if (newObj.__observeId__ instanceof ObserveId) { return } Object.defineProperty(newObj, '__observeId__', { value: new ObserveId(), configurable: false, writable: false, enumerable: false, }) } export function isObserve(obj) { return (obj instanceof Observe) || (obj instanceof Array && '__observe__obj' in obj) } export function addObserve(ctx: any, key: string, defaultValue = ctx[key], init = false) { addObserverId(ctx) // 这里隐含了一个bug,如果state,prop被更改,就懵逼了,因此设置state、props writable: false if (ctx.__observeId__.keys.includes(key)) { ctx[key] = defaultValue // 数据已监听,则更新 return } else { ctx.__observeId__.keys.push(key) } let value = bindContext(defaultValue, ctx) // 依赖此key的函数 const depends = new Depends() let initH = init // 默认bind this Object.defineProperty(ctx, key, { get() { // 收集依赖 depends.collect() // 只有在被获取的时候,才会对数据进行observe if (!initH && !isObserve(value)) { value = observer(value, true, depends) } else { initH = false } return value }, set(newValue) { value = bindContext(newValue, ctx) // 触发依赖函数更新 depends.run() }, enumerable: true, }) } /** * 监听数组 * @param arr * @param parentDepends */ export function observeArr(arr = [], parentDepends) { const newArr = arr.map(item => observer(item)) Object.defineProperty(newArr, '__observe__', { value: true, enumerable: false, }) ;[ 'splice', 'push', 'shift', 'unshift', 'pop', ].forEach(key => { const value = [][key].bind(newArr) // 如果数组长度被修改,通知他的父元节点 Object.defineProperty(newArr, key, { get() { return (...args) => { if (key == 'push' || key == 'unshift') { args = args.map(i => observer(i, true)) } else if (key == 'splice') { args = args.slice(0, 2).concat( args.slice(2).map(i => observer(i, true)) ) } const result = value(...args) // 对于数组的变化,直接出发调用数组的依赖 parentDepends.run() return result } }, enumerable: false, }) }); return newArr } /** * 监听对象 * @param obj * @param init */ export function observeObj(obj = {}, init = false) { let newObj = new Observe() // 添加id addObserverId(newObj) Object.keys(obj).forEach(key => { addObserve(newObj, key, obj[key], init) }) return newObj } /** * 构造一个新的observe对象 * @param {objet} obj * @returns */ export function observer(obj, init = false, parentDepends?: Depends) { if (isObserve(obj)) { return obj } switch (getType(obj)) { case 'array': return observeArr(obj, parentDepends) case 'object': return observeObj(obj, init) default: return obj } } /** * 接受函数,当依赖的数据发生变化后,会立即执行函数 * * @param {function} fn * @returns */ export function autorun(fn: ()=> void) { let destoryDepends: DestoryFn[] = [] // 销毁依赖 const destory = () => { destoryDepends.forEach(fn => fn()) destoryDepends = [] } const wrapFn = () => { destory() // 销毁上次依赖监听 // 收集本次依赖 currentFn = (getDestory) => { destoryDepends.push(getDestory(wrapFn)) return wrapFn } fn() // 重置回默认值 currentFn = defaultCurrent } // 立即执行 wrapFn() return destory }