React全家桶

React全家桶

      • React了解
        • 前言
        • 介绍
        • 原生缺点
        • React特点
        • 虚拟DOM对比真实DOM
      • React基础
        • JSX语法规则
        • JSX注释
        • JS语句和表达式
        • React开发者工具
        • 函数式组件
          • 创建使用
          • 渲染过程
        • 类式组件
          • 创建使用
          • 渲染过程
        • 复杂组件和简单组件
        • setState
        • React中的事件绑定
        • 组件实例的三大核心属性—state
        • 组件实例的三大核心属性—props
          • 传参限制,默认值
          • 传递参数
        • 组件实例的三大核心属性—ref
          • 字符串形式的ref
          • 回调函数形式的ref
          • createRef的ref
          • ref回调形式调用次数
        • React事件处理
        • 非受控组件
        • 非受控组件
        • 生命周期
          • 旧版生命周期钩子
          • 新版生命周期钩子
          • 重要的钩子
          • 即将废弃的钩子
        • DOM的diff算法
        • Hooks
          • 介绍
          • useState
          • useEffect
          • useRef
        • Fragment
        • Context
        • 组件优化
          • 问题
          • 解决
          • 原因
          • 解决
        • childrenProps
        • renderProps
        • ErrorBoundary
      • React脚手架
        • 介绍
        • 安装
        • 命令操作
        • Reatc项目结构
        • 创建组件
          • 第一种方式
          • 第二种方式
          • App组件引入使用
        • 防止样式重叠
        • React插件
        • 组件化编码流程
        • 父向子传递参数
        • 子向父传递参数
        • 请求配置代理
          • 方法一
          • 方法二
        • 消息订阅与发布
          • 安装
          • 订阅
          • 发布
      • React路由基本知识
        • SPA
        • 路由
          • 路由概念
          • 路由分类
          • 路由的原理
        • React-router-dom
      • Reatc路由v6版本以下
        • 下载
        • Link使用
        • NavLink使用
        • Route使用
        • 一般组件和路由组件
        • 路由使用
          • index.js文件
          • App.js文件
        • 获取标签体内容
        • Switch
        • 解决样式丢失
        • 路由的严格匹配和模糊匹配
        • 路由重定向
        • 路由嵌套
        • 路由组件传递参数—params
        • 路由组件传递参数—search
        • 路由组件传递参数—state
        • push与repalce
        • 编程式路由导航
          • replace
          • push
          • 其他
        • withRouter
        • BrowserRouter和HashRouter
        • lazyLoad
      • React路由v6版本
        • 下载
        • NavLink使用
        • Route使用
        • Routes
        • useRouters路由表
        • 路由重定向
        • 路由嵌套
          • 不使用路由表
          • 使用路由表
        • 路由组件传递参数—params
        • 路由组件传递参数—search
        • 路由组件传递参数—state
        • 编程式路由导航
        • useInRouterContext
        • useNavigationType
        • useOutlet
        • useResolvedPath
      • Ant Design基本使用
        • 下载
        • 使用
        • 样式按需引入
      • Redux
        • 介绍
        • 安装redux
        • redux的三个核心概念
          • action
          • reducer
          • stote
        • redux使用
          • store.js
          • constant.js
          • count_reducer.js
          • count_action.js
          • index.js
        • 异步action
          • store.js
          • count_action.js
      • React-Redux
        • 安装
        • 基本使用
          • App.jsx文件
          • 容器组件代码案例
          • UI组件代码案例
        • 优化代码
          • 容器组件
          • App.jsx
          • index.js
          • 代码整合
        • 多组件使用redux
          • 步骤
        • 使用redux开发者工具
        • 最终版本
      • 打包上线

React了解

前言

  • 该篇笔记是React全家桶笔记,该篇笔记包含最初的基础语法,脚手架创建项目,组件,集中式状态管理,路由
  • 结合视频加上自身的理解,写成了这一篇笔记

介绍

  • react是一个将数据渲染为html视图的开源javascript库

  • 主要负责的是操作dom呈现界面,关于获取数据和处理数据还需要自己去弄

  • react由国外知名公司Facebook开发的,且开源

原生缺点

  • 原生js操作dom繁琐且效率低

  • 使用js直接操作dom,浏览器会进行大量的重绘重排

  • 原生js没有组件编码方案,代码复用率低

  • react使用的jsx语法,并非js语法

React特点

  • 采用组件化模式,和vue一样,声明式编码,提高开发效率及组件复用率

  • 在react native中可以使用react语法进行移动端开发

  • 使用的虚拟dom和diffing算法,减少与真实dom的交互

虚拟DOM对比真实DOM

  • 本质上虚拟dom就是一个object对象(一般对象)

  • 虚拟dom比较”轻“,真实dom比较”重“,因为虚拟dom是react内部在用,没有那么多的属性

  • 虚拟dom最终会转换成真实dom,呈现在界面上

React基础

JSX语法规则

  • 全称:JavaScript XML

  • react定义的一种类似于XML的JS扩展语法

  • 定义虚拟dom的时候不要用引号

  • 标签中混入js表达式时要用{}

  • 样式的类名不要使用class,要使用className

  • 内联样式要用style={{key: value}}的形式去写

  • 虚拟dom必须只有一个根标签

  • 标签必须闭合

  • 若标签首字母为小写,则将标签转换成html中的同名标签,若无该标签,则报错

  • 若标签首字母为大写,react会去渲染对应的组件,若组件没有定义,则报错

JSX注释

{}里面才可以写js表达式,js里面注释/**/

return (
	
{/*

p1

*/}

p2

)

JS语句和表达式

  • 语句例如:if(){},for(){},switch(){}

  • 表达式:a,a+b,demo(1),arr.map(),function test (){}

React开发者工具

  • 链接:https://pan.baidu.com/s/1m2E6kqiUu3ENvNk5AvpoOQ
    提取码:3ewl

函数式组件

创建使用
  • 先进行声明函数

  • 里面通过return返回标签结构

  • 函数名必须大写,否则react会去寻找html标签

  • 然后通过标签形式进行使用该组件即可

  • 将该组件填充刚到id为box的容器中

  • function MyDemo(){
    	return 

    我是函数式组件

    } ReacrDOM.render(,document.getElementById("box"))
渲染过程
  • react先解析组件标签,找到MyDemo组件

  • 发现组件是函数定义的,随后调用该函数,将返回的虚拟DOM转换成真实得到DOM,然后呈现在界面上

类式组件

创建使用
  • 先进行声明类式组件

  • 里面必须有一个render方法返回标签结构

  • 类名大写且必须继承React.Component

  • 然后通过标签形式进行使用即可

  • 将该组件填充刚到id为box的容器中

  • class MyComponent extends React.Component {
    	render(){
    		return 

    我是类式组件

    } } ReacrDOM.render(,document.getElementById("box"))
渲染过程
  • react先解析组件标签,找到MyComponent组件

  • 发现组件是类定义的,随后new出该类的实例,并通过该实例找到render方法,将render返回的虚拟DOM转换成真实得到DOM,然后呈现在界面上

复杂组件和简单组件

  • 有state的组件就叫做复杂组件,没有的叫做简单组件

  • state就是状态,简单来说就是存放数据的地方,它可以驱动界面

