历史

以前做后端时,接触过一点Spring,也是第一次了解DI、IOC等概念,面向切面编程,对于面向对象编程还不怎么熟练的情况下,整个人慌的一批,它的日志记录、数据库配置等都非常方便,不回侵入到业务代码中,后来转战前端,就没怎么关注了.....

JS引入DI编程概念

学习 redux 时,看到语法里面有 @ 符号,卧槽,后端已经侵入到前端啦,不知不觉中,前端已经这么NB了,再也不是写写页面,用个框架,绑定个事件啦,已经把后端的一些经典设计思想融入进来了

对于前端开发而言,如果有一种方式,能够将一些非业务代码,甚至抽象的东西,无侵入的方式挂载到业务代码上,那么对于个人而言,这真是一种解放,太帅了......

装饰器初探

1.给方法记录一下log

@log
class Numberic {
add(...nums) {
return nums.reduce((p, n) => (p + n), 0)
}
} function log(target) {
// Numberic
const desc = Object.getOwnPropertyDescriptors(target.prototype)
/**
* desc
add:
configurable: true - 可配置
enumerable: false - 可枚举
value: ƒ ()
writable: true - 可改写
__proto__: Object constructor:
configurable: true
enumerable: false
value: ƒ Numberic()
writable: true
__proto__: Object
*/ for (const key of Object.keys(desc)) {
if (key === 'constructor') {
continue
} const func = desc[key].value if ('function' === typeof func) {
Object.defineProperty(target.prototype, key, {
value(...args) {
console.log('before ' + key)
const ret = func.apply(this, args)
console.log('after ' + key)
return ret
}
})
}
}
} new Numberic().add(2)
// before add
// 2
// after add

2.给属性添加readonly校验

@log
class Numberic {
@readonly PI = 3.1415126 add(...nums) {
return nums.reduce((p, n) => (p + n), 0)
}
} function readonly(target, key, descriptor) {
descriptor.writable = false
} new Numberic().PI = 100
// 报错

3.给一个表单提交进行校验

var validateRules = {
expectNumber(value) {
return Object.prototype.toString.call(value) === '[object Number]'
},
maxLength(value) {
return value <= 30
}
} function validate(value) {
return Object.keys(validateRules).every(key => validateRules[key](value))
} function enableValidate(target, key, descriptor) {
const fn = descriptor.value
if (typeof fn === 'function') {
descriptor.value = function(value) {
return validate(value)
? fn.apply(this, [value])
: console.error('Form validate failed!')
}
}
} class Form {
@enableValidate
send(value) {
console.log('This is send action', value)
}
} let form = new Form()
form.send(44) // Form validate failed!
form.send('12') // Form validate failed!
form.send(12) // This is send action 12

应用React与mobx

