react

1. React簡介

  • React 起源于 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram(照片交友) 的網站。做出來以后,發現這套東西很好用,就在2013年5月開源了
  • Angular1 2009 年 谷歌 MVC 不支持 組件化開發
  • 由于 React 的設計思想極其獨特,屬于革命性創新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。
  • 清楚兩個概念:
    • library(庫):小而巧的庫,只提供了特定的API;優點就是 船小好掉頭,可以很方便的從一個庫切換到另外的庫;但是代碼幾乎不會改變;
    • Framework(框架):大而全的是框架;框架提供了一整套的解決方案;所以,如果在項目中間,想切換到另外的框架,是比較困難的;

2. 前端三大主流框架

三大框架一大抄

  • Angular.js:出來較早的前端框架,學習曲線比較陡,NG1學起來比較麻煩,NG2 ~ NG5開始,進行了一系列的改革,也提供了組件化開發的概念;從NG2開始,也支持使用TS(TypeScript)進行編程;
  • Vue.js:最火(關注的人比較多)的一門前端框架,它是中國人開發的,對我我們來說,文檔要友好一些;
  • React.js:最流行(用的人比較多)的一門框架,因為它的設計很優秀;

3. React與vue的對比

組件化方面

  1. 什么是模塊化:是從代碼的角度來進行分析的;把一些可復用的代碼,抽離為單個的模塊;便于項目的維護和開發;
  2. 什么是組件化: 是從 UI 界面的角度 來進行分析的;把一些可服用的UI元素,抽離為單獨的組件;便于項目的維護和開發;
  3. 組件化的好處:隨著項目規模的增大,手里的組件越來越多;很方便就能把現有的組件,拼接為一個完整的頁面;
  4. Vue是如何實現組件化的: 通過 .vue 文件,來創建對應的組件;
    • template 結構
    • script 行為
    • style 樣式
  1. React如何實現組件化:大家注意,React中有組件化的概念,但是,并沒有像vue這樣的組件模板文件;React中,一切都是以JS來表現的;因此要學習React,JS要合格;ES6 和 ES7 (async 和 await) 要會用;

開發團隊方面

  • React是由FaceBook前端官方團隊進行維護和更新的;因此,React的維護開發團隊,技術實力比較雄厚;
  • Vue:第一版,主要是有作者 尤雨溪 專門進行維護的,當 Vue更新到 2.x 版本后,也有了一個以 尤雨溪 為主導的開源小團隊,進行相關的開發和維護;

社區方面

  • 在社區方面,React由于誕生的較早,所以社區比較強大,一些常見的問題、坑、最優解決方案,文檔、博客在社區中都是可以很方便就能找到的;
  • Vue是近兩年才火起來的,所以,它的社區相對于React來說,要小一些,可能有的一些坑,沒人踩過;

移動APP開發體驗方面

  • Vue,結合 Weex 這門技術,提供了 遷移到 移動端App開發的體驗(Weex,目前只是一個 小的玩具, 并沒有很成功的 大案例;)
  • React,結合 ReactNative,也提供了無縫遷移到 移動App的開發體驗(RN用的最多,也是最火最流行的);

4. 為什么要學習React

  1. 和Angular1相比,React設計很優秀,一切基于JS并且實現了組件化開發的思想;
  2. 開發團隊實力強悍,不必擔心斷更的情況;
  3. 社區強大,很多問題都能找到對應的解決方案;
  4. 提供了無縫轉到 ReactNative 上的開發體驗,讓我們技術能力得到了拓展;增強了我們的核心競爭力;
  5. 很多企業中,前端項目的技術選型采用的是React.js;

5. React中幾個核心的概念

虛擬DOM(Virtual Document Object Model)

  • DOM的本質是什么:瀏覽器中的概念,用JS對象來表示 頁面上的元素,并提供了操作 DOM 對象的API;

  • 什么是React中的虛擬DOM:是框架中的概念,是程序員 用JS對象來模擬 頁面上的 DOM 和 DOM嵌套;

  • 為什么要實現虛擬DOM(虛擬DOM的目的):為了實現頁面中, DOM 元素的高效更新

  • DOM和虛擬DOM的區別

  • DOM:瀏覽器中,提供的概念;用JS對象,表示頁面上的元素,并提供了操作元素的API;

  • 虛擬DOM:是框架中的概念;而是開發框架的程序員,手動用JS對象來模擬DOM元素和嵌套關系;

    + 本質: 用JS對象,來模擬DOM元素和嵌套關系;
    
    • 目的:就是為了實現頁面元素的高效更新;

      虛擬DOM的概念.png

Diff算法

  • tree diff:新舊兩棵DOM樹,逐層對比的過程,就是 Tree Diff; 當整顆DOM逐層對比完畢,則所有需要被按需更新的元素,必然能夠找到;

  • component diff:在進行Tree Diff的時候,每一層中,組件級別的對比,叫做 Component Diff;

    • 如果對比前后,組件的類型相同,則暫時認為此組件不需要被更新;
    • 如果對比前后,組件類型不同,則需要移除舊組件,創建新組件,并追加到頁面上;
  • element diff:在進行組件對比的時候,如果兩個組件類型相同,則需要進行 元素級別的對比,這叫做 Element Diff;

    [圖片上傳失敗...(image-19dc01-1595464286977)]

6. 創建項目

第一種方式

全局安裝create-react-app
npm install -g create-react-app

創建項目
create-react-app 項目名

第二種方式

npx —— 它是 [npm 5.2+ 附帶的 package 運行工具],這種方式會先局部安裝腳手架,然后在安裝項目

npx create-react-app your-app

項目安裝這需要等待一段時間,這個過程實際上會安裝三個東西

  • react: react的頂級庫,專門用于創建組件和虛擬DOM的,同時組件的生命周期都在這個包中
  • react-dom:專門進行DOM操作的,最主要的應用場景,就是ReactDOM.render()
  • react-scripts: 包含運行和打包react應用程序的所有腳本及配置

7. 創建虛擬dom并渲染

1.創建虛擬DOM元素:

// 這是 創建虛擬DOM元素的 API    <h1 title="啊,五環" id="myh1">你比四環多一環</h1>
//  第一個參數: 字符串類型的參數,表示要創建的標簽的名稱
//  第二個參數:對象類型的參數, 表示 創建的元素的屬性節點
//  第三個參數: 子節點
const myh1 = React.createElement('h1', { title: '啊,五環', id: 'myh1' }, '你比四環多一環')

