源碼地址
https://github.com/lanzhsh/react-vue-koa/tree/master/react-pc-skill
歡迎 star
效果圖
1 組件通訊
1.1 props
子組件
<code>import React from "react";
import PropTypes from "prop-types";
import { Button } from "antd";
export default class EightteenChildOne extends React.Component {
static propTypes = { //propTypes校驗傳入類型,詳情在技巧11
name: PropTypes.string
};
click = () => {
// 通過觸發方法子傳父
this.props.eightteenChildOneToFather("這是 props 改變父元素的值");
};
render() {
return (
這是通過 props 傳入的值{this.props.name}
<button>
點擊改變父元素值
/<button>
);
}
}
複製代碼/<code>
父組件
<code><eightteenchildone>this.eightteenChildOneToFather(mode)}>/<eightteenchildone>
// 或者
<eightteenchildone>
複製代碼/<code>
props 傳多個值時: 傳統寫法
<code>const {dataOne,dataTwo,dataThree} = this.state/<code>
複製代碼
升級寫法
<code>/<code>
複製代碼
1.2 props 升級版
原理:子組件裡面利用 props 獲取父組件方法直接調用,從而改變父組件的值 注意: 此方法和 props 大同小異,都是 props 的應用,所以在源碼中沒有舉例
調用父組件方法改變該值
<code>// 父組件
state = {
count: {}
}
changeParentState = obj => {
this.setState(obj);
}
// 子組件
onClick = () => {
this.props.changeParentState({ count: 2 });
}
複製代碼/<code>
1.3 Provider,Consumer和Context
1.Context在 16.x 之前是定義一個全局的對象,類似 vue 的 eventBus,如果組件要使用到該值直接通過this.context獲取
<code>//根組件
class MessageList extends React.Component {
getChildContext() {
return {color: "purple",text: "item text"};
}
render() {
const {messages} = this.props || {}
const children = messages && messages.map((message) =>
<message>
);
return{children};
}
}
MessageList.childContextTypes = {
color: React.PropTypes.string
text: React.PropTypes.string
};
//中間組件
class Message extends React.Component {
render() {
return (
<messageitem>
<button>Delete/<button>
);
}
}
//孫組件(接收組件)
class MessageItem extends React.Component {
render() {
return (
{this.context.text}
);
}
}
MessageItem.contextTypes = {
text: React.PropTypes.string //React.PropTypes在 15.5 版本被廢棄,看項目實際的 React 版本
};
class Button extends React.Component {
render() {
return (
<button>
{this.props.children}
/<button>
);
}
}
Button.contextTypes = {
color: React.PropTypes.string
};
複製代碼/<code>
2.16.x 之後的Context使用了Provider和Customer模式,在頂層的Provider中傳入value,在子孫級的Consumer中獲取該值,並且能夠傳遞函數,用來修改context 聲明一個全局的 context 定義,context.js
<code>import React from 'react'
let { Consumer, Provider } = React.createContext();//創建 context 並暴露Consumer和Provider模式
export {
Consumer,
Provider
}
複製代碼/<code>
父組件導入
<code>// 導入 Provider
import {Provider} from "../../utils/context"
<provider>
父組件定義的值:{name}
<eightteenchildtwo>
/<provider>
複製代碼/<code>
子組件
<code>// 導入Consumer
import { Consumer } from "../../utils/context"
function Son(props) {
return (
//Consumer容器,可以拿到上文傳遞下來的name屬性,並可以展示對應的值
<consumer>
{name => (
style={{
border: "1px solid blue",
width: "60%",
margin: "20px auto",
textAlign: "center"
}}
>
// 在 Consumer 中可以直接通過 name 獲取父組件的值
子組件。獲取父組件的值:{name}
)}
/<consumer>
);
}
export default Son;
複製代碼/<code>
1.4 EventEmitter
EventEmiter 傳送門 使用 events 插件定義一個全局的事件機制
1.5 路由傳參
1.params
<code><route>
<link>xxx
this.props.history.push({pathname:"/path/" + name});
讀取參數用:this.props.match.params.name
複製代碼/<route>/<code>
2.query
<code><route>
<link>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
讀取參數用: this.props.location.query.name
複製代碼/<route>/<code>
3.state
<code><route>
<link>
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
讀取參數用: this.props.location.query.state
複製代碼/<route>/<code>
4.search
<code><route>
<link>xxx
this.props.history.push({pathname:`/web/search?id ${row.id}`});
讀取參數用: this.props.location.search
複製代碼/<route>/<code>
這個在 react-router-dom: v4.2.2有 bug,傳參跳轉頁面會空白,刷新才會加載出來
5.優缺點
<code>1.params在HashRouter和BrowserRouter路由中刷新頁面參數都不會丟失
2.state在BrowserRouter中刷新頁面參數不會丟失,在HashRouter路由中刷新頁面會丟失
3.query:在HashRouter和BrowserRouter路由中刷新頁面參數都會丟失
4.query和 state 可以傳對象
複製代碼/<code>
1.6 onRef
原理:onRef 通訊原理就是通過 props 的事件機制將組件的 this(組件實例)當做參數傳到父組件,父組件就可以操作子組件的 state 和方法
EightteenChildFour.jsx
<code>export default class EightteenChildFour extends React.Component {
state={
name:'這是組件EightteenChildFour的name 值'
}
componentDidMount(){
this.props.onRef(this)
console.log(this) // ->將EightteenChildFour傳遞給父組件this.props.onRef()方法
}
click = () => {
this.setState({name:'這是組件click 方法改變EightteenChildFour改變的name 值'})
};
render() {
return (
{this.state.name}
<button>
點擊改變組件EightteenChildFour的name 值
/<button>
);
}
}
複製代碼/<code>
eighteen.jsx
<code><eightteenchildfour>
eightteenChildFourRef = (ref)=>{
console.log('eightteenChildFour的Ref值為')
// 獲取的 ref 裡面包括整個組件實例
console.log(ref)
// 調用子組件方法
ref.click()
}
複製代碼/<code>
1.7 ref
原理:就是通過 React 的 ref 屬性獲取到整個子組件實例,再進行操作
EightteenChildFive.jsx
<code>// 常用的組件定義方法
export default class EightteenChildFive extends React.Component {
state={
name:'這是組件EightteenChildFive的name 值'
}
click = () => {
this.setState({name:'這是組件click 方法改變EightteenChildFive改變的name 值'})
};
render() {
return (
{this.state.name}
<button>
點擊改變組件EightteenChildFive的name 值
/<button>
);
}
}
複製代碼/<code>
eighteen.jsx
<code>// 鉤子獲取實例
componentDidMount(){
console.log('eightteenChildFive的Ref值為')
// 獲取的 ref 裡面包括整個組件實例,同樣可以拿到子組件的實例
console.log(this.refs["eightteenChildFiveRef"])
}
// 組件定義 ref 屬性
<eightteenchildfive>
複製代碼/<code>
1.8 redux
redux 是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:
1.9 MobX
MobX 也是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:
1.10 flux
flux 也是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:
1.11 hooks
1.hooks 是利用 userReducer 和 context 實現通訊,下面模擬實現一個簡單的 redux 2.核心文件分為 action,reducer,types action.js
<code>import * as Types from './types';
export const onChangeCount = count => ({
type: Types.EXAMPLE_TEST,
count: count + 1
})
複製代碼/<code>
reducer.js
<code>import * as Types from "./types";
export const defaultState = {
count: 0
};
export default (state, action) => {
switch (action.type) {
case Types.EXAMPLE_TEST:
return {
...state,
count: action.count
};
default: {
return state;
}
}
};
複製代碼/<code>
types.js
<code>export const EXAMPLE_TEST = 'EXAMPLE_TEST';
複製代碼/<code>
eightteen.jsx
<code>export const ExampleContext = React.createContext(null);//創建createContext上下文
// 定義組件
function ReducerCom() {
const [exampleState, exampleDispatch] = useReducer(example, defaultState);
return (
<examplecontext.provider> value={{ exampleState, dispatch: exampleDispatch }}
>
<eightteenchildthree>
/<examplecontext.provider>
);
}
複製代碼/<code>
EightteenChildThree.jsx // 組件
<code>import React, { useEffect, useContext } from 'react';
import {Button} from 'antd'
import {onChangeCount} from '../../pages/TwoTen/store/action';
import { ExampleContext } from '../../pages/TwoTen/eighteen';
const Example = () => {
const exampleContext = useContext(ExampleContext);
useEffect(() => { // 監聽變化
console.log('變化執行啦')
}, [exampleContext.exampleState.count]);
return (
值為{exampleContext.exampleState.count}
<button> exampleContext.dispatch(onChangeCount(exampleContext.exampleState.count))}>點擊加 1/<button>
)
}
export default Example;
複製代碼/<code>
3.hooks其實就是對原有React 的 API 進行了封裝,暴露比較方便使用的鉤子;
4.鉤子有:
鉤子名 作用 useState 初始化和設置狀態 useEffect componentDidMount,componentDidUpdate和componentWillUnmount和結合體,所以可以監聽useState定義值的變化 useContext 定義一個全局的對象,類似 context useReducer 可以增強函數提供類似 Redux 的功能 useCallback 記憶作用,共有兩個參數,第一個參數為一個匿名函數,就是我們想要創建的函數體。第二參數為一個數組,裡面的每一項是用來判斷是否需要重新創建函數體的變量,如果傳入的變量值保持不變,返回記憶結果。如果任何一項改變,則返回新的結果 useMemo 作用和傳入參數與 useCallback 一致,useCallback返回函數,useDemo 返回值 useRef 獲取 ref 屬性對應的 dom useImperativeMethods 自定義使用ref時公開給父組件的實例值 useMutationEffect 作用與useEffect相同,但在更新兄弟組件之前,它在React執行其DOM改變的同一階段同步觸發 useLayoutEffect 作用與useEffect相同,但在所有DOM改變後同步觸發
5.useImperativeMethods
<code>function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeMethods(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return ;
}
FancyInput = forwardRef(FancyInput);
複製代碼/<code>
1.12 slot
slot 就是將父組件的標籤傳給子組件,類似vue 的 v-slot 場景:有些組件只是共用部分dom 邏輯,裡面有部分邏輯是獨立的
<code>// 父組件文件
import SlotChild from 'SlotChild'
<slotchild>slot={這是父組件的 slot}>
/<slotchild>
// 子組件
子組件直接獲取 this.props.slot 就可獲取到內容
複製代碼/<code>
1.13 對比
方法 優點 缺點 props 不需要引入外部插件 兄弟組件通訊需要建立共同父級組件,麻煩 props 升級版 不需要引入外部插件,子傳父,不需要在父組件用方法接收 同 props Provider,Consumer和Context 不需要引入外部插件,跨多級組件或者兄弟組件通訊利器 狀態數據狀態追蹤麻煩 EventEmitter 可支持兄弟,父子組件通訊 要引入外部插件 路由傳參 可支持兄弟組件傳值,頁面簡單數據傳遞非常方便 父子組件通訊無能為力 onRef 可以在獲取整個子組件實例,使用簡單 兄弟組件通訊麻煩,官方不建議使用 ref 同 onRef 同 onRef redux 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 mobx 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 flux 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 hooks 16.x 新的屬性,可支持兄弟,父子組件通訊 需要結合 context 一起使用 slot 支持父向子傳標籤
redux , mobx和flux對比
方法 介紹 redux 1.核心模塊:Action,Reducer,Store;2. Store 和更改邏輯是分開的;3. 只有一個 Store;4. 帶有分層 reducer 的單一 Store;5. 沒有調度器的概念;6. 容器組件是有聯繫的;7. 狀態是不可改變的;8.更多的是遵循函數式編程思想 mobx 1.核心模塊:Action,Reducer,Derivation;2.有多個 store;3.設計更多偏向於面向對象編程和響應式編程,通常將狀態包裝成可觀察對象,一旦狀態對象變更,就能自動獲得更新 flux 1.核心模塊:Store,ReduceStore,Container;2.有多個 store;
2.require.context()
這個是 webpack 的 api,這個在 vue 技巧中有介紹,因為 Vue 和 React 工程都是基於 webpack打包,所以在 react 也可以使用
<code>const path = require('path')
const files = require.context('@/components/home', false, /\\.vue$/)
const modules = {}
files.keys().forEach(key => {
const name = path.basename(key, '.vue')
modules[name] = files(key).default || files(key)
})
複製代碼/<code>
3.Decorator
定義:decorator是ES7的一個新特性,可以修改class的屬性
<code>import React from 'react'
import Test from '../../utils/decorators'
@Test
//只要Decorator後面是Class,默認就已經把Class當成參數隱形傳進Decorator了。
class TwentyNine extends React.Component{
componentDidMount(){
console.log(this,'decorator.js') // 這裡的this是類的一個實例
console.log(this.testable)
}
render(){
return (
這是技巧23
)
}
}
export default TwentyNine
複製代碼/<code>
decorators.js
<code>function testable(target) {
console.log(target)
target.isTestable = true;
target.prototype.getDate = ()=>{
console.log( new Date() )
}
}
export default testable
複製代碼/<code>
很多中間件,像 redux 裡面就封裝了Decorator的使用
4.使用 if...else
場景:有些時候需要根據不同狀態值頁面顯示不同內容
<code>import React from "react";
export default class Four extends React.Component {
state = {
count: 1
};
render() {
let info
if(this.state.count===0){
info=(
這是數量為 0 顯示
)
} else if(this.state.count===1){
info=(
這是數量為 1 顯示
)
}
return (
{info}
);
}
}
複製代碼/<code>
5.state 值改變的五種方式
方式 1
<code>let {count} = this.state
this.setState({count:2})
複製代碼/<code>
方式 2:callBack
<code>this.setState(({count})=>({count:count+2}))
複製代碼/<code>
方式 3:接收 state 和 props 參數
<code>this.setState((state, props) => {
return { count: state.count + props.step };
});
複製代碼/<code>
方式 4:hooks
<code>const [count, setCount] = useState(0)
// 設置值
setCount(count+2)
複製代碼/<code>
方式 5:state 值改變後調用
<code>this.setState(
{count:3},()=>{
//得到結果做某種事
}
)
複製代碼/<code>
6.監聽states 變化
1.16.x 之前使用componentWillReceiveProps
<code>componentWillReceiveProps (nextProps){
if(this.props.visible !== nextProps.visible){
//props 值改變做的事
}
}
複製代碼/<code>
注意:有些時候componentWillReceiveProps在 props 值未變化也會觸發,因為在生命週期的第一次render後不會被調用,但是會在之後的每次render中被調用 = 當父組件再次傳送props
2.16.x 之後使用getDerivedStateFromProps,16.x 以後componentWillReveiveProps也未移除
<code>export default class Six extends React.Component {
state = {
countOne:1,
changeFlag:''
};
clickOne(){
let {countOne} = this.state
this.setState({countOne:countOne+1})
};
static getDerivedStateFromProps (nextProps){
console.log('變化執行')
return{
changeFlag:'state 值變化執行'
}
}
render() {
const {countOne,changeFlag} = this.state
return (
<button>點擊加 1/<button>countOne 值為{countOne}
{changeFlag}
);
}
}
複製代碼/<code>
7.組件定義方法
方式 1:ES5 的Function 定義
<code>function FunCom(props){
return這是Function 定義的組件
}
ReactDOM.render(<funcom>, mountNode)
// 在 hooks 未出來之前,這個是定義無狀態組件的方法,現在有了 hooks 也可以處理狀態
複製代碼/<code>
方式 2: ES5的 createClass 定義
<code>const CreateClassCom = React.createClass({
render: function() {
return這是React.createClass定義的組件
}
});
複製代碼/<code>
方式 3:ES6 的 extends
<code>class Com extends React.Component {
render(){
return(這是React.Component定義的組件)
}
}
複製代碼/<code>
調用
<code>export default class Seven extends React.Component {
render() {
return (
<funcom>
);
}
}
複製代碼/<code>
區別: ES5的 createClass是利用function模擬class的寫法做出來的es6; 通過es6新增class的屬性創建的組件此組件創建簡單.
8.通過 ref 屬性獲取 component
方式 1:也是最早的用法,通過 this.refs[屬性名獲取] 也可以作用到組件上,從而拿到組件實例
<code>class RefOne extends React.Component{
componentDidMount() {
this.refs['box'].innerHTML='這是 div 盒子,通過 ref 獲取'
}
render(){
return(
)
}
}
複製代碼/<code>
方式 2:回調函數,在dom節點或組件上掛載函數,函數的入參是dom節點或組件實例,達到的效果與字符串形式是一樣的,都是獲取其引用
<code>class RefTwo extends React.Component{
componentDidMount() {
this.input.value='這是輸入框默認值';
this.input.focus();
}
render(){
return(
{ this.input = comp; }}/>
)
}
}
複製代碼/<code>
方式 3:React.createRef() React 16.3版本後,使用此方法來創建ref。將其賦值給一個變量,通過ref掛載在dom節點或組件上,該ref的current屬性,將能拿到dom節點或組件的實例
<code>class RefThree extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return
}
}
複製代碼/<code>
方式 4:React.forwardRef React 16.3版本後提供的,可以用來創建子組件,以傳遞ref
<code>class RefFour extends React.Component{
constructor(props){
super(props);
this.myFourRef=React.forwardRef();
}
componentDidMount(){
console.log(this.myFourRef.current);
}
render(){
return <child>
}
}
複製代碼/<child>/<code>
子組件通過React.forwardRef來創建,可以將ref傳遞到內部的節點或組件,進而實現跨層級的引用。forwardRef在高階組件中可以獲取到原始組件的實例.這個功能在技巧 18 會著重講
9.static 使用
場景:聲明靜態方法的關鍵字,靜態方法是指即使沒有組件實例也可以直接調用
<code>export default class Nine extends React.Component {
static update(data) {
console.log('靜態方法調用執行啦')
}
render() {
return (
這是 static 關鍵字技能
);
}
}
Nine.update('2')
複製代碼/<code>
注意: 1.ES6的class,我們定義一個組件的時候通常是定義了一個類,而static則是創建了一個屬於這個類的屬性或者方法 2.組件則是這個類的一個實例,component的props和state是屬於這個實例的,所以實例還未創建 3.所以static並不是react定義的,而加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,所以也是無法訪問到 this 4.getDerivedStateFromProps也是通過靜態方法監聽值,詳情請見技巧 6
10.constructor和super
回顧: 1.談這兩個屬性之前,先回顧一下ES6 函數定義方法 2.每一個使用class方式定義的類默認都有一個constructor函數, 這個函數是構造函數的主函數, 該函數體內部的this指向生成的實例 3.super關鍵字用於訪問和調用一個對象的父對象上的函數
<code>export default class Ten extends React.Component {
constructor() { // class 的主函數
super() // React.Component.prototype.constructor.call(this),其實就是拿到父類的屬性和方法
this.state = {
arr:[]
}
}
render() {
return (
這是技巧 10
);
}
}
複製代碼/<code>
11.PropTypes
場景:檢測傳入子組件的數據類型 類型檢查PropTypes自React v15.5起已棄用,請使用prop-types 方式 1:舊的寫法
<code>class PropTypeOne extends React.Component {
render() {
return (
{this.props.email}
{this.props.name}
);
}
}
PropTypeOne.propTypes = {
name: PropTypes.string, //值可為array,bool,func,number,object,symbol
email: function(props, propName, componentName) { //自定義校驗
if (
!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(
props[propName]
)
) {
return new Error(
"組件" + componentName + "裡的屬性" + propName + "不符合郵箱的格式"
);
}
},
};
複製代碼/<code>
方法 2:利用 ES7 的靜態屬性關鍵字 static
<code>class PropTypeTwo extends React.Component {
static propTypes = {
name:PropTypes.string
};
render() {
return (
{this.props.name}
);
}
}
複製代碼/<code>
場景:可以在不使用構造函數的情況下初始化本地狀態,並通過使用箭頭函數聲明類方法,而無需額外對它們進行綁定
<code>class Counter extends Component {
state = { value: 0 };
handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
{this.state.value}
<button>+/<button>
<button>-/<button>
)
}
}
複製代碼/<code>
13.異步組件
1.場景:路由切換,如果同步加載多個頁面路由會導致緩慢
2.核心 API: loader:需要加載的組件 loading:未加載出來的頁面展示組件 delay:延遲加載時間 timeout:超時時間
3.使用方法: 安裝 react-loadable ,babel插件安裝 syntax-dynamic-import. react-loadable是通過webpack的異步import實現的
<code>const Loading = () => {
returnloading;
};
const LoadableComponent = Loadable({
loader: () => import("../../components/TwoTen/thirteen"),
loading: Loading
});
export default class Thirteen extends React.Component {
render() {
return <loadablecomponent>;
}
}
複製代碼/<code>
4.Loadable.Map() 並行加載多個資源的高階組件
14.動態組件
場景:做一個 tab 切換時就會涉及到組件動態加載 實質上是利用三元表達式判斷組件是否顯示
<code>class FourteenChildOne extends React.Component {
render() {
return這是動態組件 1;
}
}
class FourteenChildTwo extends React.Component {
render() {
return這是動態組件 2;
}
}
export default class Fourteen extends React.Component {
state={
oneShowFlag:true
}
tab=()=>{
this.setState({oneShowFlag:!this.state.oneShowFlag})
}
render() {
const {oneShowFlag} = this.state
return ();
<button>顯示組件{oneShowFlag?2:1}/<button>
{oneShowFlag?<fourteenchildone>:<fourteenchildtwo>}
}
}
複製代碼/<code>
如果是單個組件是否顯示可以用短路運算
<code>oneShowFlag&&<fourteenchildone>
複製代碼/<code>
15.遞歸組件
場景:tree組件 利用React.Fragment或者 div 包裹循環
<code>class Item extends React.Component {
render() {
const list = this.props.children || [];
return (
{list.map((item, index) => {
return (
<react.fragment>
{item.name}
{// 當該節點還有children時,則遞歸調用本身
item.children && item.children.length ? (
<item>{item.children}/<item>
) : null}
/<react.fragment>
);
})}
);
}
}
複製代碼/<code>
16.受控組件和不受控組件
受控組件:組件的狀態通過React 的狀態值 state 或者 props 控制
<code>class Controll extends React.Component {
constructor() {
super();
this.state = { value: "這是受控組件默認值" };
}
render() {
return{this.state.value};
}
}
複製代碼/<code>
不受控組件:組件不被 React的狀態值控制,通過 dom 的特性或者React 的ref 來控制
<code>class NoControll extends React.Component {
render() {
return{this.props.value};
}
}
複製代碼/<code>
導入代碼:
<code>export default class Sixteen extends React.Component {
componentDidMount() {
console.log("ref 獲取的不受控組件值為", this.refs["noControll"]);
}
render() {
return (
<controll>
<nocontroll> value={"這是不受控組件傳入值"}
ref="noControll"
>/<nocontroll>
);
}
}
複製代碼/<code>
17.高階組件(HOC)
17.1 定義
1.就是類似高階函數的定義,將組件作為參數或者返回一個組件的組件; 2.作用: 抽取重複代碼,實現組件複用,常見場景,頁面複用; 條件渲染,控制組件的渲染邏輯(渲染劫持),常見場景,權限控制; 捕獲/劫持被處理組件的生命週期,常見場景,組件渲染性能追蹤、日誌打點
17.2 實現方法
1.屬性代理
<code>import React,{Component} from 'react';
const Seventeen = WrappedComponent =>
class extends React.Component {
render() {
const props = {
...this.props,
name: "這是高階組件"
};
return <wrappedcomponent>;
}
};
class WrappedComponent extends React.Component {
state={
baseName:'這是基礎組件'
}
render() {
const {baseName} = this.state
const {name} = this.props
return
基礎組件值為{baseName}
通過高階組件屬性代理的得到的值為{name}
}
}
export default Seventeen(WrappedComponent)
複製代碼/<code>
2.反向繼承 原理就是利用 super 改變改組件的 this 方向,繼而就可以在該組件處理容器組件的一些值
<code> const Seventeen = (WrappedComponent)=>{
return class extends WrappedComponent {
componentDidMount() {
this.setState({baseName:'這是通過反向繼承修改後的基礎組件名稱'})
}
render(){
return super.render();
}
}
}
class WrappedComponent extends React.Component {
state={
baseName:'這是基礎組件'
}
render() {
const {baseName} = this.state
return
基礎組件值為{baseName}
}
}
export default Seventeen(WrappedComponent);
複製代碼/<code>
18.元素是否顯示
一般用三元表達式
<code> flag?顯示內容:''
flag&&顯示內容
複製代碼/<code>
19.Dialog 組件創建
Dialog 應該是用的比較多的組件,下面有三種不同的創建方法 方式 1:通過 state 控制組件是否顯示
<code> class NineteenChildOne extends React.Component {
render() {
const Dialog = () =>這是彈層1;
return this.props.dialogOneFlag && <dialog>;
}
}
複製代碼/<code>
方式 2:通過ReactDom.render創建彈層-掛載根節點外層 通過原生的createElement,appendChild, removeChild和react 的ReactDOM.render,ReactDOM.unmountComponentAtNode來控制元素的顯示和隱藏
NineteenChild.jsx
<code>import ReactDOM from "react-dom";
class Dialog {
constructor(name) {
this.div = document.createElement("div");
this.div.style.width = "200px";
this.div.style.height = "200px";
this.div.style.backgroundColor = "green";
this.div.style.position = "absolute";
this.div.style.top = "200px";
this.div.style.left = "400px";
this.div.id = "dialog-box";
}
show(children) {
// 銷燬
const dom = document.querySelector("#dialog-box");
if(!dom){ //兼容多次點擊
// 顯示
document.body.appendChild(this.div);
ReactDOM.render(children, this.div);
}
}
destroy() {
// 銷燬
const dom = document.querySelector("#dialog-box");
if(dom){//兼容多次點擊
ReactDOM.unmountComponentAtNode(this.div);
dom.parentNode.removeChild(dom);
}
}
}
export default {
show: function(children) {
new Dialog().show(children);
},
hide: function() {
new Dialog().destroy();
}
};
複製代碼/<code>
nineteen.jsx
<code>twoSubmit=()=>{
Dialog.show('這是彈層2')
}
twoCancel=()=>{
Dialog.hide()
}
複製代碼/<code>
20.React.memo
作用:當類組件的輸入屬性相同時,可以使用 pureComponent 或 shouldComponentUpdate 來避免組件的渲染。現在,你可以通過把函數組件包裝在 React.memo 中來實現相同的功能
<code>import React from "react";
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 傳入 render 方法的返回結果與
將 prevProps 傳入 render 方法的返回結果一致則返回 true,
否則返回 false
*/
if (prevProps.val === nextProps.val) {
return true;
} else {
return false;
}
}
// React.memo()兩個參數,第一個是純函數,第二個是比較函數
export default React.memo(function twentyChild(props) {
console.log("MemoSon rendered : " + Date.now());
return{props.val};
}, areEqual);
複製代碼/<code>
21.React.PureComponent
作用: 1.React.PureComponent 和 React.Component類似,都是定義一個組件類。 2.不同是React.Component沒有實現shouldComponentUpdate(),而 React.PureComponent通過props和state的淺比較實現了。 3.React.PureComponent是作用在類中,而React.memo是作用在函數中。 4.如果組件的props和state相同時,render的內容也一致,那麼就可以使用React.PureComponent了,這樣可以提高組件的性能
<code>class TwentyOneChild extends React.PureComponent{ //組件直接繼承React.PureComponent
render() {
return{this.props.name}
}
}
export default class TwentyOne extends React.Component{
render(){
return (
<twentyonechild>
)
}
}
複製代碼/<code>
22.React.Component
作用:是基於ES6 class的React組件,React允許定義一個class或者function作為組件,那麼定義一個組件類,就需要繼承React.Component
<code>export default class TwentyTwo extends React.Component{ //組件定義方法
render(){
return (
這是技巧22
)
}
}
複製代碼/<code>
23.在 JSX 打印 falsy 值
定義: 1.falsy 值 (虛值) 是在 Boolean 上下文中認定為 false 的值; 2.值有 0,"",'',``,null,undefined,NaN
<code>export default class TwentyThree extends React.Component{
state={myVariable:null}
render(){
return (
{String(this.state.myVariable)}
)
}
}
複製代碼/<code>
虛值如果直接展示,會發生隱式轉換,為 false,所以頁面不顯示
24.ReactDOM.createPortal
作用:組件的render函數返回的元素會被掛載在它的父級組件上,createPortal 提供了一種將子節點渲染到存在於父組件以外的 DOM 節點的優秀的方案
<code>import React from "react";
import ReactDOM from "react-dom";
import {Button} from "antd"
const modalRoot = document.body;
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement("div");
this.el.style.width = "200px";
this.el.style.height = "200px";
this.el.style.backgroundColor = "green";
this.el.style.position = "absolute";
this.el.style.top = "200px";
this.el.style.left = "400px";
}
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}
function Child() {
return (
這個是通過ReactDOM.createPortal創建的內容
);
}
export default class TwentyFour extends React.Component {
constructor(props) {
super(props);
this.state = { clicks: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}
render() {
return (
<button>點擊加1/<button>
點擊次數: {this.state.clicks}
<modal>
<child>
/<modal>
);
}
}
複製代碼/<code>
這樣元素就追加到指定的元素下面啦
25.在 React 使用innerHTML
場景:有些後臺返回是 html 格式字段,就需要用到 innerHTML 屬性
<code>export default class TwentyFive extends React.Component {
render() {
return (
這是渲染的 HTML 內容" }}>
);
}
}
複製代碼/<code>
26.React.createElement
語法: React.createElement( type, [props], [...children] )
源碼:
<code>export default class TwentySix extends React.Component {
render() {
return (
{React.createElement(
"div",
{ id: "one", className: "two" },
React.createElement("span", { id: "spanOne" }, "這是第一個 span 標籤"),
React.createElement("br"),
React.createElement("span", { id: "spanTwo" }, "這是第二個 span 標籤")
)}
);
}
}
複製代碼/<code>
原理:實質上 JSX 的 dom 最後轉化為 js 都是React.createElement
<code>// jsx 語法
this is spanOne
this is spanTwo
// 轉化為 js
React.createElement(
"div",
{ id: "one", class: "two" },
React.createElement( "span", { id: "spanOne" }, "this is spanOne"),
React.createElement("span", { id: "spanTwo" }, "this is spanTwo")
);
複製代碼/<code>
27.React.cloneElement
語法:
<code>React.cloneElement(
element,
[props],
[...children]
)
複製代碼/<code>
作用:這個方法的作用是複製組件,給組件傳值或者添加屬性 核心代碼
<code>React.Children.map(children, child => {
return React.cloneElement(child, {
count: _this.state.count
});
});
複製代碼/<code>
28.React.Fragment
作用:React.Fragment可以讓你聚合一個子元素列表,並且不在DOM中增加額外節點 核心代碼
<code>render() {
const { info } = this.state;
return (
{info.map((item, index) => {
return (
<react.fragment>
{item.name}
{item.age}
/<react.fragment>
);
})}
);
}
複製代碼/<code>
29.循環元素
內部沒有封裝像 vue 裡面 v-for 的指令,而是通過 map 遍歷
<code>{arr.map((item,index)=>{
return(
{item.name}
{item.age}
)
})}
複製代碼/<code>
30.給 DOM 設置和獲取自定義屬性
作用:有些要通過自定義屬性傳值
<code>export default class Thirty extends React.Component {
click = e => {
console.log(e.target.getAttribute("data-row"));
};
render() {
return (
點擊獲取屬性
);
}
}
複製代碼/<code>
31.綁定事件
場景:交互就會涉及到事件點擊,然後點擊選中值傳參也是一個很常見場景
<code>import React from "react";
import { Button } from 'antd'
export default class Three extends React.Component {
state = {
flag: true,
flagOne: 1
};
click(data1,data2){
console.log('data1 值為',data1)
console.log('data2 值為',data2)
}
render() {
return (
<button>點擊事件/<button>
);
}
}
複製代碼/<code>
使用方法在源碼 routes.js 有詳細使用
32.React-Router
32.1 V3和 V4的區別
1.V3或者說V早期版本是把router 和 layout components 分開; 2.V4是集中式 router,通過 Route 嵌套,實現 Layout 和 page 嵌套,Layout 和 page 組件 是作為 router 的一部分; 3.在V3 中的 routing 規則是 exclusive,意思就是最終只獲取一個 route; 4.V4 中的 routes 默認是 inclusive 的,這就意味著多個; 可以同時匹配和呈現.如果只想匹配一個路由,可以使用Switch,在 中只有一個 會被渲染,同時可以再在每個路由添加exact,做到精準匹配 Redirect,瀏覽器重定向,當多有都不匹配的時候,進行匹配
32.2 使用
<code>import { HashRouter as Router, Switch } from "react-router-dom";
class App extends React.Component{
render(){
const authPath = '/login' // 默認未登錄的時候返回的頁面,可以自行設置
let authed = this.props.state.authed || localStorage.getItem('authed') // 如果登陸之後可以利用redux修改該值
return (
<router>
<switch>
{renderRoutes(routes, authed, authPath)}
/<switch>
/<router>
)
}
}
複製代碼/<code>
V4是通過 Route 嵌套,實現 Layout 和 page 嵌套,Switch切換路由的作用
33.樣式引入方法
方式 1:import 導入
<code>import './App.css';
複製代碼/<code>
方式 2:內聯方式
<code>import React from 'react';
const Header = () => {
const heading = '頭部組件'
return(
{heading}
)
}
或者
import React from 'react';
const footerStyle = {
width: '100%',
backgroundColor: 'green',
padding: '50px',
font: '30px',
color: 'white',
fontWeight: 'bold'
}
export const Footer = () => {
return(
底部組件
)
}
複製代碼/<code>
34.動態綁定 className
原理:通過三元表達式控制 className 值
<code>render(){
const flag=true
return (
這是技巧 34
)
}
複製代碼/<code>
總結
這就是從實際項目開發總結的 React的 34 個技巧;
源碼地址:https://github.com/lanzhsh/react-vue-koa/tree/master/react-pc-skill
歡迎 star
閱讀更多 Echa攻城獅 的文章