setState

  • setState用来修改state中的数据

  • setState有两种写法

  • 第一种是对象写法,也叫做状态改变对象,也是一种简写

  • 第二种就是函数写法,是一个函数方式,通过返回修改,里面可以拿到当前的state和传递过来的props

  • 两种都有一个第二个参数,是一个回调函数,因为setState是异步的,如果想直接查看修改的结果就需要在回调函数中查看

  • this.setState((state,props) => {
    	return {count: state.count + 1}
    })
    
  • 使用情况

    如果新状态不依赖于原状态,就使用简写,如果依赖于,就使用函数,例如在原来数字的基础上加一

React中的事件绑定

  • 传统的绑定事件,只不过所有on之后的第一个字母大写

  • 事件不需要带小括号,否则会立刻执行,拿到返回的结果交给这个事件

  • 在类式组件里面,所创建的方法都是使用赋值来进行定义方法,也需要使用到箭头函数,因为在里面定义的方法是没有this指向的

  • 在函数式组件里面就是定义函数方法

  • 组件的自定义方法this为undefined

  • 如何解决自定义方法的this指向:强行绑定通过bind(),箭头函数

  • class MyComponent extends React.Component {
    	//类式组件的定义方法
    	demo = () => {
    		console.log("触发了事件")
    	}
    	render(){
    		return 

    我是类式组件

    } }

组件实例的三大核心属性—state

  • state是组件对象最重要的属性,值是对象(可以包含多组key: value的组合)

  • 组件被称为状态机,通过更新组件state的状态来更新页面显示的效果

  • 组件中render方法的this为组件的实例对象

  • 状态数据不可以直接修改,需要调用setState()

  • 修改的数据不是替换,而是合并

  • class MyComponent extends React.Component {
    	state = {name:"张三",age:19}
    	demo = () => {
    		this.setState({age:30})
    	}
    	render(){
    		return (
    			

    姓名:{this.state.name}

    年龄:{this.state.age}

    ) } }

组件实例的三大核心属性—props

  • 每个组件对象都会有props属性

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

  • 主要作用是通过标签属性从组件外向组件内传递变化的数据

  • 同时组件内部也不可以进行修改props数据

  • class MyComponent extends React.Component {
    	render(){
    		//从当前实例身上的props解构出来name和age
    		const {name,age} = this.props
    		return (
    			

    姓名:{this.state.name}

    年龄:{this.state.age}

    ) } }
传参限制,默认值
  • //传递参数的限制,例如名字必须是字符串类型,还必须传入
    //写在类里面的话就不用写MyComponent了,但是前面需要加上一个static
    //需要下载一个库叫做prop-types
    MyComponent.propTypes = {
    	name:PropTypes.string.isRequired
    }
    //传递参数的默认,例如年龄,没有传递的时候默认为18
    //写在类里面的话就不用写MyComponent了,但是前面需要加上一个static
    MyComponent.defaultProps = {
    	age:18
    }
    
传递参数
  • //第一种用法
    
    //第二种用法
    const info = {name:"李四",age:30}
    
    

组件实例的三大核心属性—ref

  • 用于获取DOM节点

  • 不要过度的使用ref’属性

字符串形式的ref
  • class MyComponent extends React.Component {
    	demo = () => {
    		console.log(this.refs.input1.value)
    	}
    	render(){
    		return (
    			
    ) } }
回调函数形式的ref
  • class MyComponent extends React.Component {
    	demo = () => {
    		console.log(this.input1.value)
    	}
    	render(){
    		return (
    			
    this.input2 = c}/>
    ) } }
createRef的ref
  • class MyComponent extends React.Component {
    	myRef01 = React.createRef()
    	myRef02 = React.createRef()
    	demo = () => {
    		console.log(this.myRef01.value)
    	}
    	demo2 = () => {
    		console.log(this.myRef02.value)
    	}
    	render(){
    		return (
    			
    ) } }
ref回调形式调用次数
  • ref如果使用回调的方式,那么在界面更新的时候会带调用两次,第一次的时候拿到的是null,并没有拿到该节点,第二次才真正的拿到该节点,如果把回调的方式单独拿出来定义成方法,那么就不会出现这个问题了,但是对于真正的开发项目来说,并没有什么影响,还是使用内联回调的方式即可,简便一点

React事件处理

  • react使用的是自定义事件,而不是使用原生的DOM事件,主要是为了更好的兼容

  • react是通过事件委托的方式处理的,委托给最外层的元素,为了更高效

  • 通过event.target就可以得到当前发生事件的DOM元素对象

非受控组件

  • 简单来说就是输入项,在点击登录的时候通过获取dom节点,然后拿到对应的值

  • class Login extends React.Component {
    	login = () => {
    		const {username,password} = this
    		alert(`用户名为:$(username.value),密码为:$(password.value)`)
    	}
    	render(){
    		return (
    			
    用户名: this.username = c}/> 密码: this.password = c}/>
    ) } }

非受控组件

  • 简单来说就是输入项在改变的时候自动将值存储到state状态中,然后需要的时候直接去state中取就可以了,建议使用这个,因为不需要操作ref,react的原则就是尽量少使用ref

  • class Login extends React.Component {
    	state = {
    		username:''
    		password:''
    	}
    	handerUsername = (event) => {
    		this.setState({
    			username:event.target.value
    		})
    	}
    	handerPassword = (event) => {
    		this.setState({
    			password:event.target.value
    		})
    	}
    	login = () => {
    		const {username,password} = this.state
    		alert(`用户名为:$(username),密码为:$(password)`)
    	}
    	render(){
    		return (
    			
    用户名: 密码:
    ) } }

生命周期

旧版生命周期钩子
  • 初始化阶段,由ReactDOM.render()触发,初次渲染

  • constructor()				//构造器
    componentWillMount()		//将要挂载之前
    render()					//render函数
    componentDidMount()			//挂载完毕,常用,一般都是在这个钩子里面左一些初始的事
    
  • 更新状态,由组件内部调用this.setState()或者父组件render触发

  • componentWillReceiveProps()		//用在父子组件,刚开始不会调用,更新的时候会进行调用,里面可以拿到参数props
    shouldComponentUpdate()			//控制更新的阀门
    componentWillUpdate()			//将要更新的之前
    render()						//必须使用的一个
    componentDidUpdate()			//更新完毕
    
  • 卸载组件,由ReactDOM.unmountComponentAtNode()触发

  • componentWillUnmount()			//常用,一般在这个钩子里面做一些收尾的事
    
新版生命周期钩子
  • 初始化阶段,由ReactDOM.render()触发,初次渲染

  • constructor()					//构造器
    getDerivedStateFromProps		//需要返回结果,可以返回null或者数据
    render()						//render函数
    componentDidMount()				//挂载完毕,常用,一般都是在这个钩子里面左一些初始的事
    
  • 更新状态,由组件内部调用this.setState()或者父组件render触发

  • getDerivedStateFromProps	//需要返回结果,可以返回null或者数据
    shouldComponentUpdate()		//控制更新的阀门
    render()					//必须使用的一个
    getSnapshotBeforeUpdate		//即将更新之前,可以拿到更新之前的props和state还有getDerivedStateFromProps所返回的数据
    componentDidUpdate()		//更新完毕
    
  • 卸载组件,由ReactDOM.unmountComponentAtNode()触发

  • componentWillUnmount()		//常用,一般在这个钩子里面做一些收尾的事
    
