React基础

文章目录

React基础

1、React

1.1、使用React脚手架安装

1.全局命令cnpm install -g create-react-app

2.更换源 npm config set registry https://registry.npm.taobao.org

npm config set registry https://registry.npm.taobao.org
-- 配置后可通过下面方式来验证是否成功
npm config get registry
-- 显示出上述地址的话就是更换成功

3.创建todolist create-react-app todolist

4.进入目录 cd todolist

5.启动项目 npm start

1.2、脚手架代码精简

项目目录说明:

node_modules ===> npm 下载的相关依赖
package.json ===> node包文件 包括项目的一些介绍 以及 相关文件版本

public文件夹:

index.html ===> 项目的首页

favico.ico ===> 项目图标

mainfest.json ===> 定义网页快捷方式

src 文件夹:

index.js ===> 整个项目的入口文件
registerServiceWorker.js ===> 离线缓存

代码工程精简

src 目录下仅保存 App.jsindex.js 文件

index.js 是入口文件,精简代码如下:

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

ReactDOM.render(<App />, document.getElementById('root'));

App.js 文件精简代码如下:

import React from 'react';

function App() {
  return (
    <div>
      hello React...
    </div>
  );
}

export default App;

2、React基础语法

2.1、 JSX 语法基础

React 使用 JSX 来替代常规的JavaScript

JSX 是按照 XML 语法规范 的 JavaScript 语法扩展。

JSX 有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化;
  • 它是类型安全的,在编译过程中就能发现错误;
  • 使用 JSX 编写模板更加简单快速。

**JSX 语法的本质:**并不是直接把 JSX 渲染到页面上,而是内部先转换成了 createElement 形式,再渲染的。

**JSX 注释:**推荐使用 {/* 这是注释 */}

**JSX中添加class类名:**需要使用 className 来替代 classhtmlFor 替代 labelfor 属性;

JSX创建DOM的时候,所有节点必须有唯一的根元素进行包裹;

JSX语法中,标签必须成对出现,如果是单标签,则必须自闭和;

代码示例:

const mydiv = <div>这是一个Div标签</div>;
ReactDOM.render(mydiv, document.getElementById('root'));

使用组件化开发代码示例:

App.js 组件文件代码

import React from 'react';

class App extends React.Component{
  render(){
    return (
      <div>
        {1+1}
        <hr/>
        Hello,Reactjs!!
      </div>
    );
  }
}

export default App;

在其他文件中使用 JSX 语法引用组件:

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

ReactDOM.render(<App />, document.getElementById('root'));

渲染数字

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

let a = 10;

ReactDOM.render(
    <div>{a}</div>
, document.getElementById('root'));

渲染字符串

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

let str = 'hello react';

ReactDOM.render(
    <div>{str}</div>
, document.getElementById('root'));

渲染布尔类型

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

let rel = true;

ReactDOM.render(
    <div>{rel ? '结果为真' : '结果为假'}</div>
, document.getElementById('root'));

渲染属性值

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

let title = "this is a div";

ReactDOM.render(
    <div title={title}>Hello React</div>
, document.getElementById('root'));

渲染标签对象

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

const h1 = <h1>Hello React!</h1>;

ReactDOM.render(
    <div>
        {h1}
    </div>
, document.getElementById('root'));

渲染数组

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

const arr = [
    <h1>1</h1>,
    <h2>2</h2>,
];

ReactDOM.render(
    <div>
        {arr}
    </div>
, document.getElementById('root'));

将普通数组转为 JSX 数组,并渲染到页面中

解决 Warning: Each child in a list should have a unique “key” prop.

方法一:

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

//原数组
const arr = ['html','css','vue'];
//新数组
const newArr = [];
//forEach()方法没有返回值
arr.forEach((item,index) => {
    const temp = <h2 key={index}>{item}</h2>
    newArr.push(temp);
});

ReactDOM.render(
    <div>
        {newArr}
    </div>
, document.getElementById('root'));

方法二:

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import './style.css';

//原数组
const arr = ['html','css','vue'];

ReactDOM.render(
    <div>
    	{/* map()方法有返回值 */}
        {arr.map((item,index) => {
        return <h2 key={index}>{item}</h2>
        })}
    </div>
, document.getElementById('root'));

2.2、React组件

组件的概念

React 应用都是构建在组件之上。

组件作为React的核心内容,是View的重要组成部分,每一个View页面都由一个或多个组件构成,可以说组件是React应用程序的基石。在React的组件构成中,按照状态来分可以分为有状态组件和无状态组件。

组件的特点

组件化的概念在后端早已存在多年,只不过近几年随着前端的发展,这个概念在前端开始被频繁提及,特别是在MV*的框架中。

前端中的“组件化”这个词,在UI这一层通常指“标签化”,也就是把大块的业务界面,拆分成若干小块,然后进行组装。

狭义的组件化一般是指标签化,也就是以自定义标签(自定义属性)为核心的机制。

广义的组件化包括对数据逻辑层业务梳理,形成不同层级的能力封装。

  • 标准性

    任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。

  • 组合性

    组件之前应该是可以组合的。我们知道前端页面的展示都是一些HTML DOM的组合,而组件在最终形态上也可以理解为一个个的HTML片段。那么组成一个完整的界面展示,肯定是要依赖不同组件之间的组合,嵌套以及通信。

  • 重用性

    任何一个组件应该都是一个可以独立的个体,可以使其应用在不同的场景中。

  • 可维护性

    任何一个组件应该都具有一套自己的完整的稳定的功能,仅包含自身的,与其它组件无关的逻辑,使其更加的容易理解,使其更加的容易理解,同时大大减少发生bug的几率。

函数组件

无状态函数式组件形式上表现为一个只带有一个 render() 方法的组件类,通过函数形式或者 ES6 箭头 function的形式在创建,并且该组件是无state状态的。具体的创建形式如下

import React from 'react';

//定义一个React组件
function App() {
  return (
    <div>
      hello React...
    </div>
  );
}

export default App;

class组件

React.Component 是以 ES6 的形式来创建react的组件的,是React目前极为推荐的创建有状态组件的方式,形式改为 React.Component 形式如下

import React from 'react';

//定义一个React组件
class App extends React.Component{
  render(){
    return (
      <div>
        Hello,Reactjs!!
      </div>
    );
  }
}

export default App;

在其他文件中引用组件

import React from 'react';
import ReactDOM from 'react-dom';
//App组件,组件要求大写字母开头
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

函数组件和class组件的区别

用构造函数创建出来的组件,叫做“无状态组件”;

用class关键字创建出来的组件,叫做“有状态组件”;

有状态组件和无状态组件之间的本质区别是有无state属性

注意:

使用class 关键字创建的组件,有自己的私有数据(this.state)和生命周期函数;

使用function创建的组件,只有props,没有自己的私有数据和生命周期函数;

