Vue中实现简单的AOP

有个简单的需求,需要在前端记录用户的一些行为,联想到Java中可以用annotation加拦截函数调用实现,试着在JS中看看能不能也这样实现。


annotation或者decorator的功能在JS中还处在草稿阶段,但是通过Babel的转换,已经可以使用了,例如React中的mobx状态管理,就可以通过decorator实现。下面是简单的实现过程:


先通过npm安装babel-plugin-transform-decorators-legacy,然后在babelrc文件中的plugins数组里加入transform-decorators-legacy,这样就能编译加了decorator的vue文件了

export default {
  ......
	methods: {
		@anno('test')
		transform () {	
		}
	}
};

function anno (...args) {
	return function (target, key, descriptor) {
      console.log(args);
		return descriptor;
	};
};

具体decorator的函数参数都表示什么意思,请参考网上文档,不重复了。但现在的问题就是我们需要在transform函数执行之前和执行之后执行相应的记录操作,该如何实现呢,大家一定想到了可以用函数的call或者apply

function anno (...args) {
	return function (target, key, descriptor) {
		let method = descriptor.value;
		descriptor.value = function () {
			console.log('before ...', args);
			method.apply(target, arguments);
			console.log('after ...', args);
		}
		return descriptor;
	}
}

但是,这样是不行的。因为target根本不是vue组件运行时的this,仅仅是表示methods的对象,所以在transform用this去访问vue组件的各种属性都会报错。


后来我又看到vue本身就可以写成ES6的class形式,vue官方提供了vue-class-component插件,可以通过npm进行安装,具体可以参考:https://github.com/vuejs/vue-class-component,这个插件本身提供了一个createDecorator工具方法,看一下官方的例子:

// decorators.js
import { createDecorator } from 'vue-class-component'

export const NoCache = createDecorator((options, key) => {
  // component options should be passed to the callback
  // and update for the options object affect the component
  options.computed[key].cache = false
})

-------------------------------------

import { NoCache } from './decorators'

@Component
class MyComp extends Vue {
  // the computed property will not be cached
  @NoCache
  get random () {
    return Math.random()
  }
}

用这个工具方法可以访问到vue组件里的各种属性了,但是,但是,仅仅能访问到各种属性而已,这里的options仍然不是运行时表示组件的this,就是说仍然不能实现像Java中AOP那种拦截方法的操作。


下面看一下真正的实现方案,我们还需要返璞归真,用JS中的原型,去扩展AOP功能。我们知道所有js中函数的原型都是Function.prototype,所以我们需要扩展这个原型:

//以下代码来源于网上,非原创
Function.prototype.before = function (beforefn) {
	var __self = this;
	return function () {
		beforefn.apply(this, arguments);
		return __self.apply(this, arguments);
	};
};

Function.prototype.after = function (afterfn) {
	var __self = this;
	return function () {
		var ret = __self.apply(this, arguments);
		afterfn.apply(this, arguments);
		return ret;
	};
};

Function.prototype.around = function (beforefn, afterfn) {
	var __self = this;
	return function () {
		beforefn.apply(this, arguments);
		var ret = __self.apply(this, arguments);
		afterfn.apply(this, arguments);
		return ret;
	};
};
在Function.prototype上添加了三种拦截的模式,仿照Java中的before,after和around。那么该如何应用到vue中的方法上呢,还需要继续写对应的 decorator:

function before (...args) {
	return function (target, key, descriptor) {
		descriptor.value = descriptor.value.before(() => console.log(...args));
	};
};

function after (...args) {
	return function (target, key, descriptor) {
		descriptor.value = descriptor.value.after(() => console.log(...args));
	};
};

function around (...args) {
	return function (target, key, descriptor) {
		descriptor.value = descriptor.value.around(() => console.log(...args), () => console.log(...args));
	};
};

上面仅仅是简单打印了传入的参数,大家可以根据自己的需要在Function.prototye.before,after,around方法中加入自己的业务逻辑。


然后就可以真正应用到vue的方法上了:

@around('test', '123', '456')
transform () {
//这里可以使用this访问vue组件中的各种属性,比如this.$refs等
}
至此实现了在vue中进行简单的AOP操作,上述代码有不足的地方大家可以自行改进。








猜你喜欢

转载自blog.csdn.net/kittyjie/article/details/79158439