【react学习】react入门必备知识

1.新建react项目

npx:

该方式不用全局下载脚手架,直接运行脚手架的命令

原理:先下载脚手架生成项目,完了再删掉脚手架

npx create-react-app my-app
npm start

全局安装脚手架来创建项目:

//全局安装脚手架
npm i -g create-react-app

//新建react项目
create-react-app my-app

npm start

2.js文件被看做是一个组件

当js文件中引入React时,本js文件会被看作是一个组件;若没被引用,则是普通js文件

import React from 'react;


function App(){
   return (
      <div className="App">
         根节点
      </div>
   );
}


export default App;

3.组件 

1.函数组件 

 函数组件性能最好,没有生命周期

 函数组件没有组件内部的数据

function App(){
   return (
      <div className="App">
         根节点
      </div>
   );
}


//另一种写法
function App(){
   //return这一行必须有代码在,否则会报错
   return <div className="App">根节点</div>
}

 2.箭头函数组件

const App = () =>{
   return <div>123</div>
}

//再精简
const App = () => <div>123</div>
扫描二维码关注公众号,回复: 13124217 查看本文章

 3.类组件 

  性能没有函数组件好,有生命周期

  有组件内部的数据

import React from 'react';


//声明一个类class
//必须要继承一个父类,表明当前class是一个react组件
//类组件必须提供一个render方法,方法里面必须返回标签
class App extends React.Component{
   render(){
      return <div>
              123
              <Bpp />//类组件嵌套使用
             </div>
   }
}



class Bpp extends React.Component{
   render(){
      return <div>345</div>
   }
}


export default App;

  tips:什么叫做组件内部的数据?

3.组件命名规范 

函数组件命名:首字母大写

标签里面没有东西的话,可以改为单标签即<Home></Home>改为<Home /> 

4.模板规范 

let num = 100;
let str = '哈哈';
let bo = true;//react不能直接渲染boolean类型
let obj = {  //不能直接渲染复杂类型
   name:'嘿嘿'
};


function App(){ 
   return <div>
     {num}
     {str}
     {'' + bo}
     {obj}
     {JSON.stringify(obj)}
   </div>
}

5.JSX的概念

JSX :javascript xml 

react中使用JSX语法,可以直接在js代码中写标签,在标签中写js代码,非常灵活,但可读性因水平而异。

6.简单使用JSX 

js与html标签相互嵌套使用 

let num = 123;

function App(){
   return <div> { <div> 哈{num}哈 </div> } </div>
}

7.将函数组件看成一个普通函数来调用 

function aaa(){
   return <div>AAA</div>
}


//这时候aaa函数会作为一个普通函数来执行,并不会发生异常
//问题:不是首字母应该大写吗?
//当函数作为一个组件来执行时,需要首字母大写:<Aaa></Aaa>
//当函数作为一个普通函数执行时,则不受约束
function App(){
   return <div> { aaa() } </div>
}

//aaa函数作为组件执行时
//此时首字母非大写,会抛异常
function App(){
   return <div><aaa /></div>
}

8.循环语法 

function App(){
  return <div>
    <p>哈哈1<p>
    <p>哈哈2<p>
    <p>哈哈3<p>
  </div>
}


//在jsx中使用map做映射并把数据返回
//不加key会抛异常,但并不影响运行
let list = ['哈哈1','哈哈2','哈哈3']
function App(){
   return <div>
     {list.map(function(item){
        return <p key={item}>{item}</p>
     })}
   </div>
}


//再精简
function App(){
  return <div>{list.map(item => <p key={item}>{item}</p>)}</div>
}

  9.组件内部的数据

   state允许组件自己修改数据 

   1.函数组件没有自己内部的数据

   函数组件数据无法响应式

let num = 0;

function addNum(){
  num++;
  console.log(num);
}

function App(){
   return (
     <div className='App'>
        <button onClick={addNum}>点击增加 num值</button>
        函数组件 { num }
     </div>
   );
}