函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件

区别函数组件class组件
是否有 this没有
是否有生命周期没有
是否有状态 state没有

3、React核心属性

3.1.1、props属性

react中说的单向数据流值说的就是props,根据这一特点它还有一个作用:组件之间的通信。props本身是不可变的,但是有一种情形它貌似可变,即是将父组件的state作为子组件的props,当父组件的state改变,子组件的props也跟着改变,其实它仍旧遵循了这一定律:props是不可更改的。

props属性的特点:

1.每个组件对象都会有props(properties的简写)属性

2.组件标签的所有属性都保存在props中

3.内部读取某个属性值:this.props.propertyName

4.作用:通过标签属性从组件外 向组件内传递数据(只读 read only)

5.对props中的属性值进行类型限制和必要性限制

代码示例

使用函数组件:

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

//使用函数组件
function User(props){
    //在组件中获取props属性值
	return <div>{props.name}{props.age}</div>
}

//定义数据
const person ={
    name:'张三',
    age:20,
    sex:'男'
}

ReactDOM.render(
    <User {...person}></User>
, document.getElementById('root'));

使用类组件:

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

//使用class组件
class User extends React.Component{
    render(){
        return (
    <div>{this.props.name}--{this.props.age}</div>
        );
    }
}

//数据
const person ={
    name:'张三',
    age:20,
    sex:'男'
}

ReactDOM.render(
    <User {...person}></User>
, document.getElementById('root'));

3.1.2、state 属性

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

代码示例:

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

class Person extends React.Component{
	//构造方法
    constructor(){
        super();
        this.state = {
            name: 'tom'
        }
    }

    render(){
        //state属性是可修改的
        this.state.name = 'jack';
        return (
        <h1>{this.state.name}</h1>
        );
    }
}

ReactDOM.render(<Person />, document.getElementById('root'));

设置状态:setState

setState(object nextState[, function callback])

不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。

setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。

setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑

3.1.3、propsstate属性的区别

  • props中的数据都是外界传递过来的;
  • state中的数据都是组件私有的;(通过Ajax获取回来的数据,一般都是私有数据)
  • props中的数据都是只读的,不能重新赋值;
  • state中的数据,都是可读可写的;
  • 子组件只能通过props传递数据;

3.1.4、refs 属性

在React数据流中,父子组件唯一的交流方式是通过props属性;如果要修改子组件,通过修改父组件的属性,更新达到子组件props属性的值,重新渲染组件以达到视图的更新。但是,有些场景需要获取某一个真实的DOM元素来交互,比如文本框的聚焦、触发强制动画等

  • 给DOM元素添加ref属性
  • 给类组件添加ref属性

代码示例:

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

class Person extends React.Component{

    constructor(){
        super();
        this.handleClick = this.handleClick.bind(this);
    }

    //点击事件
    handleClick(){
        // 使用原生的 DOM API 获取焦点
        this.refs.myInput.focus();
    }

    render(){
        return (
            <div>
                <input type="text" ref="myInput" />
                <input
                    type="button"
                    value="点我输入框获取焦点"
                    onClick={this.handleClick}/>
            </div>
        );    
    }
}

ReactDOM.render(<Person />, document.getElementById('root'));

4、组件的生命周期

4.1、组件生命周期的三个阶段

  1. Mounting(加载阶段)
  2. Updating(更新阶段)
  3. Unmounting(卸载阶段)

4.2、旧的生命周期

图片描述

Mounting(加载阶段:涉及6个钩子函数)

constructor()

加载的时候调用一次,可以初始化state

getDefaultProps()

设置默认的props,也可以用dufaultProps设置组件的默认属性。

getInitialState()

初始化state,可以直接在constructor中定义this.state

componentWillMount()

组件加载时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state

render()

react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

componentDidMount()

组件渲染之后调用,只调用一次
Updating(更新阶段:涉及5个钩子函数)

componentWillReceivePorps(nextProps)

组件加载时不调用,组件接受新的props时调用

shouldComponentUpdate(nextProps, nextState)

组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)

componentWillUpdata(nextProps, nextState)

组件加载时不调用,只有在组件将要更新时才调用,此时可以修改state

render()

react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

componentDidUpdate()

组件加载时不调用,组件更新完成后调用
Unmounting(卸载阶段:涉及1个钩子函数)

componentWillUnmount()

组件渲染之后调用,只调用一次
组件的基本写法
import React, { Component } from 'react'

export default class OldReactComponent extends Component {
    constructor(props) {
        super(props)
        // getDefaultProps:接收初始props
        // getInitialState:初始化state
    }
    state = {

    }
    componentWillMount() { // 组件挂载前触发

    }
    render() {
        return (
            <h2>Old React.Component</h2>
        )
    }
    componentDidMount() { // 组件挂载后触发

    }
    componentWillReceivePorps(nextProps) { // 接收到新的props时触发

    }
    shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
        return true
    }
    componentWillUpdate(nextProps, nextState) { // 组件更新前触发

    }
    componentDidUpdate() { // 组件更新后触发

    }
    componentWillUnmount() { // 组件卸载时触发

    }
}

4.3、新的生命周期

Mounting(加载阶段:涉及4个钩子函数)

constructor()

加载的时候调用一次,可以初始化state

static getDerivedStateFromProps(props, state)

组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

render()

react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

componentDidMount()

组件渲染之后调用,只调用一次
Updating(更新阶段:涉及5个钩子函数)

static getDerivedStateFromProps(props, state)

组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

shouldComponentUpdate(nextProps, nextState)

组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)

render()

react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

getSnapshotBeforeUpdate(prevProps, prevState)

触发时间: update发生的时候,在render之后,在组件dom渲染之前;返回一个值,作为componentDidUpdate的第三个参数;配合componentDidUpdate, 可以覆盖componentWillUpdate的所有用法

componentDidUpdate()

组件加载时不调用,组件更新完成后调用
Unmounting(卸载阶段:涉及1个钩子函数)
组件渲染之后调用,只调用一次
Error Handling(错误处理)

componentDidCatch(error,info)

任何一处的javascript报错会触发
组件的基本写法
import React, { Component } from 'react'

export default class NewReactComponent extends Component {
    constructor(props) {
        super(props)
        // getDefaultProps:接收初始props
        // getInitialState:初始化state
    }
    state = {

    }
    static getDerivedStateFromProps(props, state) { // 组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state
        return state
    }
    componentDidCatch(error, info) { // 获取到javascript错误

    }
    render() {
        return (
            <h2>New React.Component</h2>
        )
    }
    componentDidMount() { // 挂载后
        
    }   
    shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
        return true
    }
    getSnapshotBeforeUpdate(prevProps, prevState) { // 组件更新前触发

    }
    componentDidUpdate() { // 组件更新后触发

    }
    componentWillUnmount() { // 组件卸载时触发

    }
}

