React 之「爬坑記錄」

寫這篇文章初衷是整理一下本身這兩個多月的心路歷程,從剛開始 「入坑」react時的一臉懵,不知如何下手,到如今能夠寫簡單的業務。之因此定義名字爲「爬坑記錄」,並非由於中介跌入不少坑,而是在講述本身從徹底不瞭解這個框架 ,而後又一步步上道兒,我這裏就稱之爲「爬坑」,哈哈。因此想把本身的學習過程以及爬坑記錄下來,給本身往後翻閱,若有也在寫react ,想交流的小夥伴,文末有微信哦,哈哈。其實從很早就想研究下react了,只是時間上的不容許,如今終於能夠騰出時間來(其實平時工做較飽和,也只能擠業餘時間了)。html

------文中的示例都是本身通過實踐的,如理解有誤,還請告知哦!😂------vue

環境介紹:

部分純react組件示例,部分來自umi腳手架配合dva的項目組件示例,ui組件是ant-design,模版使用的是antdesign的pro-layout現成的模版。react

具體版本號以下:ios

"@ant-design/pro-layout": "4.7.0",
    "@antv/g2": "^3.5.11",
    "antd": "^3.25.2",
    "array-move": "^2.2.0",
    "umi": "2.12.3",
    "umi-plugin-react": "1.14.7",
    "uuid": "^3.3.3",
    "axios": "^0.19.0",
    "bizcharts": "^3.5.6",
    "classnames": "^2.2.6",
    "copy-to-clipboard": "^3.2.0",
    "dayjs": "^1.8.17",
    "immutable": "^4.0.0-rc.12",
    "lodash": "^4.17.15",
    "moment": "^2.24.0",
    "mz-modules": "^2.1.0",
    "parameter": "^3.6.0",
    "prop-types": "^15.7.2",
    "qrcode": "^1.4.4",
    "qs": "^6.9.1",
    "rc-form-hooks": "^0.0.1-alpha.22",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "swr": "^0.1.12",
    
複製代碼

關於react的生命週期

如同vue同樣,react 也是有本身的生命週期,方便咱們根據加載順序來執行相應的操做。json

經常使用的生命週期以下:axios

  1. 在渲染前調用:componentWillMount數組

  2. 在第一次渲染後調用:componentDidMountbash

  3. 在組件完成更新前調用:componentWillUpdate微信

  4. 在組件完成更新後當即調用:componentDidUpdatebabel

  5. 在組件接收到一個新的 prop (更新後)時被調用:componentWillReceiveProps

  6. 在組件從 DOM 中移除以前馬上被調用:componentWillUnmount

react的父組件和子組件生命週期執行順序:

加載渲染過程

父 bcomponentWillMount => 父 render => 子 componentWillMount =>子 render => 子 componentDidMount => 父componentDidMount
複製代碼

子組件經過props取值並更新過程:

子 componentWillUpdate => 父 render => 子 componentWillReceiveProps => 父 componentWillUpdate => 子 render => 子 componentDidUpdate => 父 componentDidUpdate
複製代碼

單一父 / 子組件(不依賴props)更新過程:

componentWillUpdate => render => componentDidUpdate 
複製代碼

銷燬過程:

componentWillUnmount
複製代碼

1. 編譯

let root =  document.getElementById('example');

  /* 
  * 相似vue的template ,第一個參數是插入的模版,第二個是插入的跟元素
  * 在react中樣式中的 class 要重命名爲 className 
  * render 是必不可少的,用來渲染到頁面上
  */

  ReactDOM.render(

    <h1 className="box">Hello, world!</h1>,

   root

  );

複製代碼

效果展現:


2. 定義變量

在react中定義變量是很方便的,能夠定義數組,數組中能夠包含咱們的html 標籤,而後能夠將變量直接帶入到頁面上。

<body>
    <div id="example"></div>
    <script type="text/babel">
      let root =  document.getElementById('example')
      let arr = [
        <h1 >Hello world!</h1>,
        <h2 >Hello React!</h2>,
      ];
      // 注意,react 的變量使用的是單花括號 {}
      ReactDOM.render(
        <div>{arr}</div>,
        root
      
      );
    </script>