//当点击按钮时,会打印+1后的num值,但函数组件中的num值并不会被更改
//但num改变时,并没有什么东西会通知函数组件进行改变,即这中间没有数据劫持,订阅通知等等操作。所以函数组件性能最好

2.类组件的内部数据

点击按钮时,num自增并渲染到页面上 

//state 类似于vue中的data

class App extends React.Component{
     state = {
        num: 100
     }
     
     addNum = () => {
       this.setState({
          num: ++this.state.num
       });
     }
     
     render(){
       return <div>
              <button onClick={this.addNum}>改变num</button>
              数据 {this.state.num}
       </div>
     }
}

10.状态state定义的两种方式

1.在类的属性上定义

class App extends React.Component{
     state = {
        num:11
     }
     
     render(){
        return <div>{this.state.num}</div>
     }
}

2. 直接在类的构造函数中定义

class App extends Component{

    constructor(){
       super();
       this.state = {
          num: 10
       };
    }
    
    render(){
       return <div> {this.state.num} </div>
    }
}

11.错误的修改state 

this.setState({})方法修改state会触发render方法更新视图,render方法一执行又会去修改state,所以会抛出递归超出最大层级的异常 

所以禁止在render中直接修改数据

class App{
   state = {
     num:10
   }
   
   render(){
     this.setState({
        num: 200
     });
     
     return <div> {this.state.num} </div>
   }
}

12.属性

react中父组件给子组件传递的数据,就叫属性。子组件中不能直接修改props的数据

//数据写死
class App extends React.Component{
   render(){
     return <div>
        <Btn aaa="按钮" bbb="哈哈"/>
     </div>
   }
}

//数据放到state中
class App extends React.Component{
    state = {
       aaa:'按钮',
       bbb:'哈哈'
    }
    render(){
       return <div>
        <Btn aaa={aaa} bbb={bbb}/>
        //多个值的时候可以使用展开运算符
        //vue3同样支持
        <Btn {...this.state} />
     </div>
    }
}


class Btn extends React.Component{
   render(){
     return <div>
            <button>{this.props.aaa} - {this.props.bbb}</button>
     </div>
   }
}

13.属性的默认值

 子组件中定义static defaultProps = {} 来说明属性的默认值

class App extends React.Component{
    render(){
       return <div>
        <Btn btnname='按钮的新值'/>
     </div>
    }
}


class Btn extends React.Component{
   static defaultProps = {
      btnname:'按钮的默认值'
   }

   render(){
     return <div>
            <button>{this.props.btnname}</button>
     </div>
   }
}

14.属性的类型校验

1.安装prop-types 

//安装
npm i prop-types --dev

//引入
import PropTypes from 'prop-types';

class App extends React.Component{
    render(){
       return <div>
        //btnname要求number类型,传入其他类型会抛异常
        <Btn btnname='按钮的新值'/>
     </div>
    }
}


class Btn extends React.Component{
   static propTypes = {
      btnname:PropTypes.number
   }

   render(){
     return <div>
            <button>{this.props.btnname}</button>
     </div>
   }
}

15.事件


class App extends React.Component{
    state = {
      num:10
    }
    
    //事件函数 一定要写成箭头函数的形式 
    addNum = () => {
      this.setState({
          num:++this.state.num
      });
    }
    
    render(){
       return <div>
       <button onClick={ this.addNum }>{ this.state.num }</button>
     </div>
    }
}

16.事件函数的this指向问题和解决方案


class App extends React.Component{
    state = {
      num:10
    }
    
    //当不使用箭头函数的时候,点击按钮this为undefined
    addNum(){
      this.setState({
          num:++this.state.num
      });
    }
    
    render(){
       return <div>
       <button onClick={ this.addNum }>{ this.state.num }</button>
     </div>
    }
}

tips:this的问题暂不清楚

this指向我查了一些资料,以下是比较认同的:

https://blog.csdn.net/a1059526327/article/details/104327176

https://www.zhihu.com/question/68092028