import React, { Component } from 'react'
import { render } from 'react-dom'
import { observable, action } from 'mobx'
import { observer } from 'mobx-react' import { Log, Required, TrackInOut } from './decorator.js' // store
@Log
class User {
@observable name = ''
@observable password = '' @action setName = val => {
this.name = val
} @action setPwd = val => {
this.password = val
} @action login = (info) => {
console.log('ready to login', info.name, info.password)
}
} const userStore = new User() @observer
class Login extends Component {
constructor(props){
super(props)
console.log('原始组件的constructor')
} @Required(['name', 'password'])
login(info) {
this.props.store.login(info)
} componentDidMount() {
console.log('原始组件的cmd')
} render() {
let { name, password, setName, setPwd } = this.props.store
return (
<div className="login-panel">
<input type="text" value={name} onChange={e => setName(e.target.value)}/>
<input type="password" value={password} onChange={e => setPwd(e.target.value)}/><br/>
<button onClick={() => this.login({ name, password })}>登录</button>
</div>
)
}
} render(<Login store={userStore} />, document.getElementById('root')) import _ from 'lodash'
import React from 'react' // 获取方法参数的名称列表
const getArgumentsList = func => {
var funcString = func.toString();
var regExp =/function\s*\w*\(([\s\S]*?)\)/;
if(regExp.test(funcString)){
var argList = RegExp.$1.split(',');
return argList.map(function(arg){
return arg.replace(/\s/g,'');
});
}else{
return []
}
} // 记录日志
export const Log = target => {
const desc = Object.getOwnPropertyDescriptors(target.prototype)
for (const key of Object.keys(desc)) {
if (key === 'constructor') {
continue
} const func = desc[key].value if ('function' === typeof func) {
Object.defineProperty(target.prototype, key, {
value(...args) {
console.log(`before ${key}`)
const ret = func.apply(this, args)
console.log(`after ${key}`)
return ret
}
})
}
}
} // 只读
export const Readonly = (target, key, descriptor) => {
descriptor.writable = false
} // 必传参数
export const Required = checkArr => {
return (target, key, descriptor) => { const fn = descriptor.value
// console.log(target, key, descriptor)
if (typeof fn === 'function') {
descriptor.value = function(args) {
console.log('required')
if (_.isPlainObject(args)) {
if (checkArr && checkArr.length > 0) {
for (let a of checkArr) {
if (!args[a]) {
throw new Error(`[required] params ${a} of ${key} is undefined or null!`)
}
}
}
} else if (_.isArray(args)) {
if (args.length == 0) {
throw new Error(`[required] params ${getArgumentsList(fn)[0]} of ${key} length is 0!`)
}
} else {
if (_.isEmpty(args)) {
throw new Error(`[required] params ${getArgumentsList(fn)[0]} of ${key} is undefined!`)
}
} fn.apply(this, [args])
}
}
// console.log(target)
// console.log(key)
// console.log(descriptor)
// console.log(checkArr)
}
}

直接应用在mobx上

import React, { Component } from 'react'
import { render } from 'react-dom'
import { observable, action, computed } from 'mobx'
import { observer } from 'mobx-react' //custom
import { Log, Required, Track } from './decorator.js' // store
@Log
class User {
@observable name = ''
@observable password = '' @action setName = val => {
this.name = val
} @action setPwd = val => {
this.password = val
} @Required(['name', 'password'])
@Track({ evt: '1', data: 'test', execute: 'after' })
@action
login(info) {
// login 方法如果想要使用Required,则不能使用箭头函数
console.log('login', info.name, info.password)
}
} const userStore = new User() @observer
class Login extends Component {
render() {
let { name, password, setName, setPwd } = this.props.store
return (
<div className="login-panel">
<span style={{display:'inline-block', width: 80}}>用户名:</span><input type="text" value={name} onChange={e => setName(e.target.value)}/><br/>
<span style={{display:'inline-block', width: 80}}>密码:</span><input type="password" value={password} onChange={e => setPwd(e.target.value)}/><br/>
<button onClick={() => this.props.store.login({ name, password })}>登录</button>
</div>
)
}
} render(<Login store={userStore} />, document.getElementById('root'))

无侵入式埋点

最近在做系统的埋点,很多地方要加入埋点,尤其是在一些事件上,如果按照以前的思路,就得将大量的埋点代码侵入到业务代码上,维护上就有点费劲了,因此联想到ES7的decorate 装饰器,可以IOC的方式进行编程,因此,做了一点东西,希望可以给大家带来一点启发

上面的装饰器可以挂载到 function、react的方法、mobx-stroe的action上,但如果有一个需求是这样的,react中,想在进入页面时进行埋点,上面的方法就不太适用了,因为在一个组件上挂载装饰器,它能获取到的上下文对象只是这个组件,既然能获取到这个组件,那么不妨HOC一下,高阶组件一把

发现 高阶组件的constructor 优先与原始组件的 constructor,同时componentDidMount反而晚于原始组件的componentDidMount,因此可以这样改,来根据需求进行埋点