4.4、总结

旧的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5C8PX7hH-1626080394699)(https://segmentfault.com/img/bVbhRvE?w=720&h=333)]
新的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVgSVuDI-1626080394706)(https://segmentfault.com/img/bVbhRvx?w=720&h=394)]

  1. React16新的生命周期弃用了componentWillMount、componentWillReceivePorps,componentWillUpdate
  2. 新增了getDerivedStateFromProps、getSnapshotBeforeUpdate来代替弃用的三个钩子函数(componentWillMount、componentWillReceivePorps,componentWillUpdate)
  3. React16并没有删除这三个钩子函数,但是不能和新增的钩子函数(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17将会删除componentWillMount、componentWillReceivePorps,componentWillUpdate
  4. 新增了对错误的处理(componentDidCatch)

5、父子组件传值

5.1.1、父组件传值子组件

在引用子组件的时候传递,相当于一个属性,例如:在子组件内通过porps.param获取到这个param的值。

父组件向子组件传值,通过props,将父组件的state传递给了子组件。

父组件代码片段:

constructor(props){
    super(props)
    this.state={
      message:"i am from parent"
    }
  }
  render(){
    return(
          <Child txt={this.state.message}/>
    )
  }
}

子组件代码片段:

render(){
    return(
          <p>{this.props.txt}</p>
    )
}

完整示例

创建父组件 index.js

import React from 'react';
import ReactDOM from 'react-dom';
import User from './User';//引入子组件

//定义数据
const person = {
    name: 'Tom',
    age:20
}

ReactDOM.render(
    //渲染子组件,并向子组件传递name,age属性
    <User name={person.name} age={person.age}></User>
    , document.getElementById('root'));

创建子组件 User.js

import React from 'react';

class User extends React.Component{
    render(){
        return (
            // 使用props属性接收父组件传递过来的参数
            <div>{this.props.name}{this.props.age}</div>
        );
    }
}

export default User;

在父组件中可以使用展开运算符 ... 传递对象

index.js文件

ReactDOM.render(
    //渲染子组件,并向子组件传递name,age属性
    <User {...person}></User>
    , document.getElementById('root'));

User.js文件

render(){
   return (
       // 使用props属性接收父组件传递过来的参数
       <div>{this.props.name}{this.props.age}</div>
   );
}

5.1.2、子组件传值父组件

子组件通过调用父组件传递到子组件的方法向父组件传递消息的。

完整案例

子组件 Son.js 文件代码示例:

import React from 'react';

class Son extends React.Component {
    //构造方法
    constructor(){
        super();
        this.state = {
            inputValue:''
        }
    }
    //按钮点击事件
    handleClick(){
        //通过props属性获取父组件的getdata方法,并将this.state值传递过去
        this.props.getdata(this.state.inputValue);
    }
    //输入框事件,用于为this.state赋值
    handleChange(e){
        this.setState({
            inputValue: e.target.value
        });
    }

    render(){
        return (
            <React.Fragment>
                <input onChange={this.handleChange.bind(this)}></input>
                <button onClick={this.handleClick.bind(this)}>点击获取数据</button>
            </React.Fragment>
        );
    }

}

export default Son;

父组件 Parent.js 文件代码示例:

import React from 'react';
import Son from './Son';

class Parent extends React.Component {
    //构造方法
    constructor(){
        super();
        this.state = {
            mess: '' //初始化mess属性
        }
    }
    //用于接收子组件的传值方法,参数为子组件传递过来的值
    getDatas(msg){
        //把子组件传递过来的值赋给this.state中的属性
        this.setState({
            mess: msg
        });
    }

    render(){
        return (
            <React.Fragment>
                {/* 渲染子组件,设置子组件访问的方法,
                getdata属性名为子组件中调用的父组件方法名 */}
                <Son getdata={this.getDatas.bind(this)}></Son>
                <div>展示数据:{this.state.mess}</div>
            </React.Fragment>
        );
    }

}

export default Parent;

入口文件 index.js示例代码:

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

ReactDOM.render(<Parent></Parent>, document.getElementById('root'));

5.1.3、兄弟组件传值

兄弟组件之间的传值,是通过父组件做的中转 ,流程为:

组件A传值 --> 父组件传值 --> 组件B

代码示例:

创建 Acls.js 组件,用于提供数据

import React from 'react';

class Acls extends React.Component {
	//按钮点击事件,向父组件Pcls.js传值
    handleClick(){
        this.props.data("hello...React...");
    }

    render(){
        return (
            <button onClick={this.handleClick.bind(this)}>Acls组件中获取数据</button>
        );
    }
}

export default Acls;

创建父组件 Pcls.js 用于中转数据

import React from 'react';
import Acls from './Acls';
import Bcls from './Bcls';

class Pcls extends React.Component {
	//构造函数
    constructor(){
        super();
        this.state = {
            mess: ''
        }
    }
	//向子组件Acls.js提供的传值方法,参数为获取的子组件传过来的值
    getDatas(data){
        this.setState({
            mess: data
        });
    }

    render(){
        return (
            <React.Fragment>
                Pcls组件中显示按钮并传值:
                <Acls data={this.getDatas.bind(this)}></Acls>
                <Bcls mess={this.state.mess}></Bcls>
            </React.Fragment>
        );
    }
}

export default Pcls;

创建子组件 Bcls.js 用于展示从 Acls.js 组件中生成的数据

import React from 'react';

class Bcls extends React.Component {

    render(){
        return (
            <div>在Bcls组件中展示数据:{this.props.mess}</div>
        );
    }
}

export default Bcls;

6、TodoList 案例

6.1.创建 Todolist.js 组件

import React from 'react';

class TodoList extends React.Component{

  //构造方法
  constructor(props){
    super(props);
    this.state = {
      list: [
        'learn html',
        'learn css',
        'learn react',
        'learn vue'
      ]
    }
  }

  //按钮点击事件方法
  handleBtnClick(){
    this.setState({
      list: [...this.state.list,'hello world'] //...为展开运算符,将this.state.list内容放到当前的list中
    });
  }

  render(){
    return (
      <div>
        <div>
          <input type="text"></input>
          <button onClick={this.handleBtnClick.bind(this)}>添加</button>
        </div>
        <ul>
          {/* key属性为唯一值,没有该属性,浏览器会报警告信息,在做添加操作时会出bug */}
          {this.state.list.map((item,index) => <li key={index}>{item}</li>)}
        </ul>

      </div>
    );
  }
}

export default TodoList;

引用组件

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

ReactDOM.render(<TodoList />, document.getElementById('root'));

6.2.添加列表项功能

代码示例:

import React from 'react';

class TodoList extends React.Component{

  //构造方法
  constructor(props){
    super(props);
    this.state = {
      list: [],  //展示列表
      inputValue:'' //记录输入框的值
    }
  }

  按钮点击事件方法
  handleBtnClick(){
    this.setState({
      list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
      inputValue: '' //点击添加按钮,清空输入框
    });
  }

  //输入框输入事件方法
  handleInputChange(e){
    this.setState({
      inputValue: e.target.value
    });
  }

  render(){
    return (
      <div>
        <div>
          <input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
          <button onClick={this.handleBtnClick.bind(this)}>添加</button>
        </div>
        <ul>
          {this.state.list.map((item,index) => <li key={index}>{item}</li>)}
        </ul>

      </div>
    );
  }
}

export default TodoList;

6.3.删除列表元素

删除列表元素代码:

import React from 'react';

class TodoList extends React.Component{

  //构造方法
  constructor(props){
    super(props);
    this.state = {
      list: [],  //展示列表
      inputValue:'' //记录输入框的值
    }
  }

  按钮点击事件方法
  handleBtnClick(){
    this.setState({
      list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
      inputValue: '' //点击添加按钮,清空输入框
    });
  }

  //输入框输入事件方法
  handleInputChange(e){
    this.setState({
      inputValue: e.target.value
    });
  }

  //点击展示列表事件方法,用于删除展示元素
  handleItemClick(index){
    const list = [...this.state.list];
    list.splice(index,1);
    this.setState({
      list: list
    });
  }

  render(){
    return (
      <div>
        <div>
          <input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
          <button onClick={this.handleBtnClick.bind(this)}>添加</button>
        </div>
        <ul>
          {this.state.list.map((item,index) => {
            return <li onClick={this.handleItemClick.bind(this,index)} key={index}>
                    {item}
                  </li>
          })}
        </ul>

      </div>
    );
  }
}

export default TodoList;

6.4.使用组件化实现删除功能

创建子组件 TodoItem.js

import React from 'react';

class TodoItem extends React.Component{

    //点击元素删除的方法
    //子组件如果想和父组件通信,子组件要调用父组件传递过来的方法
    handleDelete(){
        //调用父组件的方法,向父组件传值
        this.props.delete(this.props.index);
    }

    render(){
        return (
            <div onClick={this.handleDelete.bind(this)}>
                {/* 子组件通过props接收父组件传递过来的参数 */}
                {this.props.content}
            </div>
        );
    }
}

export default TodoItem;

在父组件 TodoList.js 中调用子组件

import React from 'react';
import TodoItem from './TodoItem';

class TodoList extends React.Component{

  //构造方法
  constructor(props){
    super(props);
    this.state = {
      list: [],  //展示列表
      inputValue:'' //记录输入框的值
    }
  }

  按钮点击事件方法
  handleBtnClick(){
    this.setState({
      list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
      inputValue: '' //点击添加按钮,清空输入框
    });
  }

  //输入框输入事件方法
  handleInputChange(e){
    this.setState({
      inputValue: e.target.value
    });
  }

  //点击元素删除的方法,该方法是用来接收子组件的传值
  handelDeleteItem(index){
    const list = [...this.state.list];
    list.splice(index,1);
    this.setState({
      list: list
    });
  }

  render(){
    return (
      <div>
        <div>
          <input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
          <button onClick={this.handleBtnClick.bind(this)}>添加</button>
        </div>
        <ul>
          {this.state.list.map((item,index) => {
            //父组件通过属性的形式向子组件传递参数
            return <TodoItem 
            			delete={this.handelDeleteItem.bind(this)} 
            			key={index} 
            			content={item} 
            			index={index}>
            		</TodoItem>
          })}
        </ul>

      </div>
    );
  }
}

export default TodoList;

6.5.代码优化

TodoItem.js 代码优化:

import React from 'react';

class TodoItem extends React.Component{

    //构造方法
    constructor(props){
        super(props);
        this.handleDelete = this.handleDelete.bind(this);
    }

    //点击元素删除的方法
    //子组件如果想和父组件通信,子组件要调用父组件传递过来的方法
    handleDelete(){
        const { handelDelete , index } = this.props;
        //调用父组件的方法,向父组件传值
        handelDelete(index);
    }

    render(){
        const { content } = this.props;
        return (
            <div onClick={this.handleDelete}>{content}</div>
        );
    }
}

export default TodoItem;

TodoList.js 代码优化:

import React from 'react';
import TodoItem from './TodoItem';

class TodoList extends React.Component{

  //构造方法
  constructor(props){
    super(props);
    this.state = {
      list: [],  //展示列表
      inputValue:'' //记录输入框的值
    }
    
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleBtnClick = this.handleBtnClick.bind(this);
    this.handelDeleteItem = this.handelDeleteItem.bind(this);
  }

  按钮点击事件方法
  handleBtnClick(){
    this.setState({
      list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
      inputValue: '' //点击添加按钮,清空输入框
    });
  }

  //输入框输入事件方法
  handleInputChange(e){
    this.setState({
      inputValue: e.target.value
    });
  }

  //点击元素删除的方法,该方法是用来接收子组件的传值
  handelDeleteItem(index){
    const list = [...this.state.list];
    list.splice(index,1);
    this.setState({
      list: list
    });
  }

  //遍历方法
  getTodoItems(){
    return (
      this.state.list.map((item,index) => {
        //父组件通过属性的形式向子组件传递参数
        return <TodoItem 
                  handelDelete={this.handelDeleteItem} 
                  key={index} 
                  content={item} 
                  index={index} />
      })
    );
  }

  render(){
    return (
      <div>
        <div>
          <input value={this.state.inputValue} onChange={this.handleInputChange}></input>
          <button onClick={this.handleBtnClick}>添加</button>
        </div>
        <ul>
          {this.getTodoItems()}
        </ul>

      </div>
    );
  }
}

export default TodoList;

6.6.使用CSS样式修饰

方法一:使用style属性

代码示例:

<button style={{background:'blue',color:'#fff'}} onClick={this.handleBtnClick}>
	添加
</button>

方法二:使用className属性

创建 style.css 文件

.red-btn{
    background-color: red;
    color: #ffffff;
}

index.js 入口文件引入 css

import './style.css';

在组件中使用 className 属性

 <button className='red-btn' onClick={this.handleBtnClick}>添加</button>

6.7.JSX代码优化

render() 方法 returnJSX 代码需要在最外层使用一个标签包裹,如果不想在页面中显示最外层的这个标签,可以使用 <React.Fragment> 标签替代,代码示例:

render(){
    return (
      <React.Fragment>
        <div>
          <input />
          <button>添加</button>
        </div>
        <ul>
          {this.getTodoItems()}
        </ul>
      </React.Fragment>
    );
  }

也可以在引入组件时直接引入类,在使用时就不需要用 React 调用

引入

import React,{Component,Fragment} from 'react';
class TodoList extends Component{
render(){
    return (
      <Fragment>
        <div>
          <input />
          <button>添加</button>
        </div>
        <ul>
          {this.getTodoItems()}
        </ul>
      </Fragment>
    );
  }
}
export default TodoList;

React路由

1、React路由介绍

1.1、单页面应用

单页面得特点:只需要加载一次主页面,通过局部刷新,就可以实现跳转或者切换页面

优点:加载速度快,用户体验比较好

缺点:

  • 第一次加载比传统要慢一点
  • 不利seo
  • 页面相对复杂
  • 返回键

1.2、安装react-router-dom

在项目命令行中,执行

cnpm install react-router-dom -S

下载到生产环境的依赖中。

在组件中通过对象的解构方式去获取到react-router-dom内置组件,在组件中,按需引入内置组件,在页面中进行使用:

  • HashRouter表示一个路由的根容器,将来所有的路由相关的东西,都要包裹在HashRouter里面,而且一个网站中,只需要使用一次HashRouter就好了;【HashRouter:带有 #/ ; BrowserRouterb:不带】
  • Route表示一个路由规则,在Route上,有两个比较重要的属性,path,component
  • Link表示一个路由的链接,属性to
import {HashRouter,Route,Link} from 'react-router-dom'

代码示例:

render(){
        return (
            <HashRouter>
                <div>
                    <h1>这是网站的根目录</h1>
                    <hr />
                    <Link to="/home">首页</Link>&nbsp;&nbsp;
                    <Link to="/movie/">电影</Link>&nbsp;&nbsp;
                    <Link to="/about">关于</Link>
                    <hr />
                    
                    {/* 占位符 Route ,
                    通过 / 访问新的页面,
                    Route类似if 判断,只要满足其中一个条件就会进入到这个页面
                    */}
                    <Route path="/home" component={Home} ></Route><hr/>
                    <Route path="/movie" component={Movie} exact></Route><hr/>
                    <Route path="/about" component={About}></Route><hr/>
                    
                </div>
            </HashRouter>
        );
    }
render(){
        return (
            <HashRouter>
                <div>
                    <h1>这是网站的根目录</h1>
                    <hr />
                    <Link to="/home">首页</Link>&nbsp;&nbsp;
                    <Link to="/movie/">电影</Link>&nbsp;&nbsp;
                    <Link to="/about">关于</Link>
                    <hr />
                    
                    {/* 占位符 Route ,
                    通过 / 访问新的页面,
                    Route类似if 判断,只要满足其中一个条件就会进入到这个页面
                    外边用Switch包裹,类似于if...else判断,
                    注意在import中引入 Switch
                    import {HashRouter,Route,Link,Switch} from 'react-router-dom'
                    中间只能有Route ,不能带有其他内容标签,
                    如果只有 / ,没有后面页面信息内容,自动判定已识别到新的页面,进入空页面,不会在跳转到其他后面的页面,前面如果有正常带页面信息的可正常跳转
                    */}
                    <Switch>
	                    <Route path="/home" component={Home} ></Route>
	                    <Route path="/movie" component={Movie} exact></Route>
	                    <Route path="/about" component={About}></Route>
                    </Switch>
                </div>
            </HashRouter>
        );
    }

当使用HashRouter把APP根组件的元素包裹起来之后,网站就已经启用路由了,在一个HashRouter中,只能有唯一的一个根元素。 在一个网站中,只需要使用唯一的一次<HashRouter></HashRouter>就行了。

Route创建的标签,就是路由规则,其中path表示要匹配的路由,component表示要展示的组件。Route具有两种身份:1.它是一个路由匹配规则;2.它是一个占位符,表示将来匹配到的组件都放到这个位置

需要注意的地方

  • Route 组件path地址是以/开头 ,配置component属性,是显示的组件,这个属性不可以大写
  • Route组件可以单双标签使用,单标签需要/结尾,双标签不可以在中间写入别的东西
  • Link to属性的地址也是/开头,Link在页面渲染的是a标签

2、路由传值

2.1、React-router-dom路由传值

通过配置路由的地址,在Link跳转时

  • Route path路径后面 /:id (key)

  • Link to 路径后面 /top/10 (value)

    接收传值:

  • class类组件,this.props.match.params.属性名

  • 函数组件:形参.match.params.属性名

代码示例:

render(){
        return (
            <HashRouter>
                <div>
                    <h1>这是网站的根目录</h1>
                    <hr />
                    <Link to="/movie/top/10">电影</Link>&nbsp;&nbsp;
                    <hr />
                    <Route path="/movie/:type/:id" component={Movie} exact></Route>
                </div>
            </HashRouter>
        );
    }

在Route内置组件中,配置path地址:

<Route path="/movie/:type/:id" component={Movie} exact></Route>

在Link内置组件中,配置to属性,进行跳转:

<Link to="/movie/top/10">电影</Link>

类组件中通过生命周期进行接收,this.props携带路由传递过来的数据:

render(){
        console.log(this);
        return (
            <div>
                电影--{this.props.match.params.type}--{this.props.match.params.id}
            </div>
        );
    }

代码优化后:

class Movie extends React.Component{

    constructor(props){
        super();
        this.state = {
            routeParams:props.match.params
        }
    }

    render(){
        console.log(this);
        return (
            <div>
                电影--{this.state.routeParams.type}--{this.state.routeParams.id}
            </div>
        );
    }
}

函数组件中通过形参接收传递过来的值,props形参,函数组件作为路由组件,props就是传递过来的对象,里面携带着路由传递过来的数据

import React from 'react'

export default function home(props) {
	return (
		<div>
			{props.match.params.id}
		</div>
	)
}
2.1.1一二级路由
{/*App.jsx文件 一级 */}
import React, { Component } from 'react'
import {Route,Switch} from 'react-router-dom'
import Main from './Main'


export default class App extends Component {
    render() {
        return (
            <div>
                
                {/* 一级路由 */}
                <Switch>
                    <Route path='/' component={Main}></Route>
                </Switch>
            </div>
        )
    }
}

{/*Main.jsx文件 二级*/}
import React, { Component } from 'react'
import {Redirect,Route,Link,Switch} from 'react-router-dom'
import About from './About'
import Classify from './Classify'
import Home from './Home'
import News from './News'

export default class Main extends Component {
    render() {
        return (
            <div>
                 <Link to='/home'>首页</Link> &nbsp;
                <Link to='/news'>新闻</Link> &nbsp;
                <Link to='/classify'>分类</Link> &nbsp;
                <Link to='/about'>关于</Link>

                <hr />
                
                {/* 占位符 */}

                {/* 指定跳转页面 */}
                <Redirect to='/home'></Redirect>

                <Switch>
                    <Route path="/home" component={Home} ></Route>
                    <Route path="/classify" component={Classify}></Route>
                    <Route path="/about" component={About}></Route>
                    <Route path="/news" component={News}></Route>
                </Switch>
            </div>
        )
    }
}

2.2、嵌套路由

嵌套路由:在路由组件中,使用Link, Route,配置子路由,实现跳转,切换;

下面为一级路由,在一级路由Home为路由组件

<Route path="/home" component={ Home }></Route>

在Home组件中继续使用Link,Route进行路由的嵌套,需要注意的就是路由地址,前部分为一级路由地址,后面接一个二级路由相应的路径

render() {
    return (
        <div>
            <ul>
            	<li><Link to="/home/a">推荐</Link></li>
            	<li><Link to="/home/b">新时代</Link></li>
                <li><Link to="/home/c">动漫</Link></li>
            </ul>
            <Route path="/home/a" component={ A }></Route>
            <Route path="/home/b" component={ B }></Route>
            <Route path="/home/c" component={ C }></Route>
        </div>
    )
}

2.3、JS实现路由跳转

引入BrowserRouter模块

import {BrowserRouter,HashRouter,Route,Link} from 'react-router-dom'

使用BrowserRouter作为根容器

//跳转点击事件
jump(){
    window.location.href = "/news"
}

render(){
    return (
        <BrowserRouter>
            <div>
                <h1>这是网站的根目录</h1>
                <hr />
                <button onClick={()=>{this.jump()}}>新闻</button>
                <hr />
                <Route path="/news" component={News}></Route>
            </div>
        </BrowserRouter>
    );
}

在render方法中,写一个按钮,按钮名称为js跳转路由,定义一个onClick方法,箭头函数解决this指向问题,与render同级,定义一个jump方法,在jump方法中执行一句代码进行路由跳转,使用window.location.href = “路由的地址”实现路由跳转。

3、react-router-dom内置组件

首先按需引入,使用什么内置组件,就需要引入

import { BrowserRouter, Link, Route,Redirect,NavLink,Switch } from 'react-router-dom'

3.1、在组件中使用NavLink

NavLink 带有选中activeClassName ,如果路由处于激活状态,显示激活class样式。

在我们之前案例的基础上,找到Link组件,我们已经学到Link组件的作用,可以进行路由的跳转,通过to属性,跳转相应的path地址。

<ul>
    <li>
    	<Link to="/home">首页</Link>
    </li>
    <li>
    	<Link to="/video">好看视频</Link>
    </li>
</ul>

将组件中的Link全部换成NavLink组件

<li>
	<NavLink activeClassName="red" to="/home">首页</NavLink>
</li>
<li>
	<NavLink activeClassName="red" to="/video">好看视频</NavLink>
</li>

我们会发现,之前可以正常进行路由跳转,换成NavLink,还依然可以正常跳转,证明组件得跳转使用NavLink也可以实现,那么问题来了,NavLink有什么用,为什么封装了NavLink,将每一个NavLink加入一个activeClassName属性绑定一个class类样式,这时在触发NavLink时,会触发相应得样式,这样有一个切换效果。

3.2、在组件中使用Redirect内置组件

Redirect 重定向 具备to属性,可以直接跳转到指定路由。

在render方法中,使用内置组件,Redirect内置组件使用to属性,当执行到内置标签时,会进行to跳转路由,to后面接的地址是什么,就可以匹配到相应得路由组件。

<Redirect to="/home/c"></Redirect>

3.3、在组件使用Switch内置组件

Switch 具有排他性

在组件中使用Switch标签包裹所有得Route,这时,Route相当于每一个path都是精准的匹配,展示相应的组件,最后一个Route是一个不具备地址的路由路径,如果当Link指向一个不存在的地址时,没有精准的匹配到地址,那么会显示到C404路由,使用Switch只会显示一个组件,匹配到为止。

<Switch>
    <Route path="/home" component={ Home }></Route>
    <Route path="/video" component={ Home1 }></Route>
    <Route path="/book" component={ Home2 }></Route>
    <Route path="/renwu" component={ Home3 }></Route>
    <Route path="/user" component={ Home4 }></Route>
    <Route component={ C404 }></Route>
</Switch>

Redux

1、什么是Redux

Redux 是React生态中重要的组成部分。很多人都说,简单的应用可以不用此工具。但是我个人认为,中小型应用使用的话,可以使文件结构更加规范,代码可读性更强。因为React提出将展示组件与容器组件分离的思想,所以降低了React 与Redux之间的耦合度。

img

Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。 见下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XDmWiwUE-1626080394748)(https://segmentfault.com/img/bVWi0h?w=687&h=330)]

在Redux中,所有的数据(比如state)被保存在一个被称为store的容器中 → 在一个应用程序中只能有一个。store本质上是一个状态树,保存了所有对象的状态。任何UI组件都可以直接从store访问特定对象的状态。要通过本地或远程组件更改状态,需要分发一个action分发在这里意味着将可执行信息发送到store。当一个store接收到一个action,它将把这个action代理给相关的reducerreducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。

Redux的运行原理

img

store中重要的方法

img

2、配置Redux

配置Redux开发环境的最快方法是使用create-react-app工具。在开始之前,确保已经安装并更新了nodejsnpm

npm install redux

创建store目录,在store目录下新建index.js文件,键入以下内容:

import {createStore} from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

在store目录下创建reducer.js文件,键入以下内容:

const defaultState = {
    inputValue:'',
    list:[]
}

//reducer可以接收state,但是不能修改state
export default (state = defaultState,action) => {
    return state;
}

在组件中就可以使用store的数据

import React,{Component} from 'react';
import store from './store/index';

class TodoList extends Component{

    constructor(props){
        super(props);
        this.state = store.getState();
        this.handleStoreChange = this.handleStoreChange.bind(this);
        store.subscribe(this.handleStoreChange);
    }
    
    handleStoreChange(){
        this.setState(store.getState());
    }

    render(){
        return (
            <div>
                <input type='text' value={this.state.inputValue}/>
                <button onClick={this.handleClick}>提交</button>
                <ul>
                    {this.state.list.map((item,index)=>{
                        return (
                            <li key={index}>{item}</li>
                        );
                    })}
                </ul>
            </div>
        );
    }
}

export default TodoList;

3、TodoList案例

TodoList.js

import React,{Component} from 'react';
import store from './store/index';
import {DELETE_TODO_ITEM,CHANGE_INPUT_VALUE,ADD_TODO_ITEM} from './store/actionType'

class TodoList extends Component{

    constructor(props){
        super(props);
        this.state = store.getState();
        this.handleChange = this.handleChange.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
        store.subscribe(this.handleStoreChange);

    }

    handleChange(e){
        const action = {
            type:CHANGE_INPUT_VALUE,
            value:e.target.value
        }
        store.dispatch(action);
    }

    handleStoreChange(){
        this.setState(store.getState());
    }

    handleClick(){
        const action = {
            type:ADD_TODO_ITEM
        }
        store.dispatch(action);
    }

    handleClickItem(index){
        const action = {
            type:DELETE_TODO_ITEM,
            index:index
        }
        store.dispatch(action);
    }

    render(){
        return (
            <div>
                <input type='text' value={this.state.inputValue} onChange={this.handleChange} />
                <button onClick={this.handleClick}>提交</button>
                <ul>
                    {this.state.list.map((item,index)=>{
                        return (
                            <li key={index} onClick={this.handleClickItem.bind(this,index)}>{item}</li>
                        );
                    })}
                </ul>
            </div>
        );
    }
}

export default TodoList;

store/index.js

import {createStore} from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

store/reducer.js

import {DELETE_TODO_ITEM,CHANGE_INPUT_VALUE,ADD_TODO_ITEM} from './actionType'

const defaultState = {
    inputValue:'',
    list:[]
}

//reducer可以接收state,但是不能修改state
export default (state = defaultState,action) => {
    console.log(state,action);
    if(action.type === CHANGE_INPUT_VALUE){
        const newState = state;
        newState.inputValue = action.value;
        return newState;
    }
    if(action.type === ADD_TODO_ITEM){
        const newState = state;
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState;
    }
    if(action.type === DELETE_TODO_ITEM){
        const newState = state;
        newState.list.splice(action.index,1);
        return newState;
    }
    
    return state;
}

store/actionType.js

export const CHANGE_INPUT_VALUE = 'change_input_value'
export const ADD_TODO_ITEM = 'add_todo_item'
export const DELETE_TODO_ITEM = 'delete_todo_item'

**参考文章: https://segmentfault.com/a/1190000011474522 **

核心API:

  • createStore 创建store
  • store.dispatch 派发action,把数据上传到Store中
  • store.getState 获取store中所有的数据内容,但是Store中的数据发生变化时,组件不会知道
  • store.subscribe 监听Store中的数据变化,Store中的数据一旦发生变化,该方法就会被执行

4、Redux-thunk中间件

第一步 安装redux-thunk中间件

npm install redux-thunk

第二步 在store中引入thunk组件

import {createStore,applyMiddleware } from 'redux';
import Reducer from './Reducer';
import thunk from 'redux-thunk';

const store = createStore(Reducer,applyMiddleware(thunk));

export default store;

第三步 封装异步请求方法

在TodoList.js组件中,封装异步获取请求的方法:

import React, { Component } from 'react'
import Store from './Store'
import axios from 'axios'

export class TodoList extends Component {

    constructor(props){
        super(props);
        this.state = Store.getState();
        this.handleStoreChange = this.handleStoreChange.bind(this);
        Store.subscribe(this.handleStoreChange);
    }

    //在生命周期函数中调用异步方法
    componentDidMount(){
        Store.dispatch(this.getTodoListDatas());
    }

    //异步获取请求的方法
    getTodoListDatas(){
        return (dispatch)=>{
            axios.get("/data.json")
            .then(resp => {
                const action = {
                    type:'axios_getdata',
                    data:resp.data
                }
                dispatch(action)
            })
        }
    }

    handleStoreChange(){
        this.setState(Store.getState());
    }

    render() {
        return (
            <div>
                <input type='text' value={this.state.inputValue}></input>
                <button>添加</button>
                <hr></hr>
                <ul>
                    {this.state.list.map((item,index)=>{
                        return (
                            <li key={index}>{item}</li>
                        );
                    })}
                </ul>
            </div>
        )
    }
}

export default TodoList


第四步 在reducer中接收action信息

const defaultState = {
    inputValue:'',
    list:[]
}

export default (state = defaultState,action) => {
    if(action.type === 'axios_getdata'){
        const newState = state;
        newState.list = action.data;
        return newState;
    }

    return state
}

5、Redux中间件执行原理

redux-thunk这个中间件可以使我们把这样的异步请求或者说复杂的逻辑可以放到action里面去处理,redux-thunk使redux的一个中间件,为什么叫做中间件

我们说中间件,那么肯定是谁和谁的中间,那么redux的中间件指的是谁和谁的中间呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VkAE87P-1626080394760)(C:\Users\mac\AppData\Roaming\Typora\typora-user-images\1581401374679.png)]