这是第一个博客中的一段:

这不是React的原因,这是JavaScript中本来就有的。如果你传递一个函数名给一个变量,然后通过在变量后加括号()来调用这个方法,此时方法内部的this的指向就会丢失 ,也就是说在JavaScript中就有这么一个陷阱:

注意到现在没有直接调用obj对象中的testLog方法,而是使用了一个中间变量tmpLog过渡,当使用括号()调用该方法时,方法中的this丢失了指向,会指向window,进而window.tmp未定义就是undefined;

let obj = {
 
    tmp:'Yes!',
 
    testLog:function(){
 
        console.log(this.tmp);
 
    }
 
};
 
let tmpLog = obj.testLog;
 
tmpLog();

 在React(或者说JSX)中,传递的事件参数不是一个字符串,而是一个实实在在的函数:

这样说,React中的事件名(eg:onClick、onChange)就是所举例子中的中间变量,React在事件发生时调用onClick,由于onClick只是中间变量,所以处理函数中的this指向会丢失,其实真正调用时并不是this.handleClick(),如果是这样调用那么this指向就不会有问题。真正调用时是onClick()。

为了解决这个问题,我们需要在实例化对象的时候,在构造函数中绑定this,使得无论事件处理函数如何传递,它的this的指向都是固定的,固定指向我们所实例化的对象。

解决this指向为undefined的四种方案:

(1). 在构造函数中使用bind绑定this

class Button extends React.Component {
 
constructor(props) {
 
    super(props);
 
    this.handleClick = this.handleClick.bind(this);
 
  }
 
  handleClick(){
 
    console.log('this is:', this);
 
  }
 
  render() {
 
    return (
 
      <button onClick={this.handleClick}>
 
        Click me
 
      </button>
 
    );
 
  }
 
}

(2). 在调用的时候使用bind绑定this

class Button extends React.Component {
 
  handleClick(){
 
    console.log('this is:', this);
 
  }
 
  render() {
 
    return (
 
      <button onClick={this.handleClick.bind(this)}>
 
        Click me
 
      </button>
 
    );
 
  }
 
}

(3). 在调用的时候使用箭头函数绑定this


class Button extends React.Component {
 
  handleClick(){
 
    console.log('this is:', this);
 
  }
 
  render() {
 
    return (
 
      <button onClick={()=>this.handleClick()}>
 
        Click me
 
      </button>
 
    );
 
  }
 
}

(4). 使用属性初始化器语法绑定this(实验性)

静态方法,this指向当前实例


class Button extends React.Component {
 
  handleClick=()=>{
 
    console.log('this is:', this);
 
  }
 
  render() {
 
    return (
 
      <button onClick={this.handleClick}>
 
        Click me
 
      </button>
 
    );
 
  }
 
}

四种方案之间的比较:

方式2和方式3都是在调用的时候再绑定this。

优点:写法比较简单,当组件中没有state的时候就不需要添加类构造函数来绑定this

缺点:每一次调用的时候都会生成一个新的方法实例,因此对性能有影响,并且当这个函数作为属性值传入低阶组件的时候,这些组件可能会进行额外的重新渲染,因为每一次都是新的方法实例作为的新的属性传递。

方式1在类构造函数中绑定this,调用的时候不需要再绑定

优点:只会生成一个方法实例,并且绑定一次之后如果多次用到这个方法也不需要再绑定。

缺点:即使不用到state,也需要添加类构造函数来绑定this,代码量多一点。

方式4:利用属性初始化语法,将方法初始化为箭头函数,因此在创建函数的时候就绑定了this。

优点:创建方法就绑定this,不需要在类构造函数中绑定,调用的时候不需要再作绑定。结合了方式1、方式2、方式3的优点

缺点:目前仍然是实验性语法,需要用babel转译

总结:

方式1是官方推荐的绑定方式,也是性能最好的方式。方式2和方式3会有性能影响并且当方法作为属性传递给子组件的时候会引起重渲问题。方式4目前属于实验性语法,但是是最好的绑定方式,需要结合bable转译。只要是需要在调用的地方传参,就必须在事件绑定的地方使用bind或者箭头函数,这个没有什么解决方案。

17.事件传递参数的方式

class App extends React.Component{

     showMsg = (msg) = > {
        console.log(this);
        console.log(msg);
     }
     
     render(){
        return (
          <div>
            //1.使用bind绑定this并传入参数
            <button onClick={this.showMsg.bind(this,'A')}>按钮</button>

            //2.在onClick使用箭头函数并在方法中传入参数
            <button onClick={() => { this.showMsg('B') }}>按钮</button>
          </div>
        )
     }
}

18.受控表单和非受控表单

1.受控表单 

实现了双向数据绑定的表单就是受控表单

input:

 1.使用defaultValue来为input框赋值

 2.使用onChange方法 + value属性 来获取和赋值input框

class App extends React.Component{
     state = {
        msg:'123'
     }
     
     //这里已实现一个双向数据绑定 
     inputChange = (e) => {
       console.dir(e.target);
       const { value } = e.target;
       this.setState({
          msg: value
       }); 
     } 
     
     render(){
        return (
          <div>
            <input type="text" defaultValue={this.state.msg} />
            <input type="text" onChange={this.inputChange} value={this.state.msg} />
          </div>
        );
     }
}

checkbox:

class App extends React.Component{
   state = {
     check:true
   }
   
   checkChange = (e) =>{
      this.setState({
         check:e.target.checked
      });
   }

   render(){
     return (
       <div>
         <input type="checkbox" onChange={ this.checkChange } checked={this.state.check} /> 
         <h3>{ '' + this.state.check }</h3>
       </div>
     );
   }
}

 2.非受控表单

未实现双向数据绑定的表单为非受控表单,对此react提供了操作dom的操作。

19.在react中操作dom元素

 通过 React.createRef() 创建引用,在标签上使用ref属性和引用相关联,来操作dom元素

class App extends React.Component{
     
     constructor(){
        super();
        //创建一个引用
        this.inRef = React.createRef();   
     }
     
     inputChange = () => {
       console.log(this.inRef.current);
     } 
     
     render(){
        return (
          <div onChange={this.inputChange}>
            //让ref和dom元素产生联系
            <input type="text" ref={this.inRef} />
          </div>
        );
     }
}

 20.组件传值

 1.父传子 

  类组件

  通过在父组件上自定义属性来传值给子组件 

class App extends React.Component{
   render(){
     return <div>
        <Btn btnname="按钮的名字"/>
     </div>
   }
}

class Btn extends React.Component{
   render(){
     return <div>
            <button>{this.props.btnname}</button>
     </div>
   }
}

 函数组件

 通过在函数组件中定义props形参来获取props 

class App extends React.Component{
   render(){
     return <div>
        <Btn btnname="按钮的名字"/>
     </div>
   }
}

const Btn = (props) => {
  render(){
     return <div>
            <button>{props.btnname}</button>
     </div>
  }
}

 2.子传父

类组件 

在父组件中定义一个方法,通过自定义属性将该方法传入;子组件通过this.props获取到该方法,子组件中一个方法用于触发this.props上的方法,子组件在this.props上的方法中传入参数,达到子向父传递参数的目的

class App extends React.Component{
    
    state = {
       num:10
    }

    add = (unit) => {
       this.setState({
          num: this.state.num + unit
       });
    }

    render(){
       return (
          <div>
             <h1>{this.state.num}</h1>
             <div>
                <AddBtn addUnit={ this.add } />
             </div>
          </div>
       )
    }
}


class AddBtn extends React.Component{

     onClick = () => {
       //通过props获取到父组件的函数并传入参数10
       this.props.addUnit(10);
     }  

     render(){
        return (
          <button onClick={this.onClick} >+10</button>
        )
     }
}

 函数组件

class App extends React.Component{
    
    state = {
       num:100
    }