重要的钩子
  • render						//初始化渲染或更新渲染调用
    componentDidMount			//开启监听,发送ajax请求
    componentWillUnmount		//做一些收尾工作,例如清除定时器
    
即将废弃的钩子
  • 在18版本有可能会废弃,现在使用也可以使用,但是会出现警告,18之后使用的话需要加上前缀名UNSAFE_才可以进行使用,不建议使用下面这中方式

  • componentWillMount
    componentWillReceiveProps
    componentWillUpdate
    

DOM的diff算法

  • react在渲染到界面的时候会先生成虚拟DOM,然后再渲染到界面,当你更新数据的时候,会从新生成虚拟DOM,新的虚拟DOM会和旧的虚拟DOM进行比较,一样的话就直接使用真实DOM,不一样的话替换界面中对应的DOM节点

  • 在真正的开发中,例如循环渲染数据,必须有一个key,那么这个key进行使用id或者唯一标识,不建议使用index作为key,一旦发生破坏顺序的操作,那么就会产生性能问题,例如2000条数据,使用index作为key的话,此时往之前添加一条数据,因为key使用的是index,就会从新将2001条数据全部从新渲染,其实只需要添加一条数据即可,这就是使用index作为key的后果,而使用id或者唯一标识的时候就没有这个问题,index建议使用在不破坏数据顺序的时候,一般有id或者唯一标识的时候尽量使用id或者唯一标识

Hooks

介绍
  • hook是react16.8版本新增加的新特性/新语法
  • 主要就是让开发者在函数组件里面可以使用state以及其他的react特性
useState
  • 让函数组件也可以有state状态,并进行对状态的读写操作

  • //第一次初始化的时候会做缓存,后续不会因为传递的值把之前的给覆盖
    //里面有两个元素,xxx是内部当前值,setxxx是更新状态的函数,initvalue是初始值
    const [xxx,setxxx] = React.useState(initvalue)
    
    //更新状态的两种方法
    setxxx(newvalue)
    setxxx(value => newvalue)
    
useEffect
  • 让函数组件也可以用于生命周期钩子,并且可以在组件挂载后,状态更新后,组件卸载前做一些操作

  • 如果不加[],且没有返回函数的情况下,这个函数在组件挂载后,状态更新后都会执行

  • 如果加[],且没有返回函数的情况下,这个函数在组件挂载后执行,数组里面可以放置监听的状态,当状态改变的时候就会再次触发,相当于状态更新后的钩子

  • 如果加[],且有返回函数的情况下,这个函数的return就会在卸载之前进行执行

  • React.useEffect(() => {
    	return () => {
    		
    	}
    },[])
    
useRef
  • 用于获取元素节点

  • 对应一个只能存储一个

  • const myRef = React.useRef()
    
    
    

Fragment

  • 不会渲染到界面,用于不使用div当作跟标签的情况下

  • 也可以使用<>

  • 两者的区别就是Fragment可以写key,空标签不可以写key’

  • import {Fragment} from "react"
    
    export default class Demo01 extends Component {
      render() {
        return (
          
            

    文字描述一

    文字描述二

    ) } }

Context

  • 一种组件之间的通信方式,常用于祖组件和后代组件间的通信

  • 创建Context容器对象,名称首字母大写

  • 祖组件

  • const XxxContext = React.createContext()
    
    	子组件
    
    
  • 后代组件,这种写法只限于类式组件

  • //声明接受context
    static contextType = XxxContext
    //读取context中的value数据
    this.context
    
  • 第二种接受写法,可以应用于类式组件和函式组件

  • 
    	{
    		value => (要显示的内容)
    	}
    
    

组件优化

问题
  • 只要执行this.setState({}),即使不更新状态,也会从新调用render,从而引发效率低

  • 父组件重新render的时候,就会自动重新render子组件,纵使子组件没有用到父组件的任何数据,从而引发效率低

解决
  • 只有当state状态还有props数据发生改变的时候,才调用render
原因
  • Components中的shouldComponentUpdate总是返回true
解决
  • 方法一

    • 自身解决方法

    • 在shouldComponentUpdate生命周期钩子里面进行判断,如果当前的状态和即将要改变的状态一致,则返回false,不一致的情况下再返回true

    • shouldComponentUpdate(nextProps,nextState){
      	return !this.state.name === nextState.name
      }
      
    • 子组件解决方法

    • 在shouldComponentUpdate生命周期钩子里面进行判断,如果当前接受到父组件的状态和即将要改变时接受到父组件的状态一致,则返回false,不一致的情况下再返回true

    • shouldComponentUpdate(nextProps,nextState){
      	return !this.props.name === nextProps.name
      }
      
  • 方法二

    • 使用PureComponent,把Component改成PureComponent即可
    • 项目中都是使用此方法进行优化
    • 只是进行了state和props数据的浅对比,如果是数据内部的对象发生了改变,就返回false关闭阀门不更新
    • 不要之间修改里面的数据,要产生新的数据

childrenProps

  • 使用组件的时候传递数据,类似于vue的插槽技术

  • 不可以传递参数,传递参数使用renderProps

  • //传递Hello
    Hello
    
    //拿取Hello
    this.props.children
    

renderProps

ErrorBoundary

  • 错误边界

  • 例如现在有一个网页,里面有很多子组件,此时因为后端或者其他原因,其中一个组件出错了,则整个页面都报错运行不起来了,所谓的错误边界就是把错误控制在一定范围,不影响到其他地方的展示

  • 错误边界的处理都是在父组件里面进行处理

  • 只能捕获后代组件生命周期产生的错误,无法捕获自身组件产生的错误和其他组件在合成事件,定时器产生的错误

  • 使用方式

  • //先在父组件的状态里面生命一个是否发生错误
    state = {
    	hasError: false
    }
    //生命周期函数,一旦后台组件报错,就会触发
    static getDerivedStateFromError(error){
    	console.log(error)
    	return {
    		hasError: true
    	}
    }
    //捕获错误信息
    componentDidCatch(error,info){
    	//统计错误信息,然后发送给后台去
    	console.log(error,info)
    }
    //进行渲染时候判断
    render() {
        return (
            

    {this.state.hasError ? '当前网络不稳定,稍请后再试!' : 'hello world'}

    ) }

React脚手架

介绍

  • 用于帮助快速搭建一个基于React的模板项目,包含了所需要的配置(语法检查,jsx解析,devServer),同时也下载好了所需要的相关依赖,可以直接运行一个简单的效果

  • react创建项目的脚手架库:create-react-app

  • 项目的整体技术架构为:react+webpack+es6+eslint

  • 使用脚手架开发的项目特点:模块化,组件化,工程化

安装

  • npm install -g create-react-app
    

命令操作

  • create-react-app 项目名称		创建项目
    npm start			启动项目
    npm run build		开发完毕进行打包
    npm run eject		查看webpack文件,执行之后就不可以进行撤回了
    

