美文网首页
ES6-装饰器以及mobx中@observer为什么写在最后

ES6-装饰器以及mobx中@observer为什么写在最后

作者: 伪球迷也是球迷啊 | 来源:发表于2020-05-27 21:51 被阅读0次

在用mobx加antd项目中,发现runinaction后没有重新render,后来查阅文档发现有这样一句话


微信图片_20200527215532.png
@inject('MediaManagementStore')
@observer
@Form.create()
class A extends Com

最开始是这样写的,没有重新render,深入研究见下文。

装饰器ES6文档研究

装饰器目前只是提案阶段,与类相关的语法,主要用来注释或修改类和类的方法,D是一种函数,写成@Log,放在类和类方法的定义前面。

@Log
class Foo {
    @desFunc('string')
    method = () => {
        console.log('msg')
    }
}
  • 上面Log装饰器和desFunc分别是给类和类方法加的装饰,增加或修改类或类方法。
  • 装饰器对类的修改是是在代码编译阶段发生,本质是编译时执行的函数。
  • 想给类添加实例属性,可以对target.prototype对象进行操作。
  • 多个装饰器会像洋葱模型一样,由外到内进入,由内到外执行。(Mobx的@observer为什么要写在最内层,后面分析)
  • 装饰器不能用于函数,因为函数存在提升,而类不会提升,要装饰函数只能用高阶函数的方式去做。
  • 通过装饰器可以在一个类中混入另一个对象的方法,mixin模式
  • ++以上总结来自于ES6入门++

1. 类的装饰

/**
 * 导出装饰器
 * @param {*} args
 */
export default function log(...args) {
    console.log(args);
    if (args.length === 3) {
        validateClass(args[0], 'log');
    }
    if (typeof args[0] !== 'function') {
        return function (...argsClass) {
            return decorateHandler(argsClass, args[0]);
        };
    }

    return decorateHandler(args);
}

// 等同于

class A {}
A = log(A) || A;

传给装饰器的参数就是目标类,target 就是类本身(而不是其 prototype)


参数.png

2. 类方法(属性)装饰
此时装饰器相当于Object.defineProperty(obj, prop, descriptor) 的语法糖

/**
 * 导出装饰器
 * @param {*} args
 */
export default function test(...args) {
    console.log(args);
    /**
     * 这里是真正的 decorator
     * @target 装饰的属性所述的类的原型,注意,不是实例后的类。如果装饰的是 Car 的某个属性,这个 target 的值就是 Car.prototype
     * @name 装饰的属性的 key
     * @descriptor 装饰的对象的描述对象
     */
    return function (target, key, descriptor) {
        console.log(target);
        console.log(key);
        console.log(descriptor)
      }
}
参数.png
这里装饰的是继承自react.Component的类方法
3. 利用装饰器实现一个Log日志输出,展示component日志
  • 第一步装饰器导出
/**
 * 导出装饰器
 * @param {*} args
 */
export default function log(...args) {
    console.log(args);
    // 装饰类
    if (typeof args[0] !== 'function') {
        return function (...argsClass) {
            console.log(argsClass)
            return decorateHandler(argsClass, args[0]);
        };
    }

    return decorateHandler(args);
}
// 判断是不是装饰的类,如果是function直接throw error
const validateClass = (class, decorator) => {
    if (typeof class !== 'function') {
        throw new Error(`@${decorator} decorator can only be applied to class not function`);
    }
};
  • 判断是不是react.component
function decorateHandler(args, param = false) {
    const target = args[0];

    onlyError = param;
    // 只能应用在class
    validateClass(target, 'log');

    // 判断基类
    const baseTypeName = Object.getPrototypeOf(target).name;

    if (baseTypeName === 'ReactComponent' ) {
        return logClass(...args);
    }

    return logClass(...args);
}
  • log class 装饰器
 /**
 *  log class 装饰器
 * @param {*} target
 */
function logClass(target) {
    const opd = Object.getOwnPropertyDescriptors(target.prototype);
    Object.keys(opd).forEach((name) => {
        if (typeof opd[name].value === 'function') {
            proxyMethod(target, name);
        }
    });
}