    decrease = (unit) => {
       this.setState({
          num: this.state.num - unit
       });
    }

    render(){
       return (
          <div>
             <h1>{this.state.num}</h1>
             <div>
                <AddBtn decreaseUnit={ this.decrease } />
             </div>
          </div>
       )
    }
}



const DecreaseBtn = (props) => {
   onClick = () => {
       //通过props获取到父组件的函数并传入参数10
       props.decreaseUnit(10);
     }  

     render(){
        return (
          <button onClick={this.onClick} >-10</button>
        )
     }
}


//精简函数组件
const DecreaseBtn = (props) => <button onClick={() => {props.decreaseUnit(10)}} >-10</button>

21.生命周期

图示:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

常用生命周期:

展开不常用的声明周期后:

1.生命周期的三大阶段

挂载时 

1.构造函数constructor   

      什么时候触发?实例创建时触发

      有什么用?初始化数据   比方state或非受控表单

2.render

   什么时候触发?构造函数执行完毕后  (当state或props发生改变时,render会重新执行)

   有什么用?渲染视图

3.componentDidMount

   什么时候触发?视图渲染完毕

  有什么用?发送异步请求,类似于vue中的mounted

更新时

1.render

   什么时候触发? 当state或props发生改变时,render会重新执行

2.componentDidUpdate 

  什么时候触发?组件更新完毕 

 用处? 很少用到   比方说获取变更后的数据或者dom元素

tips:该声明周期不适宜改动state,因为state会引发render,进入死循环 

卸载时

1.componentWillUnmount

   什么时候触发?  1.路由页面切换   2.条件渲染

   有什么用? 1.清除定时器  2.解绑事件  3.取消订阅  4.取消一些异步任务

   

2.常见生命周期示例

import React, { Component } from 'react';
class App extends Component {
  // state = {
  //   num: 100,
  //   show: true
  // }
  constructor() {
    super();
    // 初始化 state的数据 
    let time = Date.now();
    time = time * 1000 - 100 + 10000;
    this.state = {
      time: time,
      show: true
    }

    // 非受控表单
    this.inpRef = React.createRef();
  }

  componentDidMount() {
    // 组件挂载完毕
    // console.log("componentDidMount");
  }

  componentWillUnmount() {
    // 组件卸载时
  }
  componentDidUpdate() {
    // 组件更新完毕时
    // console.log("componentDidUpdate");
  }
  render() {
    return (
      <div>
        {/* <button onClick={() => this.setState({ num: 10 })} >{this.state.num}</button> */}
        <button onClick={() => this.setState({ show: !this.state.show })} >切换显示</button>
        //react的条件渲染
        {this.state.show && <Btn></Btn>}
      </div>
    );
  }
}


let timeId = -1;
class Btn extends Component {
  state = {
    num: 0
  }
  componentDidMount() {
    console.log("Btn 加载完毕");
    timeId = setInterval(() => {
      console.log("定时器执行");
      this.setState({
        num: this.state.num + 1
      })
    }, 1000);
  }

  componentWillUnmount() {
    console.log("Btn 组件被卸载");
    clearInterval(timeId);
  }

  render() {
    // this.setState
    return (
      <div>
        <hr />
        <h1>{this.state.num}</h1>
        <button>子组件</button>
        <hr />
      </div>
    );
  }
}


export default App;

 22.shouldComponentUpdate

  shouldComponentUpdate(){}:是否允许组件更新,默认为true

  返回true,允许render被触发,可以更新;返回false,不允许render被触发,不可以更新

  具有提高组件性能的作用

import React, { Component } from 'react';


class App extends Component {
  state = {
    time: 0
  }
  // 是否允许组件更新 
  // 如果 返回了 true render 就会被触发  允许 更新
  // 如果 返回了 false render 就不会触发 不允许更新

  shouldComponentUpdate() {
    // console.log("shouldComponentUpdate");
    // return false;
    // return undefined == false
    
  }