Reatc项目结构

  • ├── .gitignore				Git提交忽略文件配置
    ├── package-lock.json		文件说明以及版本信息
    ├── package.json			文件说明以及版本信息
    ├── README.md				项目使用
    ├── node_modules			所依赖的所有包
    ├── src						主要目录
    │  ├── App.css				App组件的样式
    │  ├── App.js				App组件
    │  ├── App.test.js			App测试文件
    │  ├── index.css			全局公用样式,会在index.js中进行引入
    │  ├── index.js				React主要文件,入口文件
    │  ├── logo.svg				React初始图片
    │  ├── reportWebVitals.js	用于记录页面性能
    │  └── setupTests.js		用于组件,应用测试的
    ├── public					界面文件夹
    │  ├── index.html			主要界面
    │  ├── logo192.png			网页图标(iOS浏览器)
    │  ├── logo512.png			加壳网页图标
    │  ├── manifest.json		应用加壳配置文件
    │  ├── robots.txt			爬虫规则
    │  └── favicon.ico			网站的图标
    

创建组件

第一种方式
  • 创建components文件夹放置所有的组件,里面有对应组件的文件夹

  • ├── src						
    │  ├── components			
    │      ├── Hello
    │          ├── index.css
    │          ├── index.jsx
    
  • index.jsx文件

  • import React,{Component} from "react";
    import "./index.css"
    export default class Hello extends Component{
        render(){
            return (
                

    我是hello组件

    ) } }
第二种方式
  • 是文件夹下面的文件都是组件名命名,同时后缀为jsx或者js

  • ├── src						
    │  ├── components			
    │      ├── Wecome
    │          ├── Wecome.css
    │          ├── Wecome.jsx
    
  • Wecome.jsx文件

  • import React from "react";
    import "./index.css"
    class Hello extends React.Component{
        render(){
            return (
                

    我是hello组件

    ) } } export default Hello
App组件引入使用
  • import React,{Component} from 'react';
    // 第一种写法,index可以省略
    import Hello from "./components/Hello"
    // 第二种写法,需要写全
    import Wecome from "./components/Wecome/Wecome"
    
    export default class App extends Component{
      render(){
        return (
          
    ) } }

防止样式重叠

  • 对应的css文件需要加上一个.module

  • ├── src						
    │  ├── components			
    │      ├── Hello
    │          ├── Hello.module.css
    │          ├── Hello.jsx
    │      ├── Wecome
    │          ├── Wecome.module.css
    │          ├── Wecome.jsx
    
  • 在引入的时候就可以使用样式模块化了,命名的话需要使用下面这种方式起名

  • import hello from "./Hello.module.css"
    export default class Hello extends Component{
        render(){
            return (
                

    我是hello组件

    ) } }

React插件

  • 推荐插件

  • ES7+ React/Redux/React-Native snippets
    

组件化编码流程

  • 拆分组件:拆分界面,抽离组件

  • 实现静态组件:使组件实现静态效果

  • 实现动态组件:动态显示初始化数据,数据的类型,数据名称,保存在那个组件

  • 交互:绑定事件

父向子传递参数

  • 传递参数

  • export default class App extends Component{
    	state = {
    		data:"我是父组件传递给子组件的参数"
    	}
        render(){
            return (
                
            )
        }
    }
    
  • 接受参数

  • export default class Hello extends Component{
        render(){
        	console.log(this.props.data)
            return (
                

    我是Hello组件

    ) } }

子向父传递参数

  • 接受参数

  • export default class App extends Component{
    	acceptHelloData = (data) => {
    		console.log("我是App组件,我接受到子组件Hello传递过来的数据为:",data)
    	}
        render(){
            return (
                
            )
        }
    }
    
  • 传递参数

  • export default class Hello extends Component{
    	handleBtnClick = () => {
    		const data = "我是Hello组件的数据"
    		this.props.handleBtnClick(data)
    	}
        render(){
            return (
                
            )
        }
    }
    

请求配置代理

方法一
  • 在package.json中追加以下配置

  • "proxy":"http://localhost:5000"
    
  • 优点就是配置简单,请求资源的时候不需要带上前缀

  • 缺点就是不能配置多个代理

  • 工作流程就是当你请求3000下面的资源不存在的时候就会走代理去请求5000下面的资源

方法二
  • 先在src目录下面创建一个文件,名称是固定的

  • setupProxy.js
    
  • 配置代理的代码

  • const { createProxyMiddleware } = require("http-proxy-middleware")
    module.exports = function(app){
    	app.use(
    		createProxyMiddleware("/api1",{		//api1是需要转发的请求(所有带/api1的请求都转发给target路径目标地址)
    			target: "http://localhost:5000",		//配置转发目标
    			changeOrigin: true,		//控制服务器收到的host字段,false的时候会收到3000,true的时候会收到5000
    			pathRewrite: {"^/api1": ""}		//必须配置,去除请求前缀
    		}),
    		createProxyMiddleware("/api2",{
    			target: "http://localhost:6000",
    			changeOrigin: true,
    			pathRewrite: {"^/api2": ""}
    		})
    	)
    }
    
  • 请求的时候,需要在端口号后面带上前缀

  • 优点是可以配置多个代理

  • 缺点就是配置繁琐,请求的时候如果需要走代理还需要加上前缀

消息订阅与发布

安装
  • npm install pubsub-js
    
  • 第二步往入口文件index.js里面进行引入,然后往React身上挂载一个方法

  • // 引入消息订阅
    import pubsub from 'pubsub-js'
    // 往vm身上挂载一个消息订阅方法
    React.pubsub = pubsub
    
订阅
  • 接受数据,通过回调拿到数据,因为里面有两个参数,第一个为事件名称,第二个为数据,因此在这里就可以使用_来进行占位,在最后组件销毁之前记得取消这个订阅

  •  componentDidMount() {
         this.pid = this.pubsub.subscribe("hello",(_,data) => {
             console.log("hello事件被触发了,触发者是:"+data)
         })
     }
     componentWillUnmount() {
         React.pubsub.unsubscribe(pid)
     },
    
发布
  • 提供数据,调用publish身上的hello方法,然后传递数据

  • accept() {
    	React.pubsub.publish('hello','我是demo08传递过去的数据')
    }
    

React路由基本知识

SPA

  • 单页Web页面

  • 整体就一个界面

  • 点击页面中的链接不会刷新界面,只会做界面的局部刷新

  • 数据通过接口进行获取之后,并在前端异步展示

  • 单界面,多组件

路由

路由概念
  • 一个路由就是一个映射关系(key: value)

  • key为路径,value可能是function或者component

路由分类
  • 后端路由

  • 是key对应一个function

  • 前端路由

  • 是key对应一个component

路由的原理
  • 实现路由切换主要靠的是history,来改变路径,然后会检测到,根据对应的路径渲染对应的组件

React-router-dom

  • react的一个插件库

  • 专门用来实现一个SPA应用

  • 基于react的项目基本都会用到该库

  • v5版本和v6版本来对比的话

  • 内置组件的改变:移除了,新增了等
    语法的变化:component={About}变成了element={}
    新增了多个hook:useParams,useNavigate,useMatch等
    也可以嵌套使用,且可以配合useRoutes()配置"路由表",但是需要通过组件来渲染子路由
    同时官方也明确了函数式组件
    