如图。view在redux中会派发一个action,action通过store的dispatch方法派发给store,store接收到action,连同之前到state,一起传给reducer,reducer返回一个新到数据给store,store去改变自己到state。这是redux的一个标准流程,那么我们说redux的中间件指的是谁和谁的之间,大家一定要记住,**redux的中间件指的是action和store之间。之前我们说action只能是一个对象,所以action是一个对象直接派发给了store。**但是现在,当我们使用了redux-thunk之后,action可以是函数了。

为什么可以是函数呢,看这张图。action通过dispatch方法被传递给store,那么action和store之间是谁,是不是就是dispatch这个方法,**实际上我们指的中间件指的是什么呢,就是对dispatch方法的一个封装,或者说是dispatch方法的一个升级,最原始的dispatch方法,他接收到一个对象之后,会把这个对象传递给store,这就是view中间件的一个情况。**当我们对dispath做了一个升级之后,比如说我们使用了redux-thunk这个中间件,对dispath做了一个升级,这个时候当你调用dispatch方法,给dispatch传递的参数是一个对象的话,那么这个dispatch就会把这个对象直接传给store。跟之前写一个对象,调用dispatch传给store没有任何的区别。但是这个时候假设传给dispatch方法是一个函数的话,这个时候dispatch方法已经升级了。他知道如果你传递过来是一个函数的话,他就不会把这个函数直接传递给store。他会怎么办呢?

