React 中最大的一亮點,就是組件化開發模式,而編寫 React 組件,對於無狀態的組件,我們可以用函數式組件編寫,而複雜的組件(聰明組件/外層組件)可以用類class編寫組件
在 React 中提到了高階函數與高階組件,一直以來,對它們都是持以仰望的,逼格高的詞調,常常把自己給整暈的,做個記錄總結一下的
如下:經常用到的setTimeout,setInterval
setTimeout(function() {
console.log('itclanCoder');
}, 2000);setTimeout 是一個定時器函數,接收兩個參數,第一個參數是一個匿名函數,第二個參數是時間過了 2 秒後,執行匿名函數中的代碼
setInterval高階函數
setInterval(() => {
console.log('http://coder.itclan.cn');
}, 2000);setTinterval 是一個定時器函數,同樣接收兩個參數,第一個參數是箭頭函數,每個參數時間參數每隔 2 秒執行一次箭頭函數
說明
類似這樣的高階函數:clearInterval 清除定時器等,將函數作為形參數放到一個函數中執行的,這個函數可以視為高階函數數組中的一些迭代器函數都可以視為高階函數:map,filter,forEach,reduce,find等
函數作為返回值輸出一個函數可以有返回值,也可以無返回值,若無指定返回值,它會默認返回undefined
函數是對象,這意味著函數可以存儲在一個變量,數組,或對象中,同時函數可以傳遞給函數,並由函數返回,它可以擁有屬性,也可以是一個值,可以像JavaScript中的其他表達式那樣被當做參數一個傳遞
function foo(x) {
return function() {
return x;
};
}上面的 foo 函數接收的形參是x,函數 foo 的返回值是一個匿名函數,匿名函數返回值返回形參x
那麼此時foo函數就是以函數作為返回值作為輸出的高階函數
定時器 setTimeout 應用
setTimeout(function() {
console.log('itclanCoder');
}, 2000);隔 2 秒後列印 itclancoder
定時器 setInterval 應用
setInterval(function() {
console.log('http://coder.itclan.cn/');
}, 2000);每隔 2 秒列印一次http://coder.itclan.cn/
Ajax應用
$.get('url?params',function() {
console.log("獲取數據");
})
axios.get('url', function() {
console.log("獲取數據");
})上面的get函數都可以視為高階函數
Es5,Es5新增的一個迭代器方法
some(),every(),map(),forEach(),reduce(),find(),filter()等
函數節流應用
function throttle2(method, duration) {
// 當前時間間隔內是否有方法執行,設置一個開關標識
var runFlag = false;
// 返回一個事件處理函數
return function(e) {
// 判斷當前是否有方法執行,有則什麼都不做,若為true,則跳出
if (runFlag) {
return false;
}
// 開始執行
runFlag = true;
// 添加定時器,在到達時間間隔時重置鎖的狀態
setTimeout(function() {
method(e);
// 執行完畢後,聲明當前沒有正在執行的方法,方便下一個時間調用
runFlag = false;
}, duration);
};
}函數節流保證一段時間內只執行一次核心代碼,連續每隔一定的時間觸發執行的函數上面使用的是重置一個開關變量+定時器的方式實現函數節流
函數防抖應用
function debounce(method, duration) {
var timer = null;
return function() {
var that = this,
args = arguments;
// 在本次調用之間的一個間隔時間內若有方法在執行,則終止該方法的執行
if (timer) {
clearTimeout(timer);
}
// 開始執行本次調用
timer = setTimeout(function() {
method.apply(that, args);
}, duration);
};
}函數的防抖:延遲事件處理函數的執行,一定時間間隔內只執行最後一次操作
函數柯裡化
// 普通的add函數
function add(x, y) {
return x + y;
}
// 柯裡化後
function curryingAdd(x) {
return function(y) {
return x + y;
};
}
add(1, 2); // 3
curryingAdd(1)(2); // 3以上是把add函數的x,y兩個參數變成了先用一個函數接收x然後返回一個函數去處理y參數
只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。
概念: 高階組件就是接收一個組件作為參數並返回一個新組件的函數
說明: 高階組件是一個函數,並不是組件
例如:如下面的彈出框
<template>
<div class="highfun-component-pop">
<div class="highfun-component-pop-box">
<div class="header">
<div>提示</div>
<div><i class="el-icon-circle-close icon-close-btn"></i></div>
</div>
<div class="content">
<div class="list">A</div>
<div class="list">B</div>
<div class="list">C</div>
<div class="list">D</div>
<div class="list">E</div>
</div>
</div>
<div class="highfun-component-pop-box">
<div class="header">
<div>提示</div>
<div><i class="el-icon-circle-close icon-close-btn"></i></div>
</div>
<div class="content2">
<div class="list2">A</div>
<div class="list2">B</div>
<div class="list2">C</div>
<div class="list2">D</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>
<style>
.highfun-component-pop {
display: flex;
justify-content: center;
}
.highfun-component-pop .highfun-component-pop-box {
width: 300px;
height: 400px;
border:1px solid #de3636;
border-top: none;
}
.highfun-component-pop .highfun-component-pop-box:nth-child(1) {
margin-right: 30px;
}
.highfun-component-pop .highfun-component-pop-box .header {
display: flex;
height: 50px;
padding: 0 10px;
justify-content: space-between;
align-items: center;
background:#de3636;
color:#fff;
}
.highfun-component-pop .highfun-component-pop-box .header .icon-close-btn {
font-size: 25px;
color:#fff;
}
.highfun-component-pop .highfun-component-pop-box .content {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
align-content: center;
}
.highfun-component-pop .highfun-component-pop-box .content2 {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.highfun-component-pop .highfun-component-pop-box .list2{
width: 100px;
height: 100px;
border:1px solid #de3636;
margin: 10px;
text-align: center;
line-height: 100px;
}
.highfun-component-pop .highfun-component-pop-box .list {
width: 80%;
height: 40px;
border: 1px solid #de3636;
margin: 10px 0;
text-align:center;
line-height: 40px;
}
</style>經過 UI,可以將上面的公共的部分以及不同的部分給提取出來,封裝成三個組件,分別為組件 A(公共部分),組件 B,組件 C
<template>
<div class="highcompnent-demo-wrap">
<div class="component-a">
<div class="component-header">
<div>提示</div>
<div><i class="el-icon-circle-close icon-close-btn"></i></div>
</div>
</div>
<div class="component-b">
<div class="list">A</div>
<div class="list">B</div>
<div class="list">C</div>
<div class="list">D</div>
<div class="list">E</div>
</div>
<div class="component-c">
<div class="component-list">A</div>
<div class="component-list">B</div>
<div class="component-list">C</div>
<div class="component-list">D</div>
</div>
</div>
</template>
<style>
.highcompnent-demo-wrap {
display: flex;
justify-content: center;
}
.highcompnent-demo-wrap .component-a {
width: 300px;
height: 400px;
border:1px solid #de3636;
border-top: none;
}
.highcompnent-demo-wrap .component-b {
width: 300px;
height: 400px;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.highcompnent-demo-wrap .component-b .list {
width: 80%;
height: 40px;
border: 1px solid #de3636;
margin: 10px 0;
text-align:center;
line-height: 40px;
}
.highcompnent-demo-wrap .component-c {
display: flex;
justify-content: center;
flex-wrap: wrap;
align-items: center;
align-content: center;
width: 226px;
height: 400px;
}
.highcompnent-demo-wrap .component-c .component-list {
width: 100px;
height: 100px;
border:1px solid #de3636;
text-align: center;
line-height: 100px;
margin-right: 10px;
margin-bottom:10px;
}
.highcompnent-demo-wrap .component-header {
display: flex;
height: 50px;
padding: 0 10px;
justify-content: space-between;
align-items: center;
background:#de3636;
color:#fff;
}
</style>
<template>
<div class="footer-component">
<div>組件A</div>
<div>組件B</div>
<div>組件C</div>
</div>
</template>
<style>
.footer-component {
padding: 15px 0 0 0;
width: 100%;
display: flex;
justify-content: space-around;
}
</style>可以用create-react-app腳手架工具創建一個項目,在src目錄下創建一個components文件夾,這個文件主要用於存放我們的自定義組件
在components中創建一個highcomponent,同時在該文件夾內創建ComponentA.js,ComponentB.js,ComponentC.js
組件 A 公共組件
import React, { Component } from 'react';
import './componentA.css';
// 聲明一個函數A組件,返回結果是一個類組件,並接收一個參數WrappendComponent
function A(WrappendComponent) {
return class ComponentA extends Component {
render() {
return (
<div>
<div className="highfun-component-pop-box">
<div className="header">
<div>提示</div>
<div>X</div> // 這裡為了簡便,我直接用一個x替代了的
</div>
<div className="content">
<WrappendComponent /> // 這裡是要渲染不同的內容
</div>
</div>
</div>
);
}
};
}
export default A; // 導出A函數組件componentA.css
.pop-box {
width: 300px;
height: 400px;
border: 1px solid #de3636;
border-top: none;
}
.header {
display: flex;
height: 50px;
padding: 0 10px;
justify-content: space-between;
align-items: center;
background: #de3636;
color: #fff;
}
.content {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
align-content: center;
}組件 B
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA'; // 引入A函數
// 類聲明B組件
class ComponentB extends Component {
render() {
return (
<div>
<div className="component-b">
<div className="list">A</div>
<div className="list">B</div>
<div className="list">C</div>
<div className="list">D</div>
</div>
</div>
);
}
}
export default A(ComponentB); // 導出調用A函數,同時將B組件讓A組件作為參數調用componentB.css
.component-b {
width: 300px;
height: 400px;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.component-b .list {
width: 80%;
height: 40px;
border: 1px solid #de3636;
margin: 10px 0;
text-align: center;
line-height: 40px;
}組件 C
import React, { Component } from 'react';
import './componentC.css';
import A from './componentA';
class ComponentC extends Component {
render() {
return (
<div>
<div className="component-c">
<div className="component-list">A</div>
<div className="component-list">B</div>
<div className="component-list">C</div>
<div className="component-list">D</div>
</div>
</div>
);
}
}
export default A(ComponentC);componentC.css
.component-c {
display: flex;
justify-content: center;
flex-wrap: wrap;
align-items: center;
align-content: center;
width: 226px;
height: 400px;
}
.component-c .component-list {
width: 100px;
height: 100px;
border: 1px solid #de3636;
text-align: center;
line-height: 100px;
margin-right: 10px;
margin-bottom: 10px;
}App.js 中
import React, { Component } from 'react';
import ComponentB from './components/highfun-component-popcomponent/componentB';
import ComponentC from './components/highfun-component-popcomponent/componentC';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<ComponentB />
<ComponentC />
</div>
);
}
}
export default App;App.css
.App {
text-align: center;
display: flex;
justify-content: space-around;
}說明
從上面的示例代碼中就可以看出 A 就是一個高階組件
⒈ 首先高階組件它是一個函數,並且函數返回一個類組件
⒉ 高階組件它需要接受一個形參數,作為在想要渲染地方以組件的形式插入
經過上面的代碼編寫:達到了組件復用的目的
多個組件都需要某個相同的功能,使用高階組件減少重複實現
react-redux 中的connect連接器就是一個高階組件
export default connect(mapStateToProps, mapDispatchToProps)(Header);⒈ 如何編寫高階組件
⒉ 如何使用高階組件
⒊ 如在高階組件中實現傳遞參數
如何編寫高階組件⒈ 實現一個普通組件
⒉ 將一個普通組件使用函數包裹
第 1 步實現一個普通組件
import React, { Component } from 'react';
// 用class類聲明一個componentD組件繼承自Component
export default class componentD extends Component {
render() {
return <div></div>;
}
}第 2 步-將普通組件使用函數包裹
import React, { Component } from 'react';
// 聲明一個函數組件ComponentD,同時接收一個形參WrappedComponent
function ComponentD(WrappedComponent) {
return class componentD extends Component {
render() {
return (
<div>
<WrappedComponent />
</div>
);
}
};
}
export default ComponentD;說明
編寫一個高階組件,首先要明確高階組件是一個函數
⒈ 先編寫一個普通的類組件
⒉ 聲明一個函數,接收一個形參,該函數返回一個類組件
⒊ 最後將該函數給導出
如何使用高階組件⒈ higherOrderComponent(WrappedComponent)
⒉ @highOrderComponent
假設現在我編寫了一個組件compnentF.js組件,將該組件當做參數傳遞給組件 componentD,然後渲染到頁面上
以下為第一種使用方式,函數調用方式
組件 F-componentF
import React, { Component } from 'react';
import componentD from './componentD'; // 引入函數componentD高階組件
class componentF extends Component {
render() {
return <div>我是組件F</div>;
}
}
export default componentD(componentF); // 將類組件componentF作為參數被組件componentD調用並導出使用高階組件,還有另外一種方式,就是使用裝飾器方式,即@+函數名,它是一個語法糖,簡化了我們的寫法
方式 1-安裝 babel 插件在 babelrc 中配置在使用這種裝飾器方式時,需要對create-react-app做一些配置,它默認是不支持裝飾器模式的,你需要對項目做一些配置
在create-react-app根目錄中終端下使用npm run eject,這條命令主要是將我們的配置項做一個反向輸出,暴露出隱藏的 webpack 配置項,這樣可以項目進行修改了的,注意它是不可逆的
使用裝飾器模式時:需要安裝兩個依賴:
cnpm install -D babel-preset-stage-2
cnpm install -D babel-preset-react-native-stage-0然後你需要在根目錄下創建一個.babelrc文件,對.babelrc文件做一些配置
{
"presets": ["react-native-stage-0/decorator-support"]
}經過這麼配置後,就可以使用裝飾器了的
import React, { Component } from 'react';
import componentD from './componentD'; // 引入函數componentD高階組件
@componentD // 直接用@符號+高階組件componentD就可以了的
class componentF extends Component {
render() {
return <div>我是組件F</div>;
}
}
export default componentF;
方式 2-經過 eject 後在 package.json 中的 plugins 中配置當用eject將webpack一些配置彈射出來以後,會看到根目錄下的package.json文件下新增了很多文件
在babel對象處進行插件的配置,將@babel/plugin-proposal-decorators添加到plugins後
{
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{ "legacy": true }
]
]
}
}出現警告提示
當使用裝飾器模式時,在 vscode 中會有一警告的提示
解決 vscode 警告問題
在vscode中的設置中tsconfig啟動Experimental Decorators就可以解決此警告
經過這麼配置後就可以使用裝飾模式模式了的-即@+函數名簡寫的方式
非裝飾器普通模式寫法
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA';
class ComponentB extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div className="component-b">
<div className="list">A</div>
<div className="list">B</div>
<div className="list">C</div>
<div className="list">D</div>
</div>
</div>
);
}
}
export default A(ComponentB); // 需要調用高階組件裝飾器寫法
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA';
@A
class ComponentB extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div className="component-b">
<div className="list">A</div>
<div className="list">B</div>
<div className="list">C</div>
<div className="list">D</div>
</div>
</div>
);
}
}
export default ComponentB; // 直接導出組件即可說明
在同一個組件中可以組合使用高階函數,能夠無限的嵌套下去,如果不用裝飾器函數,你會發現代碼將變得非常難以理解,也不好維護
import React, { Component } from 'react';
import A from './componentA';
import componentD from './componentD';
@A
@comonentD
class componentF extends Component {
render() {
return <div>我是組件F</div>;
}
}
export default componentF;為什麼需要 eject 暴露呢
因為默認create-react-app雖然已經內已經安裝了 @babel/plugin-proposal-decorators插件,但是需要自己進行配置
若不進行配置,它是不支持裝飾器模式的
結語本小節主要介紹了React中的高階函數以及高階組件,高階函數具體有哪些應用以及什麼是高階組件,如何編寫高階組件
如果您有問題,歡迎評論下方留言,一起學習探討
記帳就用輕記帳
公眾號(ID:itclanCoder)
碼能讓您早脫菜籍,文能讓您洗淨鉛華
可能您還想看更多:
文章都看完了不點個在看嗎