总有一些东西,是你再努力都触碰不到的。
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); |