Object.getOwnPropertyDescriptors(target.prototype)查看对象属性的特性,返回一个对象,所有原来的对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象

/**
 * 代理方法
 * @param {*} target
 * @param {*} name
 */
function proxyMethod(target, name) {
    const original = target.prototype[name];
    const wrapFunction = function (...args) {
        const beginTime = Date.now();

        let result;

        try {
            result = original.apply(this, args);
        } catch (e) {
            logger.error(e);
        }
        const logObj = {};

        if (args.length !== 0) {
            logObj.args = args;
        }

        const timeCost = Date.now() - beginTime;

        logObj.time = timeCost;
        if (this && this.state !== undefined) {
            logObj.state = this.state;
        }
        if (this && this.props !== undefined) {
            logObj.props = this.props;
        }
        !onlyError && logger.groupObj('@log', `${target.name} ${name}`, logObj);

        return result;
    };

    target.prototype[name] = wrapFunction;
}

logger.groupObj传给window.console.groupCollapsed展示props和state,console可设置占位符,具体可以百度文档。


实际展示效果.png

mobx @observer装饰器为什么写在最里层的问题

首先在node_modules/mobx下找到mobx.modules.js
直接上源码

/**
 * Turns an object, array or function into a reactive structure.
 * @param v the value which should become observable.
 */
function createObservable(v, arg2, arg3) {
    // @observable someProp;
    // 第二个参数是字符串,调用这个方法,干嘛的后面再去详细看,现在先找问题
    if (typeof arguments[1] === "string" || typeof arguments[1] === "symbol") {
        return deepDecorator.apply(null, arguments);
    }
    // it is an observable already, done
    // 第一个参数是不是可观察的对象,是就直接返回这个对象
    if (isObservable(v))
        return v;
    // something that can be converted and mutated?
    // 判断第一个参数是什么类型,分别调用不同的方法
    var res = isPlainObject(v)
        ? observable.object(v, arg2, arg3)
        : Array.isArray(v)
            ? observable.array(v, arg2)
            : isES6Map(v)
                ? observable.map(v, arg2)
                : isES6Set(v)
                    ? observable.set(v, arg2)
                    : v;
    // this value could be converted to a new observable data structure, return it
    if (res !== v)
        return res;
    // otherwise, just box it
    fail(process.env.NODE_ENV !== "production" &&
        "The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'");
}

var observable = createObservable;

export { observable };

上面写了几句中文注释,那么observable.array 这些方法是哪来的,可以继续看下面的observableFactories这个工厂对象,通过

// 给observable添加方法
Object.keys(observableFactories).forEach(function (name) { return (observable[name] = observableFactories[name]); });

好像看错了,后面把这块拆分到mobx源码解读里。

@observer 在mobx.js中

function observe(thing, propOrCb, cbOrFire, fireImmediately) {
    if (typeof cbOrFire === "function")
        return observeObservableProperty(thing, propOrCb, cbOrFire, fireImmediately);
    else
        return observeObservable(thing, propOrCb, cbOrFire);
}
function observeObservable(thing, listener, fireImmediately) {
    return getAdministration(thing).observe(listener, fireImmediately);
}
function observeObservableProperty(thing, property, listener, fireImmediately) {
    return getAdministration(thing, property).observe(listener, fireImmediately);
}
function getAdministration(thing, property) {
    if (!thing)
        fail("Expecting some object");
    if (property !== undefined)
        return getAdministration(getAtom(thing, property));
    if (isAtom(thing) || isComputedValue(thing) || isReaction(thing))
        return thing;
    if (isObservableMap(thing) || isObservableSet(thing))
        return thing;
    // Initializers run lazily when transpiling to babel, so make sure they are run...
    initializeInstance(thing);
    if (thing[$mobx])
        return thing[$mobx];
    fail("Cannot obtain administration from " + thing);
}

相关文章

网友评论

      本文标题:ES6-装饰器以及mobx中@observer为什么写在最后

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