複製代碼

效果展現:


3.組件

在React裏組件都是用class來寫的,React來寫組件無疑是很爽的,下面咱們就來試試。

<body>
    <div id="example"></div>
    <script type="text/babel">
    
      let root = document.getElementById('example');
      
      // class 的名字必須大寫,並繼承自 React.Component
      class HelloMessage extends React.Component {
        constructor(...args){
          super(...args);
          
          this.name=this.props.name
          this.job=this.props.job
          this.age = this.props.age
        }
        
        fn(){
          return "Aaa"
        }
        render() {
            // 變量還能夠直接定義標籤,style後面需跟一個{},而裏面的內容須要是一個json,因此此處看起來是兩個{{}}
            let div =<div style={{color:'red'}}>我是div</div>
          return (
            <div>
                // 花括號中的值還能夠參與計算
                姓名: {this.name}<br/>
                工做: {this.job}<br/>
                年齡: {this.age+3}<br/>
                
                // 花括號不只能夠輸出變量,還能夠輸出方法
                {this.fn()} <br/>
                
                // 將標籤輸出到頁面
                {div}
            </div>
            );
        }
      }

      ReactDOM.render(
        <HelloMessage name="John" job="teacher" age="18"/>,
        root
      );
    </script>
  </body>

複製代碼

4.循環

JSX語法容許咱們html 和 js 穿插來寫,咱們來看看最經常使用的循環怎麼寫。

在普通(非class)組件中添加循環:

<body>
    <div id="example"></div>
    <script type="text/babel">

      let root =  document.getElementById('example')
      let names = ['Alice', 'Emily', 'Kate'];

      ReactDOM.render(
        <div>
        {
          names.map(function (name, index) {
             // 循環中須要添加key值,用來保證惟一性
            return <div key={index}>Hello, {name}!</div>
          })
        }
        </div>,
        root
      );
    </script>
  </body>
複製代碼

效果展現:


在class 裏面寫循環:

<body>
    <div id="example"></div>
    <script type="text/babel">
      let root = document.getElementById('example');

      
      class HelloMessage extends React.Component {
        constructor(...args){
          super(...args)
        }
        
        render() {
          let arr =[];
          for(let i =0;i<5;i++){
          // 注意:這裏須要給每一個li 添加一個key
          
            arr.push(<li key={i}>{i}</li>)
          }
          return (
            <div>
                <ul>{arr}</ul>
            </div>
          
            );
        }
      }

      ReactDOM.render(
       <div>
          <HelloMessage />
        </div>,
        root
      );
    </script>
  </body>


複製代碼

效果展現:


5.組件嵌套

咱們日常的開發中,有時候須要用到些公共組件,那咱們就應對其進行封裝提取出來,如下是粗略版的父子組件嵌套寫法

<body>
        <div id="example"></div>
        <script type="text/babel">
        
     // 父組件   
     class Parent extends React.Component{
       constructor(...args){
           super(...args)
       }
       render(){
           return(
            <ul>
            // 將寫好的子組件嵌套進來便可
                    <Child/>
                    <Child/>
                    <Child/>
                    <Child/>
            </ul>
           )
       }
     }

    // 子組件
     class Child extends React.Component{
        constructor(...args){
            super(...args)
        }
        render(){
            return(
               <li>111</li> 
            )
        }
}

      ReactDOM.render(
       
        <Parent />
      ,
        document.getElementById('example')
      );
    </script>
    </body>

複製代碼

通常狀況下,render 裏面只會有一個最大的標籤包含,若是你有兩個標籤,請在外面添加一個包裹標籤,正確寫法:

ReactDOM.render(
       <div>
            <Parent></Parent>
            <Child></Child>
       </div>,
        document.getElementById('example')
      );
複製代碼

錯誤寫法:

ReactDOM.render(
 
       <Child></Child>
       
        <Parent></Parent>
       ,
        document.getElementById('example')
      );
複製代碼

在腳手架中還可用 react 中的Fragment 去充當咱們最外層的包裹層,它的好處是不會多生成一個div

import { Component, Fragment } from 'react'
class ConfigContent extends Component {
    constructor(...args){
        super(...args)
    }
    render(){
        return(
            <Fragment>
                <Parent></Parent>
                <Child></Child>
            </Fragment>
        )
    }
}

export default ConfigContent
複製代碼

組件能夠寫成單標籤或者雙標籤兩種形式。以下:

// 雙標籤
  <Parent></Parent>
  
 // 單標籤
 
 <Parent/>
複製代碼

6. 父子組件傳參 props

父組件給子組件傳遞參數,子組件接收,並渲染子組件: 父組件=>子組件

父組件

import { PureComponent } from "react";
import Child from "./child";

class Parent extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { id: 1 };
  }

  render() {
    return (
        <Child id={this.state.id} />
    );
  }
}

export default Parent;

複製代碼

子組件:

import { PureComponent } from "react";

class Child extends PureComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h1>child-page</h1>
        <p>{this.props.id}</p>
      </div>
    );
  }
}

export default Child;

複製代碼

效果展現:

子組件經過事件將子組件的值傳到父組件: 子組件=>父組件

子組件:

import { Button } from "antd";
import { PureComponent } from "react";

class Child extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { a: 1 };
  }

  action = {
    handleChange: () => {
      this.props.changeEvent(`子組件定義的值:${this.state.a}`);
    }
  };

  render() {
    return (
      <div>
        <h1>child-page</h1>
        <Button type="primary" onClick={this.action.handleChange}>
          改變父組件
        </Button>
      </div>
    );
  }
}

export default Child;

複製代碼

父組件:

import { PureComponent } from "react";
import { Button } from "antd";
import Child from "./child";

class Parent extends PureComponent {
  constructor(props) {
    super(props);
  }

  action = {
    changeEvent: mode => {
      console.log("父組件收到的值:", mode);
    }
  };

  render() {
    return (
      <div>
        <Child changeEvent={mode => this.action.changeEvent(mode)} />
      </div>
    );
  }
}

export default Parent;

複製代碼

點擊後的效果展現:

7. 添加事件

事件是咱們在交互過程當中必不可少的,那麼咱們試試看,在react中如何添加事件。

(1)咱們原生的添加事件的方式,採用的是小寫onclick

<button onclick="activateLasers()">
  Activate Lasers
</button>
複製代碼

(2)react的添加事件,採用駝峯的方式定義onClick

<button onClick={activateLasers}>
  Activate Lasers
</button>
複製代碼

下面介紹幾種在項目中添加事件的方式,你們可根據狀況選擇:

1. 在標籤中添加方法及函數體,也是和初始的事件最接近的方式:

<body>
        <div id="example"></div>
        <script type="text/babel">
 
           class Child extends React.Component {
            constructor(...args){
              super(...args)
              this.a=[123]
            }
           // 直接在標籤上添加
            render() {
              return (
                <div onClick={function(){
                  console.log("eee")
                }}>{this.a}</div>
              )
            }
            
           }
           ReactDOM.render(
           <Child/>,
             document.getElementById('example')
           )
    </script>
    </body>
複製代碼

2. 在class組件中添加方法(須要從新綁定this):

<body>
        <div id="example"></div>
        <script type="text/babel">
 

     class Cmp1 extends React.Component{
        constructor(...args){
            super(...args)
        }
        fn(){
          // props只讀的,這裏的值是不可改的,是從外面傳進來的
          console.log(this.props.a)  // 0 
        }
        // onClick 相似原生事件,此處bind就是把咱們的fn的this緊緊綁在組件上,此時的內部也能拿到咱們組件的this
        render(){
            return(
               <div>
                {this.props.a}
               <input type="button" value="+1" onClick={this.fn.bind(this)}/>
               </div>
            )
        }
    }

      ReactDOM.render(
       <div>
        
        <Cmp1 a={0}/>
       
       </div>,
        document.getElementById('example')
      );
    </script>
    </body>

