Home > Article > Web Front-end > Detailed explanation of decorator in es7 (with examples)
This article brings you a detailed explanation of the Decorator in es7 (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
1. Basic knowledge of Decorator
It can be seen in many frameworks and libraries, especially React, Redux, and mobx. So what is a decorator?
Decorator is a function used to modify the behavior of a class. I don’t quite understand this abstract concept, so it’s better to look at the code to explain it more practically.
//定义一个函数,也就是定义一个Decorator,target参数就是传进来的Class。 //这里是为类添加了一个静态属性 function addAge(target) { target.age = 2; } //在Decorator后面跟着Class,Decorator是函数的话,怎么不是addAge(MyGeekjcProject)这样写呢? //我只能这样理解:因为语法就这样,只要Decorator后面是Class,默认就已经把Class当成参数隐形传进Decorator了(就是所谓的语法糖)。 @addAge class MyGeekjcProject {} console.log(MyGeekjcProject.age) // 2
But what should I do if the above method only passes one Class as a parameter and it is not flexible enough?
We can set a function in the outer layer, as long as the final result is a Decorator, it doesn't matter how many functions you set and how many parameters you pass.
function addAge(age) { return function(target) { target.age = age; } } //注意这里,隐形传入了Class,语法类似于addAge(2)(MyGeekjcProject) @testable(2) class MyGeekjcProject {} MyGeekjcProject.age // 2 @addAge(3) class MyGeekjcProject {} MyGeekjcProject.age // 3
The following is the prototype object of the modified class
function description(target) { target.prototype.url = 'https://www.geekjc.com'; } @description class MyGeekjcProject {} let geekjc = new MyGeekjcProject(); geekjc.url // https://www.geekjc.com
The concept is roughly understood. Modifiers can not only modify the class, but also modify the attributes of the class.
//假如修饰类的属性则传入三个参数,对应Object.defineProperty()里三个参数,具体不细说 //target为目标对象,对应为Class的实例 //name为所要修饰的属性名,这里就是修饰器紧跟其后的name属性 //descriptor为该属性的描述对象 //这里的Decorator作用是使name属性不可写,并返回修改后的descriptor function readonly(target, name, descriptor){ descriptor.writable = false; return descriptor; } class Person { @readonly name() { return `${this.first} ${this.last}` } }
Regarding Object.defineProperty(), you can read the article to learn Object.defineProperty
Let’s look at a complex example again
//定义一个Class并在其add上使用了修饰器 class Math { @log add(a, b) { return a + b; } } //定义一个修饰器 function log(target, name, descriptor) { //这里是缓存旧的方法,也就是上面那个add()原始方法 var oldValue = descriptor.value; //这里修改了方法,使其作用变成一个打印函数 //最后依旧返回旧的方法,真是巧妙 descriptor.value = function() { console.log(`Calling "${name}" with`, arguments); return oldValue.apply(null, arguments); }; return descriptor; } const math = new Math(); math.add(2, 4);
After reading the above code, you will know the use of decorator Yes, modifiers can not only modify the behavior of a class, but also modify the attributes of the class.
2. React high-order components
More commonly described as, high-order components are passed in by wrapping (wrapped) React components, and after a series of processes, Finally, a relatively enhanced React component is returned for other components to call.
3. Implement a higher-order component
Let’s implement the simplest high-order component (function), which accepts a React component, wraps it and returns .
export default function withDescription(WrappedComponent) { return class HOC extends Component { render() { return <div> <div> 极客教程(https://www.geekjc.com)致力于推广各种编程语言技术,也为了未来数字化世界,让人更容易找到操作数字化的方式,为了未来而生的编程学习平台, 大部分资源是完全免费的,并且会根据当前互联网的变化实时更新本站内容。 </div> <wrappedcomponent></wrappedcomponent> </div> } } }
In other components, we reference this higher-order component to enhance it.
@withDescription export default class Geekjc extends Component { render() { return ( <div> 我是一个普通组件 </div> ); } }
The decorator in ES7 is used here (see Decorator in the first section) to improve the elegance of writing, but in fact it is just syntactic sugar. The following writing method is also possible.
const EnhanceDemo = withDescription(Geekjc);
Subsequently, observe what changes have taken place in the React component tree. As shown in the figure, you can find that the Geekjc component is wrapped by the HOC component, which is in line with the expectations of high-order components, that is, the components are wrapped layer by layer. , like an onion.
But the problem that comes with it is that if this high-order component is used multiple times, then when debugging, you will see There are a lot of HOC
, so a little optimization needs to be done at this time, that is, after the high-order components are wrapped, their original names should be retained.
We rewrite the above high-order component code and add the getDisplayName
function and static attribute displayName
, and then observe DOM Tree
.
function getDisplayName(component) { return component.displayName || component.name || 'Component'; } export default function (WrappedComponent) { return class HOC extends Component { static displayName = `HOC(${getDisplayName(WrappedComponent)})` render() { return <div> <div> 极客教程(https://www.geekjc.com)致力于推广各种编程语言技术,也为了未来数字化世界,让人更容易找到操作数字化的方式,为了未来而生的编程学习平台, 大部分资源是完全免费的,并且会根据当前互联网的变化实时更新本站内容。 </div> <wrappedcomponent></wrappedcomponent> </div> } } }
At this time, the name of the original component is correctly displayed on the DOM Tree.
In this simple example, the higher-order component only does one thing, which is adding a title style to the wrapped component. This higher-order component can be used on any component that needs to add this logic, and it only needs to be modified by this higher-order component.
It can be seen that the main function of high-order components is to encapsulate and abstract the common logic of the components, so that this part of the logic can be better reused between components.
4. Advanced usage of high-order components
4.1 Component parameters
Still taking the above example as an example, this high-order component only shows information about the website Description, but for better abstraction, this description should be parameterizable and called as follows.@withDescription('欢迎大家访问收藏(https://www.geekjc.com)') export default class Geekjc extends Component { render() { return ( //... ); } }
withDescription needs to be rewritten into the following form, which accepts a parameter and then returns a higher-order component (function).
export default function (description) { return function (WrappedComponent) { return class HOC extends Component { render() { return <div> <div> {description ?description : '我是描述'} </div> <wrappedcomponent></wrappedcomponent> </div> } } } }
Using ES6 writing can be more concise.
export default(description) => (WrappedComponent) => class HOC extends Component { render() { return <div> <div> {description ? description : '我是描述'} </div> <wrappedcomponent></wrappedcomponent> </div> } }
As you can see in the picture, the parameters passed in have been reflected in the DOM Tree.
Curriization Curry
Concept: Pass only part of the parameters of a function to call it, and let it return a function to handle the remaining parameters.
Function signature: fun(params)(otherParams)
Application: In React, through currying, we can get different higher-order components by passing in different parameters.
Attribute proxy is the most common way to use high-order components. The high-order components described above are this way. It does some operations to pass the props of the wrapped component and the newly generated props to this component, which is called a property proxy.
export default function withHeader(WrappedComponent) { return class HOC extends Component { render() { const newProps = { test:'hoc' } // 透传props,并且传递新的newProps return <p> <wrappedcomponent></wrappedcomponent> </p> } } }
The React component returned in this way inherits the passed component, so it can access more areas and permissions than attributes. The agency approach is more like breaking into an organization and modifying it. For details, you can refer to the links provided in the appendix for in-depth study.
export default function (WrappedComponent) { return class Inheritance extends WrappedComponent { componentDidMount() { // 可以方便地得到state,做一些更深入的修改。 console.log(this.state); } render() { return super.render(); } } }
上述高阶组件为React组件增强了一个功能,如果需要同时增加多个功能需要怎么做?这种场景非常常见,例如我既需要增加一个组件标题,又需要在此组件未加载完成时显示Loading。
@withDescription @withLoading class Demo extends Component{ }
使用compose可以简化上述过程,也能体现函数式编程的思想。
const enhance = compose(withHeader,withLoading); @enhance class Demo extends Component{ }组合 Compose
compose函数实现方式有很多种,这里推荐其中一个recompact.compose,详情见下方参考类库,也可以看我之前写的一篇文章reduce与redux中compose函数
5. 与父组件区别
高阶组件作为一个函数,它可以更加纯粹地关注业务逻辑层面的代码,比如数据处理,数据校验,发送请求等,可以改善目前代码里业务逻辑和UI逻辑混杂在一起的现状。父组件则是UI层的东西,我们先前经常把一些业务逻辑处理放在父组件里,这样会造成父组件混乱的情况。为了代码进一步解耦,可以考虑使用高阶组件这种模式。
6. 开源的高阶组件使用
6.1 recompact
recompact提供了一系列使用的高阶组件,可以增强组件的行为,可以利用此库学习高阶组件的写法。
import recompact from 'recompact' import { pure, withProps } from 'recompact' const enhance = recompact.compose( withProps({ className: 'beautiful' }), pure, ) @enhance class Demo extends Component{ }
通过使用此库提供的高阶组件,可以方便地让列表元素可拖动。
The above is the detailed content of Detailed explanation of decorator in es7 (with examples). For more information, please follow other related articles on the PHP Chinese website!