Reatc路由v6版本以下

下载

Link使用

  • to是要去的地址

  • About
    

NavLink使用

  • 使用activeClassName来指定选中的时候类名,然后高亮显示

  • About
    

Route使用

  • 用于注册路由,根据路径匹配,匹配到直接将对应的组件进行展示

  • path为路径是什么的时候,component是要渲染的组件

  • } />
    

一般组件和路由组件

  • 一般组件放在components文件夹里面,路由组件放在pages文件夹里面

路由使用

index.js文件
  • BrowserRouter是路径后面没有/#

  • HashRouter是路径后面有/#,但是兼容性更强

  • // 先从react-router-dom身上引入所需要的BrowserRouter(地址中没有/#),HashRouter(地址中有/#,但是兼容性更好)
    import { BrowserRouter,HashRouter } from "react-router-dom"
    
    // 使用BrowserRouter标签包裹住App组件,使用路由
    ReactDOM.render(
      
        
          
        
      ,
      document.getElementById('root')
    );
    
App.js文件
  • import React, { Component } from 'react'
    import "./App.css"
    // 通过按需引入所需要的标签
    import { Link,Route,Routes } from "react-router-dom"
    import Header from "./components/Header"
    import About from "./pages/About"
    import Home from "./pages/Home"
    
    export default class App extends Component {
      render() {
        return (
            
    {/* 切换路由标签,to是要去的地址 */} About Home
    }/> }/>
    ) } }

获取标签体内容

  • this.props.children
    

Switch

  • 如果使用的话,那么在匹配同一个路径之后,匹配到之后还会再次往下进行匹配,当路由配置过多的时候就会影响效率

  • 
        }/>
        }/>
    
    

解决样式丢失

  • 解决在public文件下下面的index.html文件里面引入css丢失的原因

  • 原因分析,在多级路由的时候,例如/sanqi/home的时候,如果index.html里面引入css写的是./开头的,那么最终的查找地址之前就会带上一个/sanqi,当发现查找不到的时候,就会把public文件下的index.html进行返回

  • 解决办法一

  • public文件夹下面的index.html中引入文件写法改变成/开头或者%PUBLIC_URL%/开头
    
  • 解决办法二

  • 可以使用hash的方式,这样就会把3000之后的全部进行省略掉
    

路由的严格匹配和模糊匹配

  • 就是路由跳转标签,后面使用的时候,例如to的路径是/home/a/b,组件渲染时候对应的是home,那么此时也可以进行跳转的,它匹配上home之后你后面多带的内容它就忽略了,但是如果写成a/home/b就不可以,在对应渲染组件的标签上面写上exact={true}即可开启严格匹配,目前v6版本是都开启了严格模式,v6之前的版本需要自己进行开启,默认没有进行开启

  • 
    

路由重定向

  • 当找不到对应路径的组件时候,渲染那个组件

  • 即刚开始初始的样子

  • 使用Redirect标签中的to来指定重定向的地址

  • import { Redirect } from "react-router-dom"
    
    

路由嵌套

  • 在有子路由的路由路径正常写就可以

  • }/>
    
  • 子路由的跳转标签,to后面需要写/home/news,不可以直接写news

  • News
    
  • 渲染组件的写法,path直接和上方to一样即可,直接写news,重定向需要写上/home,重定向的path是“”

  • 
        } />
        } />
        
    
    

路由组件传递参数—params

  • 传递参数的格式是对象格式,并且路径里面会显示参数

  • 点击传递参数

  • {item.title}
    
  • 渲染组件的时候进行接受

  • 
    
  • 拿取数据,从this.props.match.params身上拿取所传递过来的参数

  • const { id,title } = this.props.match.params
    

路由组件传递参数—search

  • 传递参数的格式是urlencoded编码字符串,并且路径里面会显示参数

  • 点击传递参数

  • {item.title}
    
  • 渲染组件的时候进行接受

  • 
    
  • 拿取数据,从this.props.location.search身上拿取所传递过来的参数,这个参数是一个urlencoded编码字符串,需要借助querystring进行解析

  • const { search } = this.props.location
    

路由组件传递参数—state

  • 传递参数的格式是对象格式,并且路径里面不会显示参数

  • 点击传递参数

  • {item.title}
    
  • 渲染组件的时候进行接受

  • 
    
  • 拿取数据,从this.props.location.state身上拿取所传递过来的参数

  • const { id,title } = this.props.location.state
    

push与repalce

  • push会留下痕迹,repalce不会留下痕迹,会进行替换

  • {item.title}
    

编程式路由导航

replace
  • replace跳转

  • this.props.history.replace("跳转地址")
    
  • replace跳转传递params参数

  • this.props.history.replace("跳转地址/参数一/参数二")
    
  • replace跳转传递serach参数

  • this.props.history.replace("跳转地址?id=参数一&title=参数二")
    
  • replace跳转传递state参数

  • this.props.history.replace("跳转地址",{id:"我是id",title:"我是title"})
    
push
  • push跳转

  • this.props.history.push("跳转地址")
    
  • push跳转传递params参数

  • this.props.history.push("跳转地址/参数一/参数二")
    
  • push跳转传递serach参数

  • this.props.history.push("跳转地址?id=参数一&title=参数二")
    
  • push跳转传递state参数

  • this.props.history.push("跳转地址",{id:"我是id",title:"我是title"})
    
其他
  • 前进

  • this.props.history.goForward()
    
  • 后退

  • this.props.history.goBack()
    
  • go

  • 指的是前进两个,负数的话就是后退

  • this.props.history.goBack(2)
    

withRouter

  • 负责加工一般组件,将路由组件上面的三个属性加工到一般组件,成为高阶组件

  • import { withRouter } from "react-router-dom"
    
    class Header extends Component {
      render() {
        return (
            

    头部一般组件

    ) } } export default withRouter(Header)

BrowserRouter和HashRouter

  • 底层原理不一样
    • BrowserRouter使用的是h5的history API,不兼容IE9及以下版本
    • HashRouter使用的是URL的哈希值
  • path的表现形式不一样
    • BrowserRouter的路径中没有#
    • HashRouter的路径中包含#
  • 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state保存在history对象中
    • HashRouter的路径中包含刷新会导致state参数的丢失
  • HashRouter可以用于解决一些路径错误相关的问题

lazyLoad

  • 实现路由的懒加载

  • import {lazy,Suspense} from "react"
    
    const Home = lazy(() => import("./Home"))
    const About = lazy(() => import("./About"))
    
    路由懒加载...
}> }/> }/>

React路由v6版本

下载

  • npm i react-router-dom
    

NavLink使用

  • 需要写成一个回调函数,上来就会进行一次调用,往后每次点击的时候都会进行一次调用

  • 根据三元运算表达式,来判断isActive,然后渲染对应的class类名,有当前activeItem类名的高亮显示

  • 如果将来有一天,里面有子路由了,那么需要子路由亮的时候父路由不再亮,那么就需要加上一个end

  •  isActive ? "navBoxItme activeItem" : "navBoxItme"} to="/about"/>About
    