他会让你这个函数先执行,然后执行完了之后,需要调用store的时候,这个函数再去调用store。所以dispatch做了一个事情,他会根据参数的不同,执行不同的事情,如果你参数是对象,那我直接传给store。如果你参数是函数,那就把这个函数执行结束。所以讲到这大家应该知道

**redux中间件,他的原理是非常简单的,他就是对store对dispatch方法做一个升级,之前这个dispatch方法只能接收一个对象,现在升级之后,就可以接收对象,也可以接收函数了。**当然这里用什么对他进行一个升级呢?用redux-thunk对他进行了升级。当然中间件不只redux-thunk这一个,实际上redux中间件非常多,比如说我们说的redux-log,可以记录action每次派发的日志,那他怎么记录呢?其实也很简单,每次调用 action的时候,都会通过dispatch把这个action传递给store,那么我可以对dispatch做一个升级,dispatch不仅仅把action传递给store,而且在每次传递之前呢,还通过console.log,把这个action打印出来。这样就写了一个redux-log的中间件。他可以在每次派发action的时候,把这个action打印在控制台里面。

最近用的比较火的redux中间件,除了redux-thunk,redux-log这样的东西,还有一个中间件,叫做redux-saga。他的应用范围也非常广,redux-saga也是解决redux中间异步问题的中间件。不同于redux-thunk。redux-thunk是把异步操作放在action里面操作。而redux-saga采用的设计思想是,单独的把一个异步逻辑拆分出来,放在一个异步文件里面管理,基本上掌握了redux-thunk和redux-saga这两者的设计思路之后呢,再去做redux里面的异步逻辑,或者说复杂的逻辑,如何去拆分,就比较明白了。

