不少人写文章喜欢把问题复杂化,所以当我学习高阶组件的时候,查阅到的不少文章都给人一种高阶组件高深莫测的感受。可是事实上却未必。html
有一个词叫作“封装”。相信写代码这么久了,你们对这个词所表达的含义都不会陌生。咱们一般会将功能相同或者类似的代码提取出来封装成为一个可共用的函数或者对象,这也是咱们从初学者慢慢进阶的必经之路。而高阶组件就是一个封装行为。react
可是高阶组件的封装与咱们一般所使用的不太同样,若是彻底同样也就不是那么难理解了。好在咱们有一个经常使用的口头语“包一层
“恰好能够用来简单解释高阶组件的不一样。在普通组件外面包一层逻辑,就是高阶组件。app
关于”包一层“,能够经过一个很是简单的例子来理解。函数
import React, { Component } from 'react'; class Div extends Component { componentDidMount() { console.log('这是新增的能力'); } render () { return ( <div>{ this.props.children }</div> ) } } export default Div;
在上面的例子中,咱们把html的DIV标签做为基础元件。对他新增了一个输出一条提示信息的能力。而新的Div组件,就能够理解为div标签的高阶组件。到这里但愿你们已经理解了包一层的具体含义。学习
为了更加透彻的理解“包一层”的概念,咱们须要来回顾一下new与构造函数之间的关系。在前面我有文章提到过为何构造函数中this在运行时会指向new出来的实例,不知道还有没有人记得。我将那段代码复制过来。this
// 先一本正经的建立一个构造函数,其实该函数与普通函数并没有区别 var Person = function(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } // 将构造函数以参数形式传入 function New(func) { // 声明一个中间对象,该对象为最终返回的实例 var res = {}; if (func.prototype !== null) { // 将实例的原型指向构造函数的原型 res.__proto__ = func.prototype; } // ret为构造函数执行的结果,这里经过apply,将构造函数内部的this指向修改成指向res,即为实例对象 var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); // 当咱们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象 if ((typeof ret === "object" || typeof ret === "function") && ret !== null) { return ret; } // 若是没有明确指定返回对象,则默认返回res,这个res就是实例对象 return res; } // 经过new声明建立实例,这里的p1,实际接收的正是new中返回的res var p1 = New(Person, 'tom', 20); console.log(p1.getName()); // 固然,这里也能够判断出实例的类型了 console.log(p1 instanceof Person); // true
在上面的例子中,首先咱们定义了一个本质上与普通函数没区别的构造函数,而后将该构造函数做为参数传入New函数中。我在New函数中进行了一些的逻辑处理,让New函数的返回值为一个实例,正由于New的内部逻辑,让构造函数中的this可以指向返回的实例。这个例子就是一个“包一层”的案例。若是由于基础不够扎实致使你对上面的例子确实理解不了,咱们还能够简单粗暴的把上面的例子分红三个步骤来记忆。spa
建立一个new方法prototype
而刚好,高阶组件的建立逻辑与使用,与这里的new方法彻底一致。由于new方法其实就是构造函数的”高阶组件“。按照这个步骤,咱们来尝试一步一步建立一个高阶组件。code
第一步,建立一个最简单的基础组件。component
class Basic extends Component { render() { return ( <div>{ this.props.children }</div> ) } }
第二步,根据上栗new方法的步骤,来建立高阶组件。
// src/Addsss.jsx import React from 'react'; // 基础组件做为高阶组件的参数传入 function Addsss(Container) { // 建立一个中间组件,该中间组件会在添加了逻辑以后返回 return class Asss extends React.Component { componentDidMount() {} render() { return ( // 高阶组件往基础组件中传入了一个name属性,这是高阶组件赋予基础组件的新能力,固然,根据实际需求还能够添加更为复杂的新能力 <Container name="asper">{ this.props.children }</Container> ) } } } export default Addsss;
高阶组件在基础组件中调用,并将高阶组件的运行结果返回给模块外部。所以基础组件的代码调整以下:
// src/basic.jsx import React, { Component } from 'react'; import Addsss from './Addsss'; class Basic extends Component { componentDidMount() { // 在基础组件中试图访问高阶组件传入的新参数 console.log(this.props.name); } render() { return ( <div className={this.props.name}>{ this.props.children }</div> ) } } // 这里至关于执行了一次new操做,返回了一个实例,其实运行结果然是高阶组件中的中间组件 export default Addsss(Basic);
咱们看到其实在基础组件中,对外抛出的接口是Addsss(Basic),这是高阶组件里定义的函数运行的结果。也就是说,其实基础组件中返回的是高阶组件中定义的Asss中间组件。这和new的思路几乎彻底一致。
因此咱们能够简单理解为:react组件的高阶组件,就是在基础react组件外面包一层,给该基础组件赋予新的能力。
固然,想要熟练使用高阶组件并非一件容易的事情,咱们还须要更多的思考他。在下面一篇文章中我将会以实际的案例来分析高阶组件的使用场景与他到底给咱们带来了哪些便利。