複製代碼

3. 定義屬性,在其內部添加方法(簡單,this的綁定不會變):

<body>
        <div id="example"></div>
        <script type="text/babel">
 
           class Child extends React.Component {

            constructor(...args){
              super(...args)
              this.a=[123]
            }
            // 定義變量
            action ={
              fn(){
                console.log(23)
              }
            }
            // 在這裏直接調用,就不用綁定this了
            render() {
              return (
                <div onClick={this.action.fn}>{this.a}</div>
              )
            }
            

           }
           ReactDOM.render(
           <Child/>,
             document.getElementById('example')
           )
    </script>
    </body>
複製代碼

4. 第四種添加事件方式

<body>
        <div id="example"></div>
        <script type="text/babel">
 
           class Child extends React.Component {

            constructor(...args){
              super(...args)
              this.a=[123]
            }
         
              fn=()=>{
                console.log(23)
              }
    
            render() {
              return (
                <div onClick={this.fn}>{this.a}</div>
              )
            }
            
           }
           ReactDOM.render(
           <Child/>,
             document.getElementById('example')
           )
    </script>
    </body>

複製代碼

備註一下,關於事件傳參的寫法

import { PureComponent } from "react";
import { Button } from "antd";

class App extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { arr: [1, 2, 3, 4] };
  }

  action = {
    handleClick: i => {
      console.log(i.target.innerHTML);
    }
  };

  render() {
    return (
      <div>
        {this.state.arr.map((item, index) => {
          return (
            <Button
              key={index}
              onClick={index => this.action.handleClick(index)}
            >
              {item}
            </Button>
          );
        })}
      </div>
    );
  }
}

export default App;

複製代碼

效果展現:

8.利用state製做一個 input ++ 功能

state 構造函數是惟一可以初始化 this.state 的地方,接收一個對象,是可變的,能夠是內部加的,也能夠從外部傳進來,this.setSate是惟一改變state的方式。

// 製做一個input ++ 功能
<body>
        <div id="example"></div>
        <script type="text/babel">
 

     class Cmp1 extends React.Component{
        constructor(...args){
            super(...args)
           
            this.state={a:0}
        }
       
        fn(){
          // this.setSate是惟一改變state的方式
        this.setState({a:this.state.a+1})
        }
        render(){
            return(
               <div>
               {this.state.a}
               <input type="button" value="+1" onClick={this.fn.bind(this)}/>
               </div>
            )
        }
    }

      
      ReactDOM.render(
       <div>
            <Cmp1/>
       </div>,
        document.getElementById('example')
      );
    </script>
    </body>
複製代碼

頁面效果:


9.路由傳參 && 獲取參數

咱們在跳轉路由時,有時須要給跳轉到到頁面攜帶一些參數來定位頁面的顯示或回顯數據,此小節咱們就來看看如何傳參,以及入股取參。

首先咱們先拿一下props,看看都有哪些參數:

console.log(this.props);
複製代碼

參數以下:

咱們來具體解析一下:

history:包含了路由push replace goBack 等方法,以及可拿到query state 等參數