  render() {
    // console.log("render");
    console.count("render");
    return (
      <div>
        <button onClick={() => { this.setState({ time: Date.now() }) }}   >
          {this.state.time}</button>
      </div>
    );
  }
}
export default App;

  解决render频繁没有必要的渲染 示例:

import React, { Component } from 'react';
class App extends Component {
  state = {
    list: ["海带"],
    text: "123"
  }

  // 输入框的值改变事件
  onChange = (e) => {
    this.setState({
      text: e.target.value
    })
  }

  // 点击添加
  onClick = () => {
    const { list, text } = this.state;
    const set = new Set(list);
    set.add(text);
    this.setState({ list: [...set] });
  }

  render() {
    const { text, list } = this.state;
    return (
      <div>
        <div>
          <input onChange={this.onChange} value={text} type="text" />
          <button onClick={this.onClick} >添加</button>
        </div>
        <MyUl list={list}></MyUl>
      </div>
    );
  }
}


class MyUl extends Component {


  shouldComponentUpdate(nextProps, nextState) {
    // nextProps.list  下一个  新的 数组
    // this.props.list 上一个 旧的 数组

    //判断上一个props的值与下一个props的值是否一致,来决定是否进行渲染
    return nextProps.list !== this.props.list;
  }

  render() {
    console.count("MyUl的render函数的调用次数")
    return (
      <ul>
        {this.props.list.map((v, i) => <li
          key={v}
        >{v}</li>)}
      </ul>
    );
  }
}

export default App;

  

23.高性能组件

1.类组件 

 引入PureComponent,并被类组件继承,即可使该类组件变为高性能组件

import React, { Component,PureComponent } from 'react';
class App extends Component {
  state = {
    list: ["海带"],
    text: "123"
  }

  // 输入框的值改变事件
  onChange = (e) => {
    this.setState({
      text: e.target.value
    })
  }

  // 点击添加
  onClick = () => {
    const { list, text } = this.state;
    const set = new Set(list);
    set.add(text);
    this.setState({ list: [...set] });
  }

  render() {
    const { text, list } = this.state;
    return (
      <div>
        <div>
          <input onChange={this.onChange} value={text} type="text" />
          <button onClick={this.onClick} >添加</button>
        </div>
        <MyUl list={list}></MyUl>
      </div>
    );
  }
}


class MyUl extends PureComponent{
  render() {
    console.count("MyUl的render函数的调用次数")
    return (
      <ul>
        {this.props.list.map((v, i) => <li
          key={v}
        >{v}</li>)}
      </ul>
    );
  }
}

export default App;

 2.函数组件

  引入memo,并使其包裹住函数组件,即可实现高性能

import React, { Component,PureComponent,memo } from 'react';
class App extends Component {
  state = {
    list: ["海带"],
    text: "123"
  }

  // 输入框的值改变事件
  onChange = (e) => {
    this.setState({
      text: e.target.value
    })
  }

  // 点击添加
  onClick = () => {
    const { list, text } = this.state;
    const set = new Set(list);
    set.add(text);
    this.setState({ list: [...set] });
  }

  render() {
    const { text, list } = this.state;
    return (
      <div>
        <div>
          <input onChange={this.onChange} value={text} type="text" />
          <button onClick={this.onClick} >添加</button>
        </div>
        <MyUl list={list}></MyUl>
      </div>
    );
  }
}


const MyUl = memo((props) => {
  console.count("函数组件的render");
  return (
    <ul>
      {props.list.map((v, i) => <li
        key={v}
      >{v}</li>)}
    </ul>
  );
});


export default App;

 24.jsx中的标签和样式

 1.jsx中特定的标签属性

//标签的class改为className,以防和类声明class冲突
<div className="col"></div>

//label标签for属性改为htmlFor
<label htmlFor="username"></label>

//富文本回显属性改为dangerouslySetInnerHTML={
   
   {__html:`富文本内容`}}
<div dangerouslySetInnerHTML={
   
   {__html:`<h1>哈哈</h1>`}}></div>

