本文主要记录如何使用高阶组件实现外部逻辑注入,如性能打点,以及其中遇到的一些问题
The defination of HOC
所谓高级组件,即:接受一个组件作为参数,并且其返回值也为一个react组件
demo
如下是一个再简单不过的react组件
import connect from './connect'
import {Component} from 'react'
class Demo extends Component {
componentWillMount(){
//do something here
}
render(){
return <div>111</div>
}
}
export default connect(Demo)
connect
是一个实现性能打点的装饰器,here is the source code
装饰器,在本质上,其实是返回一个函数的高阶函数。在react应用中,衍生出了高阶组件的含义
以下是两种HOC的实现方式:
方式一:Props Proxy
//性能追踪:渲染时间打点
export default (Target) => (props)=>{
let func1 = ['componentWillMount']
let func2 = ['componentDidMount']//Demo并没有在prototype上定义该方法,func2为undefined,但是并不会有影响,这样做只是为了事先提取出可能定义的逻辑,保持原函数的纯净
let begin, end;
['componentWillMount'] = function (...argus){//do not use arrow funciton to bind 'this' object
(this,argus);//执行原有的逻辑
begin = ();
}
['componentDidMount'] = function (...argus){
(this,argus);//执行原有的逻辑
end = ();
(+'组件渲染时间:'+(end-begin)+'毫秒')
}
return <Target {...props}/>//do not forget to pass props to the element of Target
}
connect 装饰器可以实现保持原有方法componentWillMount
和componentDidMount
纯净的情况下,实现打点逻辑的无污染注入。经过装饰以后的组件,就可以在控制台打印出该组件的渲染时间。
方式二: Inheritance Inversion
// 另一种HOC的实现方式 Inheritance Inversion
export default Target => class Enhancer extends Target {
constructor(p){
super(p);//es6 继承父类的this对象,并对其修改,所以this上的属性也被继承过来,可以访问,如state
this.end =0;
this.begin=0;
}
componentWillMount(){
super.componentWilMount && super.componentWilMount();// 如果父类没有定义该方法,直接调用会出错
this.begin = ();
}
componentDidMount(){
super.componentDidMount && super.componentDidMount();
this.end=();
(+'组件渲染时间'+(this.end-this.begin)+'ms')
}
render(){
let ele = super.render();//调用父类的render方法
return ele;//可以在这之前完成渲染劫持
}
}
你可以看到,返回的 HOC
类(Enhancer
)继承了 Target。之所以被称为 Inheritance Inversion
是因为 Target
被Enhancer
继承了,而不是Target
继承了Enhancer
。在这种方式中,它们的关系看上去被反转(inverse)了。
Inheritance Inversion
允许 HOC
通过this
访问到 Target
,意味着它可以访问到 state
、props
、组件生命周期方法
和 render
方法。
遗留问题
如何去手动connect,对项目中定义的所有react组件实现自动逻辑注入(talentLibs库的实现方式是什么样的?)
Other Questions
这里主要记录在写demo的过程中,遇到的几个问题:
关于箭头函数
在给类定义方法时,如果使用了箭头函数,发现在prototype*问不到该方法,主要原因如下:
what happens when using arrow function to define method on class
//deom1
class Super{
sayName(){
//do some thing here
}
}
//通过可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super()
var b = new Super()
=== //true
//所有实例化之后的对象共享prototypy上的sayName方法
//demo2
class Super{
sayName =()=>{
//do some thing here
}
}
//通过访问不到sayName方法,该方法没有定义在prototype上
var a = new Super()
var b = new Super()
=== //false
//实例化之后的对象各自拥有自己的sayName方法,比demo1需要更多的内存空间
通过demo1这种方式,我们可以在*问到
sayName
方法
但是通过使用demo2的定义方式,我们在*问不到
sayName
方法
Conclusion
if you use the reserved key word class
in ES6 to declare a class like demo1,the method will be defined on the ,when using arrow function,the method will be bound with this.
在定义一个类时,如果使用demo1中的方式给类添加方法,那么这个方法将被定义在类的prototype
上。然而如果使用箭头函数
的话,这个方法将会被定义在this
上,即Super
的实例化之后的对象。而且在这个方法内,this
始终会指向这个对象,而不管方法的调用方式(箭头函数的特性)
Reference
深入了解HOC