总有一些东西,是你再努力都触碰不到的。
Decorator
es7语法糖,就是一个修饰类以及类的属性或者行为的函数。
配置
1  | npm install customize-cra react-app-rewired @babel/plugin-proposal-decorators --save  | 
项目根目录新建config-overrides.js文件加入以下代码:
1  | const { override, addDecoratorsLegacy } = require('customize-cra');  | 
修改package.json文件如下:
1  | "scripts": {  | 
使用
1  | function testAble(target){  | 
在一个修饰器中,参数target就是你要修饰的类,这个类也叫做Decorator的修饰目标对象。
多参数的修饰器的实现
1  | function testAble(value) {  | 
修饰对象的属性
除了修饰class,还用来修饰对象属性。
修饰器修饰一个属性的时候,有3个参数
第一个 target 修饰器对应的class
第二个 name 属性名
第三个 descriptor 描述符
descriptor的说明
1  | let obj = {};  | 
就是其中的描述符对象。
@log的实现
1  | function log(target, name, descriptor){  | 
可见本质是对descriptor的实现。另外要注意修饰器不能用于函数。(原因:函数存在函数提升)
core-decorator第三方模块,提供常用的几个修饰器
(1) @override
检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。
(2)@autobind
使得方法中的this对象,绑定原始对象。
(3) @readonly
使得class中的属性或者方法不可写。
高阶组件
目的是解决一些交叉问题(Cross-Cutting Concerns)。而最早时候 React 官方给出的解决方案是使用 mixin 。
高阶组件通过包裹(wrapped)被传入的React组件,经过一系列处理,最终返回一个相对增强(enhanced)的 React 组件,供其他组件调用。高阶组件是接受一个组件作为参数并返回一个新组件的函数。
先复习一下class表达式语法的规则
1  | const MyClass = class Me {  | 
Me只能在class里面用,并且MyClass的引用是class Me {},所以在外面MyClass.name是Me。
如果类的内部没用到的话,可以省略Me
1  | const MyClass = class { /* ... */ };  | 
一个简单的高阶组件
1  | export default function withHeader(WrappedComponent) {  | 
@withHeader是decorator,也可以写成
1  | export default withHeader(Demo);  | 
这样写的问题
高阶组件调用多次,会造成组件数出现多个HOC,调试困难。
优化
1  | function getDisplayName(component) {  | 
由此可以看出,高阶组件的主要功能是封装并抽离组件的通用逻辑,让此部分逻辑在组件间更好地被复用。
displayName 定义调试时的组件name
例如
1  | function withHOC(WrapComponent) {  | 
如果未定义displayName,那么进行调试的时候,就会显示如下:
1  | // react自动定义名称  | 
定义displayName后,显示如下:
1  | |---withHOC(App)  | 
组件参数
1  | @withHeader('Demo')  | 
改写一下HOC
1  | export default (title) => (WrappedComponent) => class HOC extends Component {  | 
实现方式
(1)属性代理
通过做一些操作,将被包裹组件的
props和新生成的props一起传递给此组件,这称之为属性代理
1  | export default function withHeader(WrappedComponent) {  | 
(2)基于反向继承
1  | export default function (WrappedComponent) {  | 
组合多个高阶组件
1  | @withHeader  | 
简化语法
1  | const enhance = compose(withHeader,withLoading);  |