//行内样式的写法
<h1 style={
   
   {color:"red",backgroundColor:"yellow"}}>行内样式</h1>

 2.样式引用

 全局样式 

 只要在代码中这样引用,就一定是全局样式,不管在什么组件中:

//全局引用
import '../App.css';

 局部样式

 新建局部样式文件命名规则必须是:*.module.css ,引用时需要进行命名,否则还是全局样式

import SearchCss from './Search.module.css';

<input className={SearchCss.search} placeholder="搜索框" />

 25.react 路由

1. 安装sass(样式使用) 

npm install sass-loader node-sass

2.在新建react项目基础安装react路由

npm install react-router-dom

3.react-router示例

1.Router 负责把所有路由相关的组件都包裹起来

2.Link  路由超链接标签

3.Route 显示路由页面

4.BrowserRouter  可以实现单页应用,url发生改变了,页面不会直接刷新;但必须要和后台结合使用,打包后需要做处理,不然无法使用

5.HashRouter  最推荐的方式 #后面的路径改变

import React from "react";
import { HashRouter as Router, Link, Route } from "react-router-dom";

export default function App() {
  return (
    <Router>
      <nav>
        <Link to="/" >首页</Link>
        <Link to="/about" >关于</Link>
        <Link to="/users" >用户</Link>
      </nav>
      <section>
      <Route exact path="/"  component={Home}   />
      <Route exact path="/about"  component={About}   />
      <Route exact path="/users"  component={Users}   />
      </section>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

4.路由默认的匹配规则

react-router默认匹配规则是一种包含规则,而不是一种等于规则。

比如path="/about",则该path包含"/"和"/about"两种路由,则页面上会展示两种路由;而不是path="/" === path="/about"这种等于的规则。

在router标签上加上exact的属性,则会变成等于的规则,成为一种精确的路由匹配。

一旦加上路由精确匹配,就不能再实现路由嵌套

<Router exact path="/" component={Home}></Router>

5.路由参数

 使用路由,组件上多了三个对象:

  • history  负责编程式导航
  • location 和原生location相似
  • match 获取url的参数

 下图使用this.props.match.params来获取url上携带的参数

<Route path="/user/:id" component={UserDetail} ></Route>


// 用户详情页面
class UserDetail extends Component {
  render() {
    // console.log(this.props.match.params);
    console.log(this.props);
    return (
      <h1>用户的详情页面 {this.props.match.params.id}  </h1>
    );
  }
}

 编程式导航

this.props.history.push('/user/' + id);

6.路由重定向

 Redirect标签用于路由重定向,但是路由默认匹配规则并不能让Redirect起作用,该路由代码不加Switch标签时,路由默认走向为if(),if()并行,但我们需要让路由if()elseif()else()走向,所以提供Switch标签满足这一走向,Switch标签常与Redirect标签一起出现,配合使用

const App = () => {
  return <div>
    <Router>
      <nav>
        <Link to="/" >首页</Link>
        <Link to="/user" >用户</Link>
        <Link to="/about" >关于</Link>
      </nav>
      <section>
        <Switch>
          <Route exact path="/" component={Home} ></Route>
          <Route exact path="/user" component={User} ></Route>
          <Route path="/about" component={About} ></Route>
          <Route path="/404" component={PageNotFound} ></Route>
          {/* 重定向 */}
          <Redirect to="/404" />
        </Switch>
      </section>
    </Router>
  </div>
}

26.redux

和vuex一样,全局数据管理仓库,集中式管理数据的思想。

1.redux和react-redux

redux是一个完全独立的第三方库,可以使用在任意框架中,非react特供;而react-redux,是一个连接react和redux的库 

2.redux操作

//引入创建store的方法
import { createStore } from 'redux'

function counter(state = 100, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'decrement':
      return state - 1
    default:
      return state
  }
}

//创建store
let store = createStore(counter);


//导出 store
export default store;

  1.获取数据

import store from './store';

//获取仓库
store.getStore();

//获取仓库中状态
store.getState();

 2.修改数据

//dispatch 通知  INCREMENT判断+1
store.dispatch({ type:'INCREMENT' });

3.开启订阅

 开启订阅需要考虑放在哪里合适,订阅只需要开启一次 

 挂载时适合方订阅,订阅中更新state中的数据即可 

//仓库中的数据发生改变后通知更新
componentDidMount(){
   //这里挂载订阅的函数,用于取消订阅
   this.unsubscribe = store.subscribe(() => { 
      this.setState({
        num: store.getState();
      });
   }); 
}

4.取消订阅

componentWillUnmount(){
    //卸载组件时直接调用取消订阅的函数即可
    this.unsubscribe();
}

 5.redux工作流

1.store:指的是store/index.js代码

2.React Component:引用了仓库数据的标签

3.Reducer:负责接收通知,亲自修改数据

4.Action:用户点击按钮的行为或变量,触发了修改仓库中数据

27.react-redux

一个库,特供react

1.安装

npm i redux react-redux

2.react index中需要配置

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// 1 引入仓库 
import store from "./store";
// 2 引入 负责把  store 和 App 联系起来 Provider组件
import { Provider } from "react-redux";

ReactDOM.render(
  // 3  把  store 和 App 联系起来
  <Provider store={store} >
    <App />
  </Provider>,
  document.getElementById('root')
);

3.react App中需要配置

import React, { Component } from 'react';

//引用该函数负责接收全局数据
import { connect } from "react-redux";
class App extends Component {
    render(){
       console.log(this.props);
       return (
          <div>
             <h1>App</h1>
             <h2>{ this.props.a }</h2>
             <div>
                <button onClick={this.props.addNum}>+</button>
             </div>
          </div>
       )
    }
}

//定义组件属性props与state映射关系的对象
const mapStateToProps = (state) => {
   return {
      a: state
   }
}

//修改state中的数据 将props中的方法 直接触发到store的reducer上
const mapDispatchToProps = (dispatch) => {
   return {
      //this.props.addNum()
      addNum(){
         dispatch({ type:'INCREMENT' });   
      }
   }
}


export default connect(mapStateToProps,mapDispatchToProps)(App);

4.拆分Reducer

示例: 

 store--|

           index.js

           reducer--|

                         index.js

                         cartReducer.js

                         goodsReducer .js

index.js 总管理员,内部需要合并其他reducer 

// 1 引入其他的小管理员
import cartReducer from "./cartReducer";
import goodsReducer from "./goodsReducer";

// 2 引入一个负责合并管理员的 函数
import {combineReducers  } from "redux";

// 3 合并并导出
export default combineReducers({cartReducer,goodsReducer});

 cartReducer和cartReducer 小管理员

//购物车管理员
const defaultState = {
     num: 1000 
}

export default (state = defaultState,action) => {
    return state;
}
//商品管理员
const defaultState = {
     num: 10000 
}

export default (state = defaultState,action) => {
    return state;
}

获取仓库中的数据

const mapStateToProps = (state) => {
  return {
    // 获取商品的数量
    goodsNum: state.goodsReducer.num,
    // 获取购物车的数量
    cartNum: state.cartReducer.num
  }
}

 5.拆分action

示例: 

 store--|

          actionCreator--|

                                  index.js

其实就是把对象封装成一个函数,在App.js中引入来使用。

  actionCreator中index.js内容: 

// 负责创建 action 对象 
export const addGoodsNumAction = () => {
  return { type: "ADDGOODSNUM", unit: 1 }
}

// 初始化商品数据
export const initGoodsNumAction = (num) => {
  return { type: "INITGOODSNUM", value: num }
}

App.js中引入

// 引入action的创建函数
import { addGoodsNumAction ,initGoodsNumAction} from "./store/actionCreator";

组件中使用

dispatch(addGoodsNumAction())

猜你喜欢

转载自blog.csdn.net/THcoding_Cat/article/details/108901903