Route使用

  • path为路径是什么的时候,element是要渲染的组件

  • 在外层需要包裹一个Routes标签,这个是必须的

  • 
    	}/>
    
    

Routes

  • 如果使用的话,那么在匹配同一个路径之后,匹配到之后还会再次往下进行匹配,当路由配置过多的时候就会影响效率

  • 在v6中是必须使用Routes这个标签来包裹所以的Route标签

  • 
        }/>
        }/>
    
    

useRouters路由表

  • 只能在v6版本中进行使用,生成路由规则

  • 将所有的路由抽离成一个js文件进行配置,方便对于路由的管理

  • 先在src目录下面创建一个文件夹,然后下面创建一个index.js文件,里面内容如下,写好自己的路由规则,path为路径,element为要渲染的组件,包括重定向等等

  • import About from "../pages/About"
    import Home from "../pages/Home"
    import { Navigate } from "react-router-dom"
    
    export default [
        {
            path:"/about",
            element:
        },
        {
            path:"/home",
            element:
        },
        //路由重定向
        {
            path:"/",
            element:
        }
    ]
    
  • 使用,在App.js文件里面写法

  • 引入对于的路由表,使用useRoutes进行处理一下,将生成的结果放到指定位置即可

  • import React from 'react'
    import { NavLink, useRoutes } from "react-router-dom"
    import routes from "./routes"
    
    export default function App() {
      const routesElement = useRoutes(routes)
      return (
        
    About Home
    {routesElement}
    ) }

路由重定向

  • 当找不到对应路径的组件时候,渲染那个组件

  • 即刚开始初始的样子

  • 使用Route标签结合Navigate标签来指定重定向的地址

  • import { Route,Navigate } from "react-router-dom"
    }>
    

路由嵌套

不使用路由表
  • 嵌套路由可以写成嵌套的方式,里面的path就不需要加/了

  • 
        } />
        }>
        	} />
        	} />
        
        }>
    
    
  • 最后一步就是,在根标签的下级,写上标签,用于渲染子路由对应的组件

  • import { Outlet } from "react-router-dom"
    
    
使用路由表
  • 子路由的跳转标签,to后面不需要写/home/news,直接写news即可,/home/news写法也是可以的,或者./news也可以

  • News
    
  • import About from "../pages/About"
    import Home from "../pages/Home"
    import Message from "../pages/Message"
    import News from "../pages/News"
    import { Navigate } from "react-router-dom"
    
    export default [
        {
            path:"/about",
            element:
        },
        {
            path:"/home",
            element:,
            children:[
            	{
            		path:"message",
            		element:
            	},
            	{
            		path:"news",
            		element:
            	}
            ]
        },
        {
            path:"/",
            element:
        }
    ]
    
  • 最后一步就是,在有子路由的jsx文件里面写上标签,用于渲染子路由对应的组件,占位的标签

  • import { Outlet } from "react-router-dom"
    
    

路由组件传递参数—params

  • 传递参数的格式是对象格式,并且路径里面会显示参数

  • 点击传递参数

  • {item.title}
    
  • 渲染组件的时候进行接受,在路由表中进行占位

  • {
        path:"detail/:id/:title",
        element:
    }
    
  • 函数组件,使用useParams来接收即可

  • import React from "react";
    import { useParams } from "react-router-dom"
    
    const Detail = () => {
      const { id,title } = useParams();
      return (
        

    {id}

    {title}

    ); } export default Detail

路由组件传递参数—search

  • 传递参数的格式是urlencoded编码字符串,并且路径里面会显示参数

  • 点击传递参数

  • {item.title}
    
  • 拿取数据,使用userSearchParams进行拿取到search,再通过search.get()进行获取参数

  • import React from "react";
    import { useSearchParams } from "react-router-dom"
    
    const Detail = () => {
      const [search,setSearch] = useSearchParams();
      const id = search.get("id")
      const title = search.get("title")
      return (
        

    {id}

    {title}

    ); } export default Detail
  • 更新search参数

  • import React from "react";
    import { useSearchParams } from "react-router-dom"
    
    const Detail = () => {
      const [search,setSearch] = useSearchParams();
      const id = search.get("id")
      const title = search.get("title")
      return (
        

    {id}

    {title}

    ); } export default Detail

路由组件传递参数—state

  • 传递参数的格式是对象格式,并且路径里面不会显示参数

  • 点击传递参数

  • {item.title}
    
  • 拿取数据,使用useLocation进行拿取到state,再拿取对应的参数

  • import React from "react";
    import { useLocation } from "react-router-dom"
    
    const Detail = () => {
      const {state{id,title}} = useLocation();
      return (
        

    {id}

    {title}

    ); } export default Detail

编程式路由导航

  • 统一使用useNavigate进行跳转,第一个是跳转的路径,第二个是配置对象,replace是否开启,传递参数只能传递state参数,还可以写成navigate(1)或者navigate(-1),一个前进一个后退

  • import React from "react";
    import { useNavigate } from "react-router-dom"
    
    const Detail = () => {
      const navigate = useNavigate()
      const {state{id,title}} = useLocation();
      function skipHome(){
      	navigate("/home",{
      		replace:false,
      		state:{
      			message:"携带的消息"
      		}
      	})
      }
      return (
        

    {id}

    {title}

    ); } export default Detail

useInRouterContext

  • 用于判断是否在路由的上下文中,是的话调用返回true,否则返回false,所有被App组件包裹的路由,都是true

  • import React from "react";
    import { useInRouterContext } from "react-router-dom"
    
    const Demo = () => {
      console.log(useInRouterContext())
      return (
        
    Demo
    ); } export default Demo

useNavigationType

  • 用于返回当前导航的类型

  • POP,PUSH,REPLACE

  • POP指的是刷新

  • import React from "react";
    import { useNavigationType } from "react-router-dom"
    
    const Demo = () => {
      console.log(useNavigationType())
      return (
        
    Demo
    ); } export default Demo

useOutlet

  • 用于呈现当前组件中渲染的嵌套组件

  • 如果里面的组件还没有进行渲染,就返回的是null

  • import React from "react";
    import { useOutlet } from "react-router-dom"
    
    const Demo = () => {
      console.log(useOutlet())
      return (
        
    Demo
    ); } export default Demo

useResolvedPath

  • 给一个值,帮你解析url中的path,search,hash值

  • import React from "react";
    import { useResolvedPath } from "react-router-dom"
    
    const Demo = () => {
      console.log(useResolvedPath("/user?id=001&name=三七#qwe"))
      return (
        
    Demo
    ); } export default Demo

Ant Design基本使用

下载

  • npm i antd
    

使用

  • 需要先引入样式,在src下面的index.js里面进行引入

  • import "antd/dist/antd.css"
    
  • 其他参考官方文档进行使用

  • https://ant.design/components/button-cn
    

样式按需引入

  • 下载对应所需要的库

  • npm i @craco/craco
    
  • package.json修改配置

  • "scripts": {
       "start": "craco start",
       "build": "craco build",
       "test": "craco test",
    }
    
  • 在根目录创建一个craco.config.js文件,进行配置按需引入即可

  • module.exports = {
      // ...
    };
    