Decorator - 利用装饰器武装前端代码的更多相关文章

  1. flask中的endpoint、自定义转化器、与djnago中session区别、利用装饰器实现登录认证

    flask路由中的endpoint 与自定义转化器 ''' endpoint主要用于 反向解析, 例如:login函数中配的路由是/login,其中endpoint='lg' 则在其他函数,可以用 u ...

  2. python3 第二十二章 - 函数式编程之Decorator(装饰器)

    前面我们说了,在python中,一切皆对象.函数也是一个对象,而且函数对象可以被赋值给变量,通过变量也能调用该函数.如: def sayHello(name): print(name + ' hell ...

  3. tornado利用装饰器记录每个http请求

    python利用装饰器记录每个http请求 设置装饰器 from functools import wraps from datetime import datetime ""&q ...

  4. 简单理解 ES7 Decorator(装饰器)

    如何使用ES7 Decorator给你的游戏人物开挂? // 预告: 本文有点小难度,对js不太熟的人可能比较懵逼 // 本文的目的是让你们知其然 // ======================= ...

  5. Decorator模式 装饰器模式

    Android 使用了装饰器模式 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加 ...

  6. 装饰器,栈 ,asyncio 代码

    装饰器目的: 不改变原来代码的基础上. 给函数添加新功能动态代理. 拦截器 通用装饰器的写法def wrapper(fn): def inner(*args, **kwargs): '''之前''' ...

  7. $如何用Python装饰器实现一个代码计时器?

    有时候我们很希望看到程序中某个函数或某个代码段的耗时情况,那么该如何办呢?本文用两种方式实现了代码计时器的功能,第一种方式是采用装饰器来实现,第二种方式采用上下文管理器实现. 其实计算代码的运行时间, ...

  8. python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查

    在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ...     return x + y ...

  9. java 装饰器模式实现代码

    目录 1.实现装饰器模式 1.1.公共接口 1.2.接口实现 1.3.装饰器 1.4.装饰构件 1.5.测试装饰器 上图展示的是io流中的一个装饰者模式的代码结构 1.实现装饰器模式 汽车厂生产汽车实 ...

随机推荐

  1. [JZOJ 5885] [NOIP2018模拟9.27] 物理实验 解题报告 (思维)

    题目链接: https://jzoj.net/senior/#main/show/5885 题目: 题解: 把$a$数组按升序排序 我们可以枚举$x$,发现对于任意$x$,最优情况下$y$一定等于$x ...

  2. oracle 11g rac for linux add node (oracle 11g rac 节点添加)

    说明: Adding Oracle RAC to Nodes with Oracle Clusterware Installed步骤来自ORACLE 官方文档: https://docs.oracle ...

  3. C++之虚函数表

    本文引自:http://songlee24.github.io/blog/2014/09/02/c-plus-plus-jin-jie-zhi-xu-han-shu-biao/ C++通过继承(inh ...

  4. 学习es6 setter/getter研究

    1.背景 在ES6中,我们对类的定义如下 class Person { // 构造函数 constructor (name) { // 属性初始化 this.name = name; } // 成员方 ...

  5. Android之通过HttpURLConnection.getResponseCode状态码抛出异常的问题以及解决方法

    1.最近,在学习解析json数据的时候遇到一个错误信息,错误信息如下图所示:   发现解析出来的数据为空,错误信息如上图所示,发现程序中的HttpUtils工具类的22行出现了错误和MainActiv ...

  6. 读 Real-Time Rendering 收获 - chapter 6. texturing

    Texturing, at its simplest, is a techinique for efficiently modeling the surface's properties.

  7. hdu1045 - 贪心,二分图

    题目链接 左边白方格里放小球,满足同一行.列只有一个(被黑块隔开).问最多放多少个球. -------------------------------------------------------- ...

  8. ActiveMQ学习笔记(8)----ActiveMQ的消息存储持久化

    1. 概述 ActiveMQ不仅支持persistent和non-persistent两种方式,还支持消息的恢复(recovery)方式. 2. PTP Queue的存储是很简单的,其实就是FIFO的 ...

  9. fwupdate-efi 与 grub2-common 冲突

    在CentOS-7Minimal系统中使用命令如下命令yum groupinstall -y "GNOME Desktop"安装 图形界面时提示:fwupdate-efi 与 gr ...

  10. visio使用技巧

    1.背景放大: 按住Ctrl键,滚动鼠标滚轮,即可调整背景大小,这是改变的显示比例 2.背景: 按住Ctrl+E,在背景边框处拖动图标,即可拉大或者缩小背景,这是实际更改背景大小.