美文网首页
mobx及mobx-react的简单实现

mobx及mobx-react的简单实现

作者: Mr无愧于心 | 来源:发表于2022-04-19 14:18 被阅读0次
    1. Mbox的observable的方法主要实现的是对状态的深度代理
      (mobx4对应Object.defineProperty(),mobx5对应new Proxy())
      <类似于vue2、vue3的数据监听原理>
    2. 在autorun方法使用的时候,会在这个代理过程中执行状态的依赖收集的相关操作,触发对应的handle函数。
    import { observable, autorun } from "./mobx" 
    // observable方法用于完成对状态对象的Proxy的代理,将状态变为可观察对象
    const o = observable({ name: "1" });
    // autorun方法类似于vue中的watcher,其中传递进去一个handler Function,
    // 这个回调函数会在初始化的时候被执行一次,之后每次内部相关的observable中的依赖发生变动时被再次调用
    autorun(() => {
        console.log(o.name)
    })
    // 直接设置 o.name 值
    o.name = "2"
    
    // 打印: 1
    // 打印: 2
    

    实现observable和autorun

    1. 包装对象值的Observable,核心原理是Object.defineProperty(),给被包装的属性套上get和set钩子,在get中收集依赖,在set上触发监听函数。
    // 这里后面的两个参数: key 和 descriptor主要用于之后的装饰器实现
    export defualt function observable(target, key, descriptor){
        // 这里支持装饰器模式的observable写法:
        if(typeof key === "string"){
            // 如果是作为装饰器装饰属性进行监听,先将装饰的对象进行深度代理
            let v = descriptor.initializer();
            v = createObservable(v);
            // 这里执行依赖搜集: 使用的Reaction类会在之后实现
            let reaction = new Reaction();
            // 返回描述器
            return {
                enumerable: true,
                configurable: true,
                get(){
                    reaction.collect();  // 再获取target属性时进行autorun中的handler的依赖搜集
                    return v;
                },
                set(value){
                    v = value;
                    reaction.run();  // 在每次更新target中的属性时执行autorun中的依赖
                }
            }
        }
        // 如果不是装饰器写法,则创建Proxy代理
        return createObservable(target);
    }
    
    // 创建代理对象
    function createObservable(val){
        // 用于生成代理对象的控制器:
        const handler = () => {
            // 实例化Reaction在autorun获取属性的时候进行依赖搜集
            let reaction = new Reaction();
            return {
                set(target, key, value){
                    // 执行搜集绑定, 此时修改值需要先执行,这样在autorun中的handler中才能拿到最新的值
                    let r = Reflect.set(target, key, value)
                    reaction.run();
                    return r;
                },
                get(target, key){
                    // 在获取属性值的时候进行依赖搜集
                    reaction.collect()
                    return Reflect.get(target, key);
                }
            }
        }
        // 进行深层Proxy代理返回: 针对如: {name: "chensir", age: {num: 21}}这样的对象
        return deepProxy(val, handler)
    }
    
    // 深度设置Proxy对象代理
    function deepProxy(val, handler){
        if(typeof val !== "object"){
            return val;
        }
        // 深度递归进行Proxy代理,此时的递归树相当于是后序遍历进行代理
        for(let key in val){
            val[key] = deepProxy(val[key], handler);
        }
        return new Proxy(val, handler);
    }
    
    1. 实现Reaction类进行状态搜集,作为abservable和autorun之间的桥梁
    // 定义两个全局变量,这里是简单实现,所以和实际的源码实现有一定的区别
    let nowFn = null;  // 这个表示当前的autorun中的handler方法
    let counter = 0;  // 这里使用counter记录一个计数器值作为每个observable属性的id值进行和nowFn进行绑定
    
    class Reaction {
        constructor(){
            // 标识每一个proxy对象
            this.id = ++counter;  // 这里采用一个比较low的方法简易实现的,在每次对observable属性进行Proxy的时候,对Proxy进行标记
            this.store = {};  // 存储当前可观察对象对应的nowFn, 写入的形式如: {id: [nowFn]}
        }
        collect(){
            // 进行依赖搜集,只当当前有autorun绑定了相关属性观察后才会进行绑定
            if(nowFn){   // 通过这个判断主要是因为只有在调用autorun绑定的时候才会设置这里的nowFn
                this.store[this.id] = this.store[this.id] || [];
                this.store[this.id].push(nowFn);    
            }
        }
        run(){
            // 运行依赖函数
            if(this.store[this.id]){
                this.store[this.id].forEach(fn => {
                    fn()
                })
            }
        }
        // 定义两个静态方法,用于在调用autorun方法时候对nowFn进行设置和消除
        static start(handler){
            nowFn = handler;
        }
        // 在注册绑定这个就要清空当前的nowFn,用于之后进行进行搜集绑定
        static end(){
            nowFn = null;
        }
    }
    
    
    1. 实现autorun方法,进行简单的依赖搜集
    export default function autorun(handler){
        if(typeof handler !== "function"){
            throw new TypeError(`autorun function expect a function but get a ${typeof handler}`)
        }
        // 开始搜集依赖,设置Reaction中的nowFn
        Reaction.start(handler)
        // 执行一次handler,在handler中有对于相应属性的getter获取,此时就可以设置改属性的Proxy的Reaction状态依赖
        handler()
        // 清除nowFn
        Reaction.end()
    }
    
    

    实现mobx-react的@observer

    var ReactMixin = {
        componentWillMount: function() {
            autorun(() => {
                this.render();
                this.forceUpdate();
            });
        }
    };
    function observer(target) {
        const targetCWM = target.prototype.componentWillMount;
        target.prototype.componentWillMount = function() {
            targetCWM && targetCWM.call(this);
            ReactMixin.componentWillMount.call(this);
        };
    }
    

    mobx和redux的对比

    • 相同的地方
      mobx 和 redux 都是单向数据流,通过 action 触发全局 state 更新,然后通知视图。

    • 不同的地方

      1. 修改状态的方式不同,
        react每次都是修改同一个状态对象,基于响应式代理,也就是Object.defineProperty代理get\set的处理,get时把依赖收集起来,set修改时通知所有的依赖做更新
        redux是每次返回一个全新的状态
      2. 管理状态的思路上
        redux 那种方式是函数式的思路,所以状态的修改都在一个个 reducer 函数里每次返回新的state,而 mobx 那种方式则是面向对象的代理的思路,所以很容易把 state 组织成一个个 class
      3. 性能方面
        mobx 的响应式能精准的通知依赖做更新,而 redux 只能全局通知,而且 mobx 只是修改同一个对象,不是每次创建新对象,性能会比 redux 更高

    相关文章

      网友评论

          本文标题:mobx及mobx-react的简单实现

          本文链接:https://www.haomeiwen.com/subject/lybquctx.html