Redux

介绍

  • redux是一个专门用于做状态管理的js库,不是react的库

  • 它可以在react,vue,angular等项目中使用,但是配合最多的还是react

  • 作用就是管理react应用中多个组件共享的状态

  • 某个组件的状态也需要让其他组件可以随时拿到(共享)

  • 一个组件需要改变另外一个组件的状态(通信)

  • 能不用就不用,在可以不使用的情况下尽量不要使用

安装redux

  • npm i redux
    

redux的三个核心概念

action
  • 动作的对象

  • 包含两个属性

  • type,标识属性,值为字符串,唯一 ,必要属性

  • data,数据属性,值类型任意,可选属性

reducer
  • 用于初始化状态,加工状态

  • 加工时,根据旧的state和action,产生新的state的纯函数

  • redux中的reducer函数必须是一个纯函数

stote
  • 将state,action,reducer联系在一起的对象

redux使用

  1. 去除组件本身的状态

  2. src下建立redux文件夹,里面创建store.js和redecer.js

  3. store.js里面引入redux中的createStore函数,使用该函数创建一个store

  4. createStore调用的时候要传入一个为其服务的reducer

  5. 记得暴漏store对象

  6. reducer.js

    reducer的本质是一个函数,接收:preState,action,返回加工后的状态

    reducer有两个作用:初始化状态,加工状态

    reducer第一次调用的时候,是store自动触发的,传递preState是undefined

  7. 在index.js中检测store中状态的改变,一旦发生改变重新渲染App

  8. redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写

store.js
  • 主要文件

  • // 当前这个文件用于暴漏store对象,整个应用只有一个store对象
    
    // 从redux身上引入一个创建store的方法
    import {createStore} from "redux"
    
    // 引入为count组件服务的reducer
    import countReducer from "./count_reducer"
    
    // 创建store,并且暴漏出去
    export default createStore(
        countReducer
    )
    
constant.js
  • 常量文件

  • // 该模块是用于定义type类型的常量值,在便于管理的同时也可以防止用户写错单词
    
    export const INCREMENT = "increment"
    export const DECREMENT = "decrement"
    
count_reducer.js
  • 为count组件服务的reducer

  • // 该文件是用于创建一个为Count组件服务的reducer,reducer本质上就是一个函数
    // reducer函数会接受两个参数,分别为:之前的状态(reducer),动作对象(action)
    
    // 引入使用常量模块
    import {
        INCREMENT,
        DECREMENT
    } from "./constant.js"
    
    // 初始化的时候preState是不传递参数的,此时是undefined,这个时候需要有一个默认值
    // 然后进行导出
    export default function countReducer(preState = 0, action) {
        // console.log(preState,action)
        // 通过解构拿到当前动作(type)和数据(data)
        const { type, data } = action;
        // 判断当前动作对象
        switch (type) {
            case INCREMENT:
                // console.log("increment执行了")
                return preState + data
            case DECREMENT:
                return preState - data
            default:
                return preState
        }
    }
    
count_action.js
  • 为count组件服务的action

  • // 该文件是生成为Count组件服务的action对象
    
    // 引入使用常量模块
    import {
        INCREMENT,
        DECREMENT
    } from "./constant.js"
    
    // 定义加的action
    export const createIncrementAction = data => (
        {
            type:INCREMENT,
            data
        }
    )
    
    // 定义减的action
    export const createDecrementAction = data => (
        {
            type:DECREMENT,
            data
        }
    )
    
index.js
  • 在入口文件里面进行监听store改变

  • import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    // 引入store
    import store from "./redux/store"
    
    // 开启所有的监听,只要更改,执教将整个app进行从新渲染调用render函数,不用担心效率的问题,底层有diff算法
    store.subscribe(() => {
      ReactDOM.render(
        
          
        ,
        document.getElementById('root')
      );
    })
    

异步action

  • 延迟的动作不想交给组件自身,想交给action

  • 何时需要用到action,想对状态进行改变,但是不知道具体怎么改变,需要等具体数据回来之后再进行任务

  • 先安装thunk,并且配置在store中

  • npm i redux-thunk
    
  • 创建的action函数不再返回一个对象了,而是一个函数,在这个函数里面写异步任务

  • 异步任务执行完毕之后,使用同步action去真正的操作数据

  • 异步action不是必须要写的,完全可以自己等待异步任务的结果再去分发同步action

store.js
  • 主要文件

  • // 当前这个文件用于暴漏store对象,整个应用只有一个store对象
    
    // 从redux身上引入一个创建store的方法
    // applyMiddleware是用来处理action异步任务的
    import {createStore,applyMiddleware} from "redux"
    
    // 引入为count组件服务的reducer
    import countReducer from "./count_reducer"
    
    // 引入redux-thunk,用于支持异步action
    import thunk from "redux-thunk"
    
    // 创建store,并且暴漏出去
    export default createStore(
        countReducer,
        applyMiddleware(thunk)
    )
    
count_action.js
  • 为count组件服务的action

  • // 该文件是生成为Count组件服务的action对象
    
    // 引入使用常量模块
    import {
        INCREMENT,
        DECREMENT
    } from "./constant.js"
    
    // 定义加的action
    export const createIncrementAction = data => (
        {
            type:INCREMENT,
            data
        }
    )
    
    // 定义减的action
    export const createDecrementAction = data => (
        {
            type:DECREMENT,
            data
        }
    )
    
    // 定义加的异步action,里面需要调用到同步action
    export const createIncrementAsyncAction = (data,time) => {
        return (dispatch) => {
            // store只能接受到一个对象,然后交给手下reducer进行处理,但是此时交给store的是一个函数
            // 我们就需要使用到一个第三方的库thunk来进行处理
            // 安装 npm i redux-thunk
            setTimeout(() => {
                console.log("通过thunk进行处理的异步action")
                dispatch(createIncrementAction(data))
            }, time);
        }
    }
    

React-Redux

安装

  • npm i react-redux
    

基本使用

  • 之前的组件现在被拆分成了UI组件和容器组件,容器组件包裹着UI组件,UI组件只负责展示界面和逻辑交互,并不会和redux进行打交道,全部是由容器组件和redux进行打交到

  • 首先创建一个文件夹containers,里面存放的都是UI组件的容器组件

App.jsx文件
  • 需要引入对应的容器组件,并且把需要用到的store传递进去

  • import React, { Component } from 'react'
    // 引入容器组件
    import Count from "./containers/Count"
    // 引入store
    import store from "./redux/store"
    
    export default class App extends Component {
      render() {
        return (
          
    {/* 渲染容器组件并且传递过去store */}
    ) } }