6、react-redux

第一步 安装react-redux

npm install react-redux

第二步 创建store和reducer

store.js文件

import {createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

reducer.js文件

const defaultState = {
    inputValue:'hello',
    list:['a','b','c']
}
export default (state = defaultState,action) => {
    return state
}

第三步 在index.js入口文件引入Provider组件

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './reactredux/TodoList'
import {Provider} from 'react-redux'
import store from './reactredux/store'

const APP = (
    <Provider store={store}>
        <TodoList></TodoList>
    </Provider>
);

ReactDOM.render(APP , document.getElementById('root'));

第四步 在TodoList.js组件中引入connect组件

import React, { Component } from 'react'
import {connect} from 'react-redux'

export class TodoList extends Component {

    render() {
        return (
            <div>
                <input type='text' value={this.props.inputValue} onChange={this.props.changeInputValue}></input>
                <button onClick={this.props.addClick.bind(this)}>添加</button>
                <hr></hr>
                <ul>
                    {this.props.list.map((item,index)=>{
                        return (
                            <li key={index}>{item}</li>
                        );
                    })}
                </ul>
            </div>
        )
    }
}

const mapStateToProps = (state) =>{
    return {
        inputValue: state.inputValue,
        list:state.list
    }
}

const mapDispatchToProps = (dispatch) =>{
    return {
        changeInputValue(e){
            const action = {
                type:"change_inputvalue",
                inputValue:e.target.value
            }
            dispatch(action);
        },
        addClick(){
            const action = {
                type:"add_list",
                value:this.props.inputValue
            }
            dispatch(action);
        }
    }
}

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


第五步 在reducer中接收action的值

const defaultState = {
    inputValue:'hello',
    list:['a','b','c']
}
export default (state = defaultState,action) => {
    if(action.type === 'change_inputvalue'){
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.inputValue;
        return newState;
    }

    if(action.type === 'add_list'){
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(action.value);
        newState.inputValue = "";
        return newState;
    }
    
    return state
}

React-UI组件库

1、常用的React UI组件

1.1、React Material-UI介绍

React 组件用于更快速、更简便的 web 开发。你也可以建立你自己的设计系统,或者从 Material Design 开始。

Material-UI 是一个使用 MIT 许可证的开源项目。 可能完全因为这些超棒的 backers,这个项目不断的在开发

1.2、React-Bootstrap介绍

React-Bootstrap是可重用的前端组件库。 样式组件依赖于bootstrap。与 Twitter Bootstrap 一致外观与感受,但通过 Facebook 的 React.js 框架获得更清爽的代码

1.3、ant-design介绍

Ant Design 蚂蚁框架 (蚂蚁设计)

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品

特点:开箱即用的高质量 React 组件。

2、ant-design安装以及按需使用

2.1、ant-design安装

  1. 安装依赖 cnpm install antd -S
  2. 按需引入 import { Button } from ‘antd’,可以使用Button组件
  3. 引入样式 import ‘antd/dist/antd.css’
  4. 在组件render方法中,使用Button
<Button size="small" disabled href="#123" onClick={()=>{this.click()}} type="danger">button</Button>

根据api加入相应的功能,比如点击事件,size大小,href当成a链接使用,通过type类型调节颜色。

2.2、Antd-design 使用图标icon

import { Button,Icon } from 'antd'
<Icon spin twoToneColor="blue" style={{fontSize:"30px",color:"red"}} type="aliwangwang" />

2.3、Tag标签的引入

第一步引入组件样式,以及解构Tag标签

import { Tag } from 'antd';
import 'antd/dist/antd.css'

通过使用的API来认识tag标签的使用方式。

2.4、自动生成不同颜色标签案例

通过api写一个小案例,定义6种颜色,在页面上有一个开始按钮,点击开始按钮时,页面随机生成Tag标签,标签的颜色也是对应的Tag,生成在constructor中定义一个状态,里面写几个随机颜色,定义一个空数组colorarr。

<Button onClick={()=>{this.click()}}>开始</Button>
{
    this.state.colorarr.map((item)=>{
        return (
            <Tag color={item}>{item}</Tag>
        )
    })
}

当触发onClick方法时触发click方法

click() {
    // 创建一个定时器
    this.time = setInterval(()=>{
        // 随机生成一个数字 0-5
        let count = Math.round(Math.random()*5)
        console.log(count)
        // 在数组中取到颜色,通过生成的数字
        let str = this.state.color[count]
        // 将取到的颜色放入colorarr 中
        this.state.colorarr.push(str)
        this.setState({colorarr:this.state.colorarr})
    },100)
}

2.5、Rate评分的基本用法

按需引入antd中的Rate组件,在render方法中,jsx渲染Rate组件

import { Rate } from ‘antd’;

ReactDOM.render(, mountNode);

State状态中定义一个value值,默认为0

constructor() {
    super()
    this.state = {
    	value:0
    }
}

在render方法中,定义一个循环加星的按钮,加一个loop方法,根据Rate评分组件api,加入自动获取交点autoFocus onKeyDown键盘事件 星星的总数,鼠标悬停触发事件,以及当前的星数

render() {
    return (
        <div>
            <h3>rate</h3>
            <button onClick={()=>{this.loop()}}>循环加星</button>
            <Rate autoFocus onKeyDown={(e)=>{this.kd(e)}} count="20" onHoverChange={(e)=>{this.change(e)}} value={this.state.value} allowHalf></Rate>
        </div>
    )
}

当鼠标悬停时,触发事件,页面的星数随着悬停在几颗星,就为几颗星

change(e) {
    console.log(e)
    this.setState({value:e})
}

当点击开始按钮时,触发loop事件,将组件加入一个周期性定时器,将星数每0.2秒中自动加一颗星,当星数为20时,星数变为0,一直循环加星

loop() {
    this.time = setInterval(()=>{
        if(this.state.value == 20){
        	this.state.value = 0
        }
        this.state.value ++
        this.setState({value:this.state.value})
    },200)
}

当触发kd方法时,为鼠标按下事件,鼠标按右键时,触发键盘码39,按下一次星数长半颗星,按下左键时,触发键盘码37,每一次按键减少半颗星

kd(e) {
    if(e.keyCode == 39){
    	this.state.value +=0.5
    }
    if(e.keyCode == 37){
    	this.state.value -=0.5
    }
    this.setState({
    	value:this.state.value
    })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半生过往

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值