2.渲染:

  // 3. 渲染虛擬DOM元素
  // 參數1: 表示要渲染的虛擬DOM對象
  // 參數2: 指定容器,注意:這里不能直接放 容器元素的Id字符串,需要放一個容器的DOM對象
  ReactDOM.render(myh1, document.getElementById('app'))
注:像這樣創建虛擬dom太麻煩了,
React.createElement('h1', { title: '啊,五環', id: 'myh1' }, '你比四環多一環')
所以有了jsx 語法,寫jsx語法,webpack的babel-loader會轉化為這樣
React.createElement('h1', { title: '啊,五環', id: 'myh1' }, '你比四環多一環')

8. JSX語法

  • 什么是JSX語法:

    就是符合 xml 規范的 JS 語法;(語法格式相對來說,要比HTML嚴謹很多)

    REACT獨有的語法 JAVASCRIPT+XML(HTML)

    和我們之前自己拼接的HTML字符串類似,都是把HTML結構代碼和JS代碼或者數據混合在一起了,

    但是它不是字符串

下面這些安裝,如果用腳手架創建的項目,腳手架已經做了,不用安裝了

  1. 如何啟用 jsx 語法?
  • 安裝 babel 插件

    • 運行cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
    • 運行cnpm i babel-preset-env babel-preset-stage-0 -D
  • 安裝能夠識別轉換jsx語法的包 babel-preset-react

    • 運行cnpm i babel-preset-react -D
  • 添加 .babelrc 配置文件

    {
      "presets": ["env", "stage-0", "react"],
      "plugins": ["transform-runtime"]
    }
    
  • 添加babel-loader配置項:

    module: { //要打包的第三方模塊
        rules: [
          { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ }
        ]
    }
    
  1. jsx 語法的本質:并不是直接把 jsx 渲染到頁面上,而是 內部先轉換成了 createElement 形式,再渲染的;

  2. 在 jsx 中混合寫入 js 表達式:在 jsx 語法中,要把 JS代碼寫到 { } 中,但是要求JS代碼指執行完成有返回結果(JS表達式)

    • 渲染數字
    • 渲染字符串
    • 渲染布爾值
    • 為屬性綁定值
    • 渲染jsx元素
    • 渲染jsx元素數組
  3. 在 jsx 中 寫注釋:推薦使用{ /* 這是注釋 */ }

  4. 為 jsx 中的元素添加class類名:需要使用className 來替代 classhtmlFor替換label的for屬性

  5. 在JSX創建DOM的時候,所有的節點,必須有唯一的根元素進行包裹;

  6. 在 jsx 語法中,標簽必須 成對出現,如果是單標簽,則必須自閉和!

當 編譯引擎,在編譯JSX代碼的時候,如果遇到了<那么就把它當作 HTML代碼去編譯,

如果遇到了 {} 就把 花括號內部的代碼當作 普通JS代碼去編譯;

9. React中創建組件

  • 不管是VUE還是REACT框架,設計之初都是期望我們按照“組件/模塊管理”的方式來構建程序的,也就是把一個程序劃分為一個個的組件來單獨處理

  • 優勢
    1.有助于多人協作開發
    2.我們開發的組件可以被復用

  • REACT中創建組件有兩種方式:
    函數聲明式組件
    基于繼承COMPONENT類來創建組件

  • SRC -> COMPONENT :這個文件夾中存放的就是開發的組件

  • 返回的jsx,必須是唯一的節點包裹,如果不想讓這個節點展示,可以用這個包裹 <> <h1>hhh</h1>

? <h1>hhh</h1> </>

第1種 - 函數聲明式組件(無狀態組件)

函數聲明式組件,如果要接收外界傳遞的數據,需要在 構造函數的參數列表中使用props來接收;

必須要向外return一個合法的JSX;

  • 創建組件:

    //Dialog.js
    import React from 'react';
    //=>每一個組件中都要導入REACT,因為需要基于它的CREATE-ELEMENT把JSX進行解析渲染呢
    
    /*
     * 函數式聲明組件
     *   1.函數返回結果是一個新的JSX(也就是當前組件的JSX結構)
     *   2.PROPS變量存儲的值是一個對象,包含了調取組件時候傳遞的屬性值(不傳遞是一個空對象)
     *   3.children 代表雙閉合組件中的子元素,相當于vue中插槽的概念
     *   
     */
    export default function Dialog(props) {//props形參,接收外界傳遞過來的數據,只讀的;不能被重新賦值;
        let {con, lx = 0, style = {},children} = props,
            
          //=>children(代表雙閉合組件中的子元素)
        return <section style={style}>
            <h2>{title}</h2>
            <div>{con}</div>
             {children}
        </section>;
    };
    
  • 使用組件,并為組件傳遞數據:

    import React from 'react';
    import ReactDOM from 'react-dom';
    

import Dialog from './component/Dialog'; //引入組件,組件的名稱首字母必須是大寫

ReactDOM.render(<div>
{/注釋:JSX中調取組件,只需要把組件當做一個標簽調取使用即可(單閉合和雙閉合都可以)/}
<Dialog con='哈哈哈' style={{color: 'red'}}/>

 {/*屬性值不是字符串,我們需要使用大括號包起來*/}
  <Dialog con='嘿嘿嘿' lx={1}  style={{color: 'red'}}>
      <span>1</span>  //這個雙閉合標簽里面的內容就是用children接受
      <span>2</span>
</Dialog>

</div>, root);



### 第2種 - 創建類式(有狀態組件)

>必須繼承 React.Component 和有一個render函數并且返回jsx

* 創建組件

```js
import React from 'react'

export default class Dialog extends React.Component {
      render() {
       //“this.props組件的屬性是只讀的”
        return <section>
            <h3>{this.props.lx}</h3>
            <div>{this.props.con}</div>
        </section>;
    }
   
}
  • 使用組件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Dialog from './01-component/Dialog' //引入組件
    
     
    ReactDOM.render(
      <Dialog con='哈哈哈' lx={10}/>, //使用組件
      document.getElementById('root')
    );
    

兩種創建組件方式的對比

  1. 函數創建出來的組件:叫做“無狀態組件”
  2. class關鍵字創建出來的組件:叫做“有狀態組件”
  1. 用函數**創建出來的組件:叫做“無狀態組件”【無狀態組件今后用的不多】
  2. class關鍵字創建出來的組件:叫做“有狀態組件”【今后用的最多】
  3. 什么情況下使用有狀態組件?什么情況下使用無狀態組件?
    • 如果一個組件需要有自己的私有數據,則推薦使用:class創建的有狀態組件;
    • 如果一個組件不需要有私有的數據,則推薦使用:無狀態組件;
    • React官方說:無狀態組件,由于沒有自己的state和生命周期函數,所以運行效率會比 有狀態組件稍微高一些;

有狀態組件和無狀態組件之間的本質區別就是:有無state屬性、和 有無生命周期函數;

10.組件的數據掛載方式

屬性(props)

props是正常是外部傳入的,屬性不能被組件自己更改,組件內部也可以設置默認值,字符類型,

屬性是描述性質、特點的,組件自己不能隨意更改。

在使用一個組件的時候,可以把參數放在標簽的屬性當中,所有的屬性都會作為組件 props 對象的鍵值。通過箭頭函數創建的組件,需要通過函數的參數來接收props

給屬性設置規則,需要安裝第三方插件

yarn add prop-types

給屬性設置默認值和規則

import React from 'react'
import PropTypes from 'prop-types';
export default class Dialog extends React.Component {
    //THIS.PROPS是只讀的,我們無法在方法中修改它的值,但是可以給其設置默認值 
    static defaultProps = {
        lx: '系統提示'
    };

    //PROP-TYPES是FACEBOOK公司開發的一個插件
     基于這個插件我們可以給組件傳遞的屬性設置規則
    static propTypes = {
         //=>不僅傳遞的內容是字符串,并且還必須傳遞
        con: PropTypes.string.isRequired, 

        //=>傳遞的內容需要是數字
        lx: PropTypes.number,

         //=>傳遞的內容需要是數組
        city:PropTypes.array,

        //=>數組中的每一項需要是字符串
        list:PropTypes.arrayOf(PropTypes.string),

        //=>自定義函數驗證
        address:function(props,propname){
            if(props[propname] !=='aa'){
                return new Error('內容非法')
            }
            //console.log(arguments);
        }
    };

    render() {
       //“this.props組件的屬性是只讀的”
        return <section>
            <h3>{this.props.lx}</h3>
            <div>{this.props.con}</div>
        </section>;
    }
   
}

狀態state

  • react中的state相當于vue中的data屬性

  • state顧名思義就是狀態,它只是用來控制這個組件本身自己的狀態,我們可以用state來完成對行為的控制、數據的更新、界面的渲染,由于組件不能修改傳入的props,所以需要記錄自身的數據變化。

  • 更改狀態,<span style="color:red">只能用 setState()方法</span>,當我們調用這個函數的時候,<span style="color:red">React.js 會更新組件的狀態 state ,并且重新調用 render 方法</span>,然后再把 render 方法所渲染的最新的內容顯示到頁面上

  • setState 本身在生命周期函數或者在合成事件中執行是異步的

在保證生命周期函數執行的順序不紊亂

保證其實現渲染隊列的機制(可以合并setState統一渲染)

在原生的事件綁定中和其他異步操作中的set-state是同步的,沒有渲染隊列效果了

import React, { Component } from 'react'

export default class movie extends Component {
    constructor(){
        super()
       //state定義的第一種方式
      //設置狀態
        this.state = {
            title:"中國機長"
        }
    }
  
  //state定義的第二種方式
 // state ={
//   title:"中國機長",
//   count:1
// }
  
    componentDidMount(){
      //更改狀態,因為是異步的,想立即獲取更改后的內容,可以用第2個參數:回調函數
      //更改state第一種方式
        this.setState({
            title:"戰狼"
        },() => {
          //相當于vue $nextTick
            console.log(this.state.title);
        }
        )
        
      //更改state的第二種方式
      //this.state.title="戰狼"
      //this.setState({})
    }
    render() {
        return (
            <div>
                {this.state.title}
            </div>
        )
    }
}

組件中的 propsstate/data 之間的區別

  • props 中的數據都是外界傳遞過來的;
  • state/data 中的數據,都是組件私有的;(通過 Ajax 獲取回來的數據,一般都是私有數據);
  • props 中的數據都是只讀的;不能重新賦值;
  • state/data 中的數據,都是可讀可寫的;

狀態提升

如果有多個組件共享一個數據,把這個數據放到共同的父級組件中來管理

受控組件與非受控組件

  • 受控組件

    • 其實就相當于vue中的v-model指令,在react中是自己手動實現,用onChange事件

    • <span style="color:red">雙向數據綁定就是受控組件</span>

    • 受控組件就是可以被 react 狀態控制的組件
      在 react 中,Input textarea 等組件默認是非受控組件(輸入框內部的值是用戶控制,和React無關)。

      但是也可以轉化成受控組件,<span style="color:red">就是通過 onChange 事件獲取當前輸入內容,將當前輸入內容作為 value 傳入,此時就成為受控組件</span>。
      好處:可以通過 onChange 事件控制用戶輸入,過濾不合理輸入。

    image.png
    • import React, { Component} from 'react';
      
      export default class Form extends Component {
        constructor() {
          super();
          this.state = {
            name: "",
            desc: "",
          }
           
        }
      
        handleChange(event) {
          this.setState({
            [event.target.name]: event.target.value //把表單中的值賦值給狀態
          })
        }
       
      
        render() {
          return (
            <div>//表單中的value,綁定了state上的狀態值
                 <input type="text" value={this.state.name} name="name" 
                                onChange={this.handleChange.bind(this)} /> 
                    <textarea          value={this.state.desc} name="desc"
                            onChange={this.handleChange.bind(this)}>
              </textarea> 
            </div>
          );
        }
      }
      
      
      
  • 非受控組件

    • 不受狀態控制的就是非受控組件

    • 基于REF操作DOM實現視圖更新的,叫做“非受控組件”

    • 使用場景,必須手動操作DOM元素,setState 實現不了的,例如文件上傳 <input type=file>

    • import React,{Component} from 'react';
      import ReactDOM from 'react-dom';
      
      export default class Sum extends Component{
          handleChange=(event)=>{
              let a = parseInt(this.refs.a.value||0);
              let b = parseInt(this.refs.b.value||0);
              this.refs.result.value = a+b;
          }
          render(){
              return (
                  //經過React封裝可以onChange可以寫在div上
                  <div onChange={this.handleChange}> 
                      <input type="text" ref="a" /> +
                      <input type="text" ref="b" /> = 
                      <input type="text" ref="result" />
                  </div>
                  //input是非受控組件,因為不受狀態控制
              )
          }
      }
      

11.組件中DOM樣式

  • 行內

    import React from 'react'
     
    export default class Movie extends React.Component {
        render() {
            return <div>
                {/* 行內樣式 */}
                <h1 style={{ fontSize: '20px', color: 'red' }}>正在熱映</h1>
            </div>
        }
    }
    
  • 外部引入

    <span style="color:red">這種引入方式是全局生效</span>

    import React from 'react'
    import './Movie.css' //從外部引入
     
    
    export default class Movie extends React.Component {
        render() {
            return <div className="wrap">
                <h1>正在熱映</h1>
            </div>
        }
    }
    
  • 模塊化

    <span style="color:red">這種引入方式是局部生效</span>,目的解決全局沖突

    • Vue 組件中的樣式表,有沒有 沖突的問題???

      Vue 組件中的樣式表,也有沖突的問題;但是,可以使用 <style scoped></style>

    • React 中,有沒有類似于 scoped 這樣的指令呢?

    ? 沒有;因為 在 React 中,根本就沒有指令的概念;

    ? 模塊塊就是為了解決沖突的問題,局部生效

    • 文件名必須是 *.module.css結尾,同樣的,如是要是.sass.scss的話,文件名格式應該是[name].module.sass[name].module.scss

    • 其實模塊化,就是webpack給css-loader配置了參數modules

     module: {  
        rules: [ 
          // 其中,有個固定的參數,叫做 modules , 表示為 普通的 CSS 樣式表,啟用模塊化
          { test: /\.scss$/, use: ['style-loader', 'css-loader?modules', 'sass-loader'] }  
        ]
      }
    
    import React from 'react'
    import common from "./common.module.css"  //引入css文件
    
    export default class Movie extends React.Component {
       render() {
           return <div>
               <h1 className={common.error}> //使用css
                   gggg
               </h1>
           </div>
       }
    }
    
    
    //common.module.css 文件
    .error{
       color:red;
       font-size: 30px;
    }
    
  • 不同的條件添加不同的樣式

    • 安裝
    yarn add classnames
    
    • 特點就是根據動態數據控制樣式

    • 局部,不會全局污染

    代碼:

    import React  from 'react'
    import classNames from 'classnames/bind' //引入第三方插件
    import styles from './styles.css'       //引入樣式
    
    let cx = classNames.bind(styles) //綁定在一塊
    export default class profile extends React.Component{
       render(){
           let names = cx({
               inProcess:true,  //根據數據來決定使用哪一個樣式
               error:false
           })
           return <div>
               <h1 className={names}>gp18</h1>
           </div>
       }
    }
    
    //styles.css文件
    .inProcess {
     color: orange;
     font-size: 30px;
    }
    
    .error {
     color: red;
     font-size: 30px;
    }
    
  • css-in-js

    • styled-components`是針對React寫的一套css-in-js框架,簡單來講就是在js中寫css

    • 解決全局污染

    • 安裝

    yarn add styled-components
    

    代碼:

    import React from 'react'
    
    import {Wrap} from './search-style' //引入樣式
    export default class Search extends  React.Component{
        render(){
            return <Wrap color="red">  //使用
                yyyy
                <h1>hhh</h1>
            </Wrap>
        }
    }
    
    //search-style.js 文件 
    import React from 'react'
    import styled from 'styled-components' //引入第三方插件
    
    const Wrap = styled.div`
        width:500px;
        height:500px;
        background:${(props)=>props.color}; //動態賦值
        font-size:30px;
        h1 {
          font-size:50px;
        }
    `
    export {
        Wrap
    }
    

12.事件處理

  • 采用on+事件名的方式來綁定一個事件,注意,這里和原生的事件是有區別的,原生的事件全是小寫onclick, React里的事件是駝峰onClickReact的事件并不是原生事件,而是合成事件

  • Event對象

    和普通瀏覽器一樣,事件handler會被自動傳入一個 event 對象,這個對象和普通的瀏覽器 event 對象所包含的方法和屬性都基本一致。不同的是 React中的 event 對象并不是瀏覽器提供的,而是它自己內部所構建的。它同樣具有event.stopPropagationevent.preventDefault 這種常用的方法

  • 事件的幾種寫法,目的能訪問到this

    • 第一種寫法 <span style="color:red"> (不能傳遞參數)</span>
    import React, { Component } from 'react'
    
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {
                name: 'gp18'
            }
        }
    
        handleClick=(event) => { //箭頭函數
            console.log(this.state.name);
        }
    
        render() {
            return (
                <div> 
                    /*這種綁定事件的方法,要想this能訪問到,
                               handleClick函數必須用箭頭函數
                               */
                  <button onClick={this.handleClick}>add</button>
                </div>
            )
        }
    }
    
    • 第二種寫法 <span style="color:red"> (不能傳遞參數)</span>
    import React, { Component } from 'react'
    
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {
                name: 'gp18'
            }
          
          //@key
            this.myhandleclick = this.handleClick.bind(this)
        }
    
        //@key 最后追加一個參數,即可接受event
        handleClick(val, event) {
            console.log(val, event, this.state.name);
        }
    
        render() {
            return (
                <div>
                    <button 
                    onClick={this.myhandleclick}  //@key
                    >添加</button>
                </div>
            )
        }
    }
    
    • 第三種寫法 <span style="color:red"> (可以傳遞參數)</span>
    import React, { Component } from 'react'
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {name: 'gp18'}
        }
    
       //觸發函數
        handleClick(val,event){
            console.log(this.state.name,val,event);
        }
    
        render() {
            return (
                <div>
                   //因為handleClick函數不是箭頭函數,需要改變this指向
                    <button 
                    onClick={this.handleClick.bind(this,'abc')}
                    >添加</button>
                </div>
            )
        }
    }
    
    • 第四種寫法 <span style="color:red"> (可以傳遞參數)</span>
    import React, { Component } from 'react'
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {name: 'gp18'}
        }
    
        handleClick(val,event){
            console.log(this.state.name,val,event);
        }
    
        render() {
            return (
                <div>  
                      <button onClick={
                          (event) =>{return this.handleClick('abc',event)}
                      }>添加</button>
                </div>
            )
        }
    }
    

13. 組件生命周期

  • 生命周期的概念

每個組件的實例,從創建、到運行、直到銷毀,在這個過程 中,會出發一些列 事件,這些事件就叫做組件的生命周期函數;

  • 生命周期分為三部分:

  • 組件創建階段

組件創建階段的生命周期函數,有一個顯著的特點:創建階段的生命周期函數,在組件的一輩子中,只執行一次;

  • componentWillMount

? 組件將要被掛載,此時還沒有開始渲染虛擬DOM

  • render:

? 第一次開始渲染真正的虛擬DOM,當render執行完,

? 內存 中就有了完整的虛擬DOM了

  • componentDidMount

? 組件完成了掛載,此時,組件已經顯示到了頁面上,

? 當這個方法執行完,組件就進入都了 運行中 的狀態

? 如果要初始化第三方的dom庫,也在這里進行初始化。只有到這里才能獲取到真實的dom.

? 通常在這里進行ajax請求

  • 組件運行階段

也有一個顯著的特點,根據組件的state和props的改變,有選擇性的觸發0次或多次;

  • componentWillReceiveProps:

? 組件將要接收新屬性,此時,只要這個方法被觸發,

? 就證明父組件為當前子組件傳遞了新的屬性值;

         props改變的時候
  • shouldComponentUpdate:

? 組件是否需要被更新,此時,組件尚未被更新,

? 但是,state 和 props 肯定是最新的

? 在渲染新的propsstate前,shouldComponentUpdate會被調用。默認為true

? 這個方法不會在初始化時被調用,也不會在forceUpdate()時被調用。

? 返回false不會阻止子組件在state更改時重新渲染。

? 如果shouldComponentUpdate()返回falsecomponentWillUpdate,rendercomponentDidUpdate不會被調用。

  • componentWillUpdate:

? 組件將要被更新,此時,尚未開始更新,

? 內存中的虛擬DOM 樹還是舊的

  • render:

? 此時,又要重新根據最新的 state 和 props

               重新渲染一棵內存中的 虛擬DOM樹,當 render 調用完畢,內存中的舊DOM樹,

? 已經被新DOM樹替換了!此時頁面還是舊的

  • componentDidUpdate

? 此時,頁面又被重新渲染了,state 和 虛擬DOM 和頁面已經 完全保持同步

  • 組件銷毀階段

也有一個顯著的特點,一輩子只執行一次;

  • componentWillUnmount:

? 組件將要被卸載,此時組件還可以正常使用;

? 在組件被卸載并銷毀之前立即被調用。在此方法中執行任何必要的清理,

? 例如使定時器無效,取消網絡請求或清理在componentDidMount中創建的任何監聽

  • 生命周期圖

    image.png

新版本v16.4 的生命周期函數

  • v17版本

componentWillMount,componentWillReceiveProps,componentWillUpdate這三個函數將要作廢

  • v16.4新增的生命周期函數

  • getDerivedStateFromProps

    //可以拿到父組件傳遞過來的屬性,同時可以拿到當前組件的state
    //可以把傳遞過來的屬性合并到當前組件state上
      static getDerivedStateFromProps(props, state) {
        console.log('getDerivedStateFromProps:', props, state)
        return {
          ...props,
          ...state
        }
      }
    
  • getSnapshotBeforeUpdate()

    在react render()后的輸出被渲染到DOM之前被調用。它使您的組件能夠在它們被潛在更改之前捕獲當前值(如滾動位置)。這個生命周期返回的任何值都將作為參數傳componentDidUpdate()

    只要寫這個生命周期函數就必須寫componentDidUpdate

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("getSnapshotBeforeUpdate:", prevProps, prevState)
        return {
          username:'hanye',
          age:20
        };
      }
    
    componentDidUpdate(prevProps, prevState,sanpshot){
        console.log("componentDidUpdate:",prevProps, prevState,sanpshot)
      }
    
image.png

PureComponent

PureComponnet里如果接收到的新屬性或者是更改后的狀態和原屬性、原狀態相同的話,就不會去重新render了
在里面也可以使用shouldComponentUpdate,而且。是否重新渲染以shouldComponentUpdate的返回值為最終的決定因素。此方法僅作為性能優化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因為這可能會產生 bug。你應該**考慮使用內置的 [PureComponent](當 props 或 state 發生變化時,shouldComponentUpdate() 會在渲染執行之前被調用。返回值默認為 true。首次渲染或使用 forceUpdate() 時不會調用該方法。)

import React, { Component,PureComponent } from 'react';
export default class Parent extends PureComponent { //@Key

 constructor() {
   super();
   this.state = { count: 1,}
  
 }

 render() {
   return (
     <div id="app">
       <button onClick={this.handleClick}> 強制更新 </button>
     </div>
   );
 }

 handleClick = () => {
   this.state.count = 100;
   this.forceUpdate(); //@key
 }
}

14 HOC高階組件

  • 定義

具體而言,高階組件是參數為組件,返回值為新組件的函數

const EnhancedComponent = higherOrderComponent(WrappedComponent);
  • 作用

在多個不同的組件中需要用到相同的功能,這個解決方法,通常是高階組件。

  • 例子
//定義一個高階組件 Hoc.js
import React, { Component } from 'react'
const Hoc = (WrapperCommonent)=>{
    return class Copyright extends Component {
        render() {
            return (
                <div>
                    <WrapperCommonent></WrapperCommonent>
                    <div>版權所有</div>
                </div>
            )
        }
    }
}
export default Hoc


//使用高階組件
import React, { Component } from 'react'
import Hoc from './Hoc'
class Base extends Component {
    render() {
        return (
            <div>
                react.js 是一個構建用戶節目的庫
            </div>
        )
    }
}
export default  Hoc(base)  //@Key

15.組件通信

父組件與子組件通信

父組件將自己的狀態傳遞給子組件,子組件當做屬性來接收,當父組件更改自己狀態的時候,子組件接收到的屬性就會發生改變

//父組件
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }
   render() {
       return (
        <Child count={this.state.count}></Child>  //狀態傳遞給子組件
       )
   }
}
//子組件
import React, {Component} from 'react'

export default class Child extends Component {
   render() {
       return  <div>
           {this.props.count} //子組件當做屬性來接收
       </div>
       
   }
}

子組件與父組件通信

父組件將自己的某個方法傳遞給子組件,在方法里可以做任意操作,比如可以更改狀態,子組件通過this.props接收到父組件的方法后調用。

//父組件
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }

   handclick=()=>{
       this.setState({
           count:10
       })
   }
   render() {
       return (
         <div>
               {this.state.count}
               <Child  fn={this.handclick}></Child>  //將方法傳遞給子組件
         </div>
       
       )
   }
}

//子組件
import React, {Component} from 'react'

export default class Child extends Component {
   componentDidMount(){
       this.props.fn()
   }
   render() {
       return  <div>
          child
       </div>
       
   }
}

跨組件通信 context

在react沒有類似vue中的事件總線來解決這個問題,我們只能借助它們共同的父級組件來實現,將非父子關系裝換成多維度的父子關系。react提供了context api來實現跨組件通信, React 16.3之后的contextapi較之前的好用。

在父組件上提供數據,其他后代組件,都能訪問到該數據

舉個計數器的例子,這個例子是context在實際項目這樣寫,具體基礎的看文檔

//Mycontext.js
import React, { Component,createContext} from 'react'
const {Provider,Consumer:MyConsumer} = createContext()

class MyProvider extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }

   incr=()=>{
       this.setState({
           count:this.state.count+1
       })
   }

   render() {
       return ( //需要共享的數據寫在value里
           <Provider value={{...this.state,'incr':this.incr,'decr':this.decr}}>
               {this.props.children}
           </Provider>
       )
   }
}
export {MyProvider,MyConsumer}

//父組件 Parent.js
import React, { Component } from 'react'
import Child from './Child'

import {MyProvider} from './Mycontext'
export default class Parent extends Component {
   render() {
       return (
           <MyProvider>
               <Child></Child>
           </MyProvider>
       )
   }
}
//后代組件 Child.js
import React, { Component } from 'react'
import {MyConsumer} from './Mycontext'

export default class Button extends Component {
   render() {
       return (//后代訪問共享數據
           <MyConsumer>
               {
                   ({incr,count})=>{
                   return  <button onClick={incr}>add按鈕:{count}</button>
                   }
               }
           </MyConsumer>
       )
   }
}

redux

  • 什么是redux?

    進行狀態統一管理的類庫(適應于任何技術體系的項目)

1.只要兩個或多個組件之間想要實現信息的共享,都可以基于redux解決,把共享的信息存儲到redux容器進行管理

2.還可以做臨時存儲,頁面加載的時候,把從服務器獲取的數據信息存儲到redux中

  • 為什么要用redux

因為對于react來說,同級組件之間的通信尤為麻煩,或者是非常麻煩了,所以我們把所有需要多個組件使用的state拿出來,整合到頂部容器,進行分發

  • 需要使用redux的項目

  • 用戶的使用方式復雜
  • 不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
  • 多個用戶之間可以協作
  • 與服務器大量交互,或者使用了WebSocket
  • View要從多個來源獲取數據
  • 從組件層面考慮,什么樣子的需要Redux:

  • 某個組件的狀態,需要共享
  • 某個狀態需要在任何地方都可以拿到
  • 一個組件需要改變全局狀態
  • 一個組件需要改變另一個組件的狀態
  • Redux的設計思想

  1. Web 應用是一個狀態機,視圖與狀態是一一對應的。
  2. 所有的狀態,保存在一個對象里面(唯一數據源)
  • Redux的使用的三大原則

  • Single Source of Truth(唯一的數據源)
  • State is read-only(狀態是只讀的)
  • Changes are made with pure function(數據的改變必須通過純函數完成)
  • Redux的流程

image.png
  • Store的角色是整個應用的數據存儲中心,集中大部分頁面需要的狀態數據;
    ActionCreators ,view 層與data層的介質;
    Reduce ,接收action并更新Store。
    所以流程是 用戶通過界面組件 觸發ActionCreator,攜帶Store中的舊State與Action 流向Reducer,Reducer返回新的state,并更新界面。

  • 自己實現的redux

<!DOCTYPE html>
<html lang="en">
<body>
    <button onclick="store.dispatch({type:'DECR',num:1})">-</button>
    <h1 id="count"></h1>
    <button onclick="store.dispatch({type:'INCR',num:1})">+</button>

    <script>
        //集中管理state,和dispatch,action
        const createStore = (reducer) => {
            let state;
            const getState = () => state
            //定義一個監聽器隊列,管理所有訂閱的方法
            const listeners = []
            const subscribe = (listener) => listeners.push(listener)

            const dispatch = (action) => {
                //1.執行REDUCER,修改容器中的狀態信息
                state = changeState(state, action)
                //2.容器中狀態信息經過REDUCER修改后,通知事件池中的方法依次執行
                listeners.forEach((item) => {
                    item()
                })
            }

            //=>創建容器的時候執行一次DISPATCH,
            //=>目的是把REDUCER中的默認狀態信息賦值給REDUX容器中的狀態
            dispatch({
                type: '$$INIT_DEFAULT_STATE'
            });

            return {
                getState,
                subscribe,
                dispatch
            }
        }

        //其實這個方法就是寫自己的邏輯的地方
        const changeState = (state = {
            count: 0
        }, action) => {
            switch (action.type) {
                case "INCR":
                    return {
                        count: state.count + action.num
                    }
                    case "DECR":
                        return {
                            count: state.count - action.num
                        }
                        default:
                            return state
            }
        }


        const store = createStore(changeState)
        store.subscribe(render)

        function render() {
            document.getElementById("count").innerHTML = store.getState().count;
        }
        render()
    </script>
</body>
</html>

react-redux

  • 安裝

yarn add redux react-redux
yarn add redux-thunk //異步請求數據要用到這個插件
  • redux

適應于所有項目,store = createStore(reducer),然后在需要的地方通過store.getState()去獲取數據,通過store.dispatch去更新數據,通過store.subscribe去訂閱數據變化然后進行setState...如果很多地方都這樣做一遍,實在是不堪其重

  • REACT-REDUX

  • 描述

? react-redux只是適應react項目

? 是把REDUX進一步封裝,適配REACT項目,讓REDUX操作更簡潔

? STORE文件夾中的內容和REDUX一模一樣

? 在組件調取使用的時候可以優化一些步驟

  • 相對于傳統的REDUX,我們做的步驟優化

? 導出的不在是我們創建的組件,而是基于CONNECT構造后的高階組件

? export default connect([mapStateToProps], [mapDispatchToProps])([自己創建的組件]);

? REACT-REDUX幫我們做了一件非常重要的事情:

? 以前我們需要自己基于SUBSCRIBE向事件池追 加方法,以達到容器狀態信息改變,

? 執行我們追加的方法,重新渲染組件的目的,但是現在不用了,

? REACT-REDUX幫我們做了這件事:“所有用到REDUX容器狀態信息的組件,都會向事件池 中追加一個方法,當狀態信息改變,通知方法執行,

? 把最新的狀態信息作為屬性傳遞給組件,組件的屬性值改變了,組件也會重新渲染”

  • react-redux 其實就提供了這四個東西

  • Provider 根組件

當前整個項目都在Provider組件下

作用就是把創建的STORE可以供內部任何后代組件使用(基于上下文完成的)

=>Provider組件中只允許出現一個子元素

=>把創建的STORE基于屬性傳遞給Provider(這樣后代組件中都可以使用這個STORE了)

redux的時候,每個組件想使用store,屬性都得傳store

這個函數的底層實現原理

? 其實就是根據上下文實現的

? PROVIDER:當前項目的“根”組件

? 接收通過屬性傳遞進來的STORE,把STORE掛載到上下文中,

? 這樣當前項目中任何一個組件中,想要使用REDUX中的STORE,

? 直接通過上下文獲取即可

class Provider extends React.Component {
   //=>設置上下文信息類型
   static childContextTypes = {
       store: PropTypes.object
   };

   //=>設置上下文信息值
   getChildContext() {
       return {
           store: this.props.store
       };
   }

   constructor(props, context) {
       super(props, context);
   }

   render() {
       return this.props.children;
   }
}
  • connect 高階組件

CONNECT:高階組件(基于高階函數:柯理化函數)創建的組件就是高階組件
@PARAMS
   mapStateToProps:回調函數,把REDUX中的部分狀態信息掛載到指定組件的屬性上
   function mapStateToProps(state){
        //=>state:REDUX容器中的狀態信息
      return {};  //=>RETURN對象中有啥,就把啥掛載到屬性上
        }
      ```
      
    mapDispatchToProps:回調函數,把一些需要派發的任務方法也掛載到組件的屬性上
     ```
   function mapDispatchToProps(dispatch){
        //=>dispatch:store中的dispatch
          return {
                init(){
                   dispatch({...});
                }
           };
           //=>RETURN啥就把啥掛載到屬性上(返回的方法中有執行dispatch派發任務的操作)
        }
      ```

  @RETURN
   返回一個新的函數 CONNECT-HOT

====

CONNECT-HOT
@PARAMS
傳遞進來的是要操作的組件,我們需要把指定的屬性和方法都掛載到當前組件的屬性上

@RETURN
返回一個新的組件Proxy(代理組件),在代理組件中,我們要獲取Provider在上下文中存儲的store,緊接著獲取store中的state和dispatch,把mapStateToProps、mapDispatchToProps回調函數執行,接收返回的結果,在把這些結果掛載到Component這個要操作組件的屬性上

function connect(mapStateToProps, mapDispatchToProps) {
return function connectHOT(Component) {
return class Proxy extends React.Component {
//=>獲取上下文中的STORE
static contextTypes = {
store: PropTypes.object
};

         constructor(props, context) {
             super(props, context);
        this.state = this.queryMountProps();
         }
     
         //=>基于REDUX中的SUBSCRIBE向事件池中追加一個方法,當容器中狀態改變,我們需要重新獲取最新的狀態信息,并且重新把COMPONENT渲染,把最新的狀態信息通過屬性傳遞給COMPONENT
         componentDidMount() {
            this.context.store.subscribe(() => {
                 this.setState(this.queryMountProps());
             });
     }
     
         //=>渲染COMPONENT組件,并且把獲取的信息(狀態、方法)掛載到組件屬性上(單獨調取POXY組件的是時候傳遞的屬性也給COMPONENT)
         render() {
             return <Component {...this.state} {...this.props}/>
         }
    
         //=>從REDUX中獲取最新的信息,基于回調函數篩選,返回的是需要掛載到組件屬性上的信息
         queryMountProps = () => {
             let {store} = this.context,
                 state = store.getState();
             let propsState = typeof mapStateToProps === 'function' ? mapStateToProps(state) : {};
            let propsDispatch = typeof mapDispatchToProps === 'function' ? mapDispatchToProps(store.dispatch) : {};
     
             return {
                 ...propsState,
                 ...propsDispatch
             };
         };
     }
}

}


  • mapStateToProps函數
let mapStateToProps = state => {

  //=>state:就是REDUX容器中的狀態信息

  //=>我們返回的是啥,就把它掛載到當前組件的屬性上
  //=>(REDUX存儲很多信息,我們想用啥就返回啥即 可)
  return {
     ...state.vote
  };

};
  • mapDispatchToProps
//=>把REDUX中的DISPATCH派發行為遍歷,也賦值給組件的屬性(ActionCreator)
let mapDispatchToProps = dispatch => {
   //=>dispatch:STORE中存儲的DISPATCH方法
   //=>返回的是啥,就相當于把啥掛載到組件的屬性上
   //(一般我們掛載一些方法,這些方法中完成了DISPATCH派發任務操作)
   return {
       init(initData) {
           //action.vote.init(initData)返回的就是類似{type:'aa',...}
           dispatch(action.vote.init(initData));
       }
   };
};

export default connect(state => ({...state.vote}), action.vote)(VoteBase);//=>REACT-REDUX幫我們做了一件事情,把ACTION-CREATOR中編寫的方法(返回ACTION對象的方法),自動構建成DISPATCH派發任務的方法,也就是mapDispatchToProps這種格式
  • 具體使用

  • 目錄

    store

? reducer 存放每一個模塊的reducer

? vote.js

? personal.js

? ...

? index.js 把每一個模塊的reducer最后合并成為一個reducer

? action 存放每一個模塊需要進行的派發任務(ActionCreator)

? vote.js

? personal.js

? ...

? index.js 所有模塊的ACTION進行合并

? action-types.js 所有派發任務的行為標識都在這里進行宏觀管理

? index.js 創建STORE

  • 代碼

  • reducer目錄的代碼

    image.png
  • action目錄的代碼

    aa.jpeg
  • action-types.js 文件

    image.png
  • index.js 文件

    image.png
  • 使用

  • 在入口index.js提供數據store

image.png
  • 在子組件消費數據
image.png

16 路由

  • 安裝

    react-router-dom
    
  • 第一個例子:基本路由

    • 基本使用

    • 路由跳轉

    • 匹配不到返回404

    • 兩種方式的傳參及接受參數

    • 使用component屬性渲染組件

    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Link,useLocation,Redirect,Switch} from 'react-router-dom'
    
    function Home(props) {
        //獲取參數
        let result = new URLSearchParams(useLocation().search)
        console.log(result.get("city"));
        
        return <h2>Home</h2>
    }
    
    function About(props) {
        //獲取參數
        console.log(props.match.params.id);
        return <h2>About</h2>;
    }
     
    
    export default class index extends Component {
        render() {
            return (
                <Router> //必須用Router包裹
                    <Link to="/">首頁</Link>&nbsp;
                    <Link to="/about/66">關于</Link>&nbsp;
                    
                    <Link to="/my">我的</Link>
                    
    
                    <hr/>
                    <Switch> //Switch:排他性,只能匹配到一個路由
                        <Route exact path="/index/" component={Home}></Route>
                        <Route path="/about/:id" component={About}></Route>
                        
                        <Route path="/my">我的</Route>
    
                        <Redirect exact from="/" to="/index?city=北京"></Redirect>
    
                        <Route>404</Route>
                    </Switch>
                </Router>
            )
        }
    }
    
    
  • 第二例子 嵌套路由

    //一級路由
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Link,useLocation,Redirect,Switch} from 'react-router-dom'
    
    import Order from './Order'
    export default class index extends Component {
        render() {
            return (
                <Router>
                 <Link to="/order">訂單</Link>
     
                  <Route  path="/order" component={Order}></Route>
                    
                </Router>
            )
        }
    }
    
    //二級路由 Order.js
    import React, { Component } from 'react'
    
    import  {Link,Route,Switch} from 'react-router-dom'
    export default class Movie extends Component {
        render() {
            return (
                <div>
                   <Link to="/order/pay">已支付</Link>
                   <Link to="/order/unpay">未支付</Link>
                <hr/>
                <Switch>
                    <Route exact path="/order/pay">pay</Route>
                    <Route exact path="/order/unpay">unpay</Route>
                </Switch>
               
                </div>
            )
        }
    }
    
  • 第三個例子 render

    • 用render函數渲染組件

    • 可以根據邏輯,來判斷渲染哪一個組件

    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Switch,Link} from 'react-router-dom'
    
    function Login(props) {
        return <h2>Login</h2>;
    }
    function Pos(props) {
        return <h2>Pos</h2>;
    }
    
    export default class index extends Component {
        constructor(){
            super()
            this.state = {isLogin:false}
        }
    
        render() {
            return (
                <Router>
                    <Link to="/index">首頁</Link>&nbsp;
                    <Link to="pos">職位管理</Link>
    
                    <Route path="/index">index</Route>
                    <Route path="/pos" render={()=>{
                     return  this.state.isLogin ?<Pos></Pos> : <Login></Login>
                    }}></Route>
                </Router>
            )
        }
    }
    
    
  • 第四例子 NavLink

    • 一般用NavLink比較多
    • 點中哪個鏈接,自動給加個 class="active"
    • Link 標簽不會自動加的
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,NavLink,useLocation,Redirect,Switch} from 'react-router-dom'
    
    export default class index extends Component {
        render() {
            return (
                <Router>  
                    <NavLink to="/index">首頁</NavLink>&nbsp;
                    <NavLink to="/about">關于</NavLink>&nbsp;
                    <hr/>
                   
                        <Route exact path="/index/" >index</Route>
                        <Route path="/about/"  >about</Route>
                </Router>
            )
        }
    }
    
  • 第五個例子 把a鏈接換成其他標簽

    • NavLink /Link 生成的是a鏈接,在外邊在用li標簽包裹一下怎么做呢
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,NavLink} from 'react-router-dom'
    
     //自定義鏈接
    const CustomLink = (props)=>{
      return <li>
          <NavLink to={props.path}>{props.name}</NavLink>
      </li>
    }
    export default class index extends Component {
        render() {
            return (
                <Router>
                      <CustomLink path="/index" name="首頁"></CustomLink>
                        <NavLink to="/movie">電影</NavLink>&nbsp;
                    <hr></hr>
                        <Route  path="/index">home</Route>
                        <Route  path="/movie">movie</Route>
                </Router>
            )
        }
    }
    
  • 第六個例子 編程式導航

    • 不像vue那么簡單,一個函數搞定
    • 這個是自己實現的
    import React, { Component } from 'react'
    import './style.css'  //這個就是定義激活active的樣式
    import {BrowserRouter as Router,Route,Link,NavLink,Redirect,Switch} from 'react-router-dom'
    
    import City from './City' //這個就是自己定義的編程式導航
    export default class index extends Component {
        
        render() {
            return (
                <Router>
                        <NavLink exact to="/">我的</NavLink> &nbsp;
                        <City path="/city"></City>
                    <hr></hr>
                   
                        <Route exact path="/">profile</Route>
                        <Route path="/city">City</Route>
                </Router>
            )
        }
    }
    
    //自己定義的編程式導航  City.js
    import React, { Component } from 'react'
    import {withRouter} from 'react-router-dom'
    
    class City extends Component {
        handleClick=()=>{
          //編程式導航
          this.props.history.push(this.props.path)
        }
    
        render() {
            return (
                <li className={this.props.path == this.props.location.pathname? 'active':''} onClick={this.handleClick}>城市</li>
            )
        }
    }
    //自定義鏈接是拿不到路由信息, 這個必須用withRouter這個高階函數增強一下,才能拿到路由相關的信息
    export default withRouter(City) 
    
  • 第七個例子 用children函數來渲染組件

    • 不管路由匹配上或者匹配不上,總會執行它

    • 只不過區別是匹配上 props.match 不為空,否則為空

    import React, { Component } from 'react'
    import './style.css'
    import {BrowserRouter as Router,Route,NavLink} from 'react-router-dom'
     
    export default class index extends Component {
        
        render() {
            return (
                <Router>
                        <NavLink exact to="/">我的</NavLink> &nbsp;
                        <Route path="/setting" children={(props)=>{
                            return <NavLink  to="/setting">設置</NavLink>
                        }}></Route>
    
                    <hr></hr>
                        <Route exact path="/">profile</Route>
                        <Route  path="/setting">setting</Route>
                </Router>
            )
        }
    }
    

安裝 React Developer Tools 調試工具

React Developer Tools - Chrome 擴展下載安裝地址

總結

理解React中虛擬DOM的概念
理解React中三種Diff算法的概念
使用JS中createElement的方式創建虛擬DOM
使用ReactDOM.render方法
使用JSX語法并理解其本質
掌握創建組件的兩種方式
理解有狀態組件和無狀態組件的本質區別
理解props和state的區別

相關文章

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。