容器组件代码案例
  • // 该文件是Count的容器组件,用于链接react-redux和Count的UI组件
    
    // 引入Count的UI组件
    import CountUI from "../../components/Count"
    
    // 引入连接UI组件与redux
    import {connect} from "react-redux"
    
    // 引入创建的action
    import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction
    } from "../../redux/count_action"
    
    // 创建一个函数,返回的是一个对象
    // 返回的对象中key就作为传递给UI组件的props的key,value就作为传递给UI组件props的value
    // 该函数用于传递状态
    function mapStateToProps(state){
        return {
            count: state
        }
    }
    
    // 创建一个mapDispatchToProps函数,返回的是一个对象
    // 返回的对象中key就作为传递给UI组件的props的key,value就作为传递给UI组件props的value
    // 该函数用于传递修改状态的方法
    function mapDispatchToProps(dispatch){
        return {
            increment: number => dispatch(createIncrementAction(number)),
            decrement: number => dispatch(createDecrementAction(number)),
            incrementAsync: (number,time) => dispatch(createIncrementAsyncAction(number,time))
        }
    }
    
    //最后使用connect连接UI组件和容器组件,并且导出
    export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
    
UI组件代码案例
  • 对应的UI组件使用props就可以使用容器组件传递过来的redux状态和修改redux状态的方法

  • import React, { Component } from 'react'
    import "./index.css"
    
    export default class Count extends Component {
        state = {
            name:"三七"
        }
        increment = () => {
            const { value } = this.selectNumber
            this.props.increment(value * 1)
        }
        decrement = () => {
            const { value } = this.selectNumber
            this.props.decrement(value * 1)
        }
        incrementIfOdd = () => {
            const { value } = this.selectNumber
            if(this.props.count % 2 !== 0){
                this.props.increment(value * 1)
            }
        }
        incrementAsync = () => {
            const { value } = this.selectNumber
            this.props.incrementAsync(value * 1,1000)
        }
        render() {
            return (
                

    当前用户为:{this.state.name},当前求和为:{this.props.count}

    ) } }

优化代码

容器组件
  • 从最开始的创建两个函数,返回对应的状态和方法,在传入到connect中,简写成直接在connect中写

  • 状态的传递还是使用函数,方法的传递有函数和对象两种方式传递

  • // 该文件是Count的容器组件,用于链接react-redux和Count的UI组件
    
    // 引入Count的UI组件
    import CountUI from "../../components/Count"
    
    // 引入连接UI组件与redux
    import {connect} from "react-redux"
    
    // 引入创建的action
    import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction
    } from "../../redux/count_action"
    
    // 简写方式,第一个写成一个函数返回对应的状态,第二个也是一个函数返回对应操作状态的方法,也可以进行简写成一个对象
    // 第二个简写成对象,因为使用了react-redux,会自动帮助你调用dispatch,不需要自己手动写了
    export default connect(
        state => ({
            count: state
        }),
        // 完整写法
        // dispatch => ({
        //     increment: number => dispatch(createIncrementAction(number)),
        //     decrement: number => dispatch(createDecrementAction(number)),
        //     incrementAsync: (number,time) => dispatch(createIncrementAsyncAction(number,time))
        // }),
        // 简写
        {
            increment: createIncrementAction,
            decrement: createDecrementAction,
            incrementAsync: createIncrementAsyncAction
        }
    )(CountUI)
    
App.jsx
  • 不需要在这里进行引入store,容器组件太多的情况下,太麻烦,直接使用即可
index.js
  • 不需要使用store.subscribe来进行监听redux状态的改变了,同时App.jsx文件里面的store,在这里使用react-redux中的Provider标签包裹整个App,进行传递store

  • import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    // 引入store
    import store from "./redux/store"
    // 引入react-redux的Provider
    import { Provider } from 'react-redux'
    
    ReactDOM.render(
      
        
          
        
      ,
      document.getElementById('root')
    );
    
代码整合
  • 主要就是使用react-redux的时候,需要定义一个UI组件还有一个容器组件,此时就定义了两个文件,当组件比较多的时候就成倍增长文件,比较麻烦,因此可以定义在一个文件里面,主要的实现思路就是不在容器组件里面引入UI组件,而是直接将UI组件写在容器组件的文件里面,然后进行连接redux

  • 此时components文件里面的CountUI组件就可以进行删除了,在containers文件下面的容器组件里面写UI组件即可

  • import React, { Component } from 'react'
    //引入样式
    import "./index.css"
    // 引入连接UI组件与redux
    import {connect} from "react-redux"
    
    // 引入创建的action
    import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction
    } from "../../redux/count_action"
    
    // 定义UI组件
    class Count extends Component {
        state = {
            name:"三七"
        }
        increment = () => {
            const { value } = this.selectNumber
            this.props.increment(value * 1)
        }
        decrement = () => {
            const { value } = this.selectNumber
            this.props.decrement(value * 1)
        }
        incrementIfOdd = () => {
            const { value } = this.selectNumber
            if(this.props.count % 2 !== 0){
                this.props.increment(value * 1)
            }
        }
        incrementAsync = () => {
            const { value } = this.selectNumber
            this.props.incrementAsync(value * 1,1000)
        }
        render() {
            return (
                

    当前用户为:{this.state.name},当前求和为:{this.props.count}

    ) } } //容器组件并且链接UI组件 export default connect( //状态 state => ({ count: state }), //修改状态的action { increment: createIncrementAction, decrement: createDecrementAction, incrementAsync: createIncrementAsyncAction } )(Count)

多组件使用redux

  • 首先需要有一个redux文件夹在src下面,里面有一个store.js文件,constant.js文件,actions文件夹,里面放置的是对应组件的action,还有一个文件夹reducers,里面放置的是对应组件的reducer
  • 当是多个组件的时候,就需要变成对象的格式进行使用了,使用reudx身上的combineReducers函数组合起来两个reducer,然后进行对应的使用
步骤
  1. 首先有A组件和B组件,那么需要在constant.js文件里面先编写A组件的constant常量,然后需要在constant.js文件里面编写B组件的constant常量,然后定义和编写对应组件的reducer文件还有action文件
  2. 在store.js文件里面通过redux身上的combineReducers函数将两者结合在一起,然后将总的对象交给createStore
  3. 取对应状态的时候需要取到位,通过state.所定义的名称来进行取状态
  • import {createStore,applyMiddleware,combineReducers} from "redux"
    // 引入为count组件服务的reducer
    import countReducer from "./reducers/count"
    // 引入为count组件服务的reducer
    import personReducer from "./reducers/person"
    
    //进行汇总所有的reducer
    const allReducer = combineReducers({
        countReducer,
        personReducer
    })
    
    // 创建store,并且暴漏出去
    export default createStore(
        allReducer
    )
    
  • 使用的时候取状态

  • export default connect(
        state => ({
            userObjList: state.personReducer,
            count: state.countReducer
        }),
        {
            addUserObj: createAdduserAction
        }
    )(Person)
    

使用redux开发者工具

  • 下载

  • npm i redux-devtools-extension
    
  • 使用

  • import {composeWithDevTools} from "redux-devtools-extension"
    export default connect(allReducer,composeWithDevTools(applyMiddleware(thunk)))(CountUI)
    

最终版本

  • 一个redux文件夹,里面有管理常量的constant.js,创建仓库的store.js,创建action的actions文件夹,里面对应的是组件的action,还有reducers文件夹,里面的index.js是汇总所有reducer

打包上线

  • 通过命令行进行打包项目

  • npm run build
    

你可能感兴趣的:(前端,react.js,reactjs,react,前端框架,前端)