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
复制代码
相关文章
相关标签/搜索