history:{
   
    location:{
        pathname:'/dashboard/workplace', // url地址
        search:'?name='xiaoxiao', // 拿到的是完整的參數字符串 hash:'', query:{name:'xiaoxiao'}, // 拿到參數的對象格式 state:undefined // 拿到經過state傳入的參數 }, push:function push(path,state){} , // 跳轉到指定路徑 replace:function replace(path,state){} , // 跳轉到指定路徑,不會保留history goBack:function goBack(path,state){} , // 返回上一個路由地址 } 複製代碼

這個location 同上面的location,可拿到query參數 以及state 參數

location:{
    pathname:'/dashboard/workplace',
    search:'?name='xiaoxiao', query:{name:'xiaoxiao'}, state:undefined } 複製代碼

包含了具體的 url 信息,並能夠拿到params的參數的值。

match:{
    path:'/dashboard/workplace',
    url:'/dashboard/workplace',
    params:{}
}
複製代碼

接下來咱們就使用:this.props.history.push 來模擬跳轉,並攜帶參數:

1. query

經過url來進行傳參,地址欄是可見的

⚠️注意️:刷新頁面後,參數不會丟失,可傳對象

A 頁面:

this.props.history.push({
    pathname: "/dashboard/workplace",
    query: { name: "xiaoqiu" }
});

複製代碼

B頁面 (參數在url裏問號後顯示)

http://localhost:8000/#/dashboard/workplace?name=xiaoqiu
複製代碼

打印一下咱們接收到的參數:

2. state

state傳參,同query差很少,只是屬性不同,並且state傳的參數是加密的,不會在地址欄顯示

注意️:刷新頁面後,參數就會丟失,可傳對象

A頁面:

this.props.history.push({
    pathname: "/dashboard/workplace",
    state: { name: "xiaoqiu" }
});
複製代碼

B頁面: (參數並無出如今地址欄哦!)

http://localhost:8000/#/dashboard/workplace
複製代碼

打印一下咱們接收到的參數:

3. search

searchquery參數同樣是經過url進行傳輸

⚠️注意️:刷新頁面後,參數不會丟失,但只可傳字符串,不能傳輸對象

A頁面:

this.props.history.push({
    pathname: "/dashboard/workplace",
    search: "a=1&b=2"
});
複製代碼

B頁面

http://localhost:8000/#/dashboard/workplace?a=1&b=2
複製代碼

打印一下咱們接收到的參數:


總結一下:

此時咱們注意到不管是經過query或是search 傳參,返回參數時二者也同時都能取到,只是展示方式不一樣。query是以對象形式展示,search是以字符串展示,若是你想在地址欄顯示,那就用query,若是想加密傳輸,那就用state,但要注意使用state傳遞參數刷新後就會丟失哦!

10.ref獲取組件實例

經過給組件綁定ref 能夠獲取到整個子組件的實例,進而可傳參數並調用其方法

1. 傳統的ref,獲取當前組件的參數以及事件

import { Button } from "antd";
import { PureComponent } from "react";

class Child extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { a: 1 };
  }

  action = {
    handleChange: () => {
      console.log(this.refs["button"]);
    }
  };

  render() {
    return (
      <div>
        <h1>child-page</h1>
        <Button type="primary" onClick={this.action.handleChange} ref="button">
          改變父組件按鈕
        </Button>
      </div>
    );
  }
}

export default Child;

複製代碼

控制檯打印ref拿到的組件參數以及方法:

2. reactcreateRef,拿到子組件的參數和方法

import { PureComponent, createRef } from "react";
import { Button } from "antd";
import Child from "./child";

class Parent extends PureComponent {
  constructor(props) {
    super(props);
    this.children = createRef();
    this.state = { id: 1, arr: [1, 2, 3, 4] };
  }

  action = {
    handleClick: () => {
        console.log(this.children);
    }
  };

  render() {
    return (
      <div>
        <Button onClick={() => this.action.handleClick()}>
            按鈕
        </Button>
        子組件:
        <Child ref={this.children} />
      </div>
    );
  }
}

export default Parent;

複製代碼

控制檯輸出子組件的值:

寫到此處,並無結束哦!接下來我還會持續追加,看文章的小夥伴們能夠添加一下關注哦!

做者:Christine    
出處:https://juejin.im/post/5a125827518825293b4fea8a
版權全部,歡迎保留原文連接進行轉載:) 
複製代碼

若是你對我對文章感興趣或者有些建議想說給我聽👂,也能夠添加一下微信哦!

郵箱:christine_lxq@sina.com

若是親感受個人文章還不錯的話,能夠一下添加關注哦!

最後:
        祝各位工做順利!
                        -小菜鳥Christine
複製代碼
相關文章
相關標籤/搜索