# react-biji **Repository Path**: sun-lan/react-biji ## Basic Information - **Project Name**: react-biji - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-02-25 - **Last Updated**: 2021-02-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React简介 ## 什么是React ``` React 起源于 Facebook React 是一个用于构建用户界面的 javascript 库。 React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。 ``` ## 虚拟Dom---快减少更新次数  减少更新区域 ``` 虚拟dom相当于在js和真实dom中间加了一个缓存。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行, 每当数据变化时,React都首先重新构建整个DOM树(减少页面更新次数), 然后React将当前整个DOM树和上一次的DOM树进行对比(DOM Diff算法-最小化页面重绘), 得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。 ``` ### 为什么虚拟 dom 会提高性能? ``` 虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作, 从而提高性能。 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树, 插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较, 记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了 ``` ### diff算法的作用 ``` 计算出虚拟DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面。 ``` ## React的diff算法 ``` 什么是调和? 将Virtual(虚拟)DOM树转换成actual(真实)DOM树的最少操作的过程称为调和 。 什么是React diff算法? diff算法是调和的具体实现。 ``` ## reqct高效的原因 ``` 使用虚拟DOM,不直接操纵页面DOM,减少页面重绘次数。同时使用DOM diff算法,最小化的减少对页面重绘。 ``` ## React特点 ```html 声明式设计 −React采用声明范式,可以轻松描述应用。(开发者只需要声明显示内容,react就会自动完成) 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。 灵活 −React可以与已知的库或框架很好地配合。 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。 (把页面的功能拆分成小模块--每个小模块就是组件) 单向数据流-- React是单向数据流,数据主要从父节点传递到子节点(通过props)。 如果顶层(父级)的某个props改变了,React会重渲染所有的子节点 ``` ## React组件特性 ```html React的核心就是组件:组件的设计目的是提高代码复用率,降低测试难度和代码的复杂程度。 提高代码复用率:组件将数据和逻辑进行封装。 降低测试难度:组件高内聚低耦合(各个元素高集成度低关联性),很容易对单个组件进行测试。 代码的复杂程度:直观的语法,可以极大提高可读性。 ``` ## 组件化编程思想--强内聚/弱耦合 ``` 灵活的组件化设计思想,可以让你的页面功能更具复用性。同时低耦合度的组件可以让功能之间的影响降到最低! ``` ## React 浏览器兼容性 ``` 除去ie8以下版本,其余浏览器都可以很好的支持 ``` # 开发环境搭建 ## React 开发环境搭建 ``` 页面插入react.js文件 react.js文件是创建React元素和组件的核心文件, react-dom.js文件用来把React组件渲染为DOM,此文件依赖于react.js文件,需在其后被引入。 Babel的主要用途是将ES6转成ES5 同时可以把JSX 语法转换新标准的JavaScript代码让现今浏览器兼容的代码 ``` ## React 依赖包下载 ``` react核心包: npm install react --save react-dom: npm install react-dom --save babel包: npm install babel-standalone --save ``` ``` 下载好的文件在node_modules目录。 1.找到react目录,找到这个目录下的umd目录下react.development.js;2.在react-dom/umd目录下找到react-dom.development.js 3.node_modules目录下找到babel-standalone目录下的babel.js ``` ## React --根DOM节点 ``` 页面中需要有一个div容器,容器中的内容都会被React Dom所管理。 这个容器叫做根DOM节点 注意:通常React 开发应用时一般只会定义一个根节点。 ``` ## React --本地模式引入 ```html ``` # ReactDOM.render()基础用法 ``` 编写React渲染组件ReactDOM.render() ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入页面指定的 根DOM 节点。 ``` ```html
``` # React --JSX ## jsx是什么 ``` JSX=javascript xml就是Javascript和XML结合的一种格式。是JavaScript 的语法扩展。 React推荐在 React 中使用 JSX 来描述用户界面。 当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。 Jsx中的HTML标签必须按照w3c规范来写 标签必须闭合 ``` ## jsx优点 ``` JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。 它是类型安全的,在编译过程中就能发现错误。 使用 JSX 编写模板更加简单快速。 ``` ## jsx-注释 ``` 注释/*内容*/ html标签内注释{/* 最外层有花括号*/} ``` ```js {/*我是注释*/} ``` ## jsx-多行html ``` 多个 HTML 标签,需要使用一个 父 元素包裹 使用圆括号包裹养成好习惯 ``` ```html ``` # JSX中使用模板语法 ## JSX插入变量 ``` 可以任意地在 JSX 当中使用 JavaScript表达式,在 JSX 当中的表达式要包含在大括号里。 ``` ```jsx ``` ## JSX渲染数组 ```jsx
``` ## JSX中使用表达式 ### JSX中使用普通表达式 ```jsx
``` ### JSX表达式 三目运算 ```jsx
``` ## JSX独立文件使用 ``` React JSX 代码可以放在一个独立文件上创建一个 demoreact.js 文件 页面中引用注意script中的type属性---要设置成typr="text/babel" ``` ## JSX绑定属性 ``` Jsx中也可以使用大括号来定义以 JavaScript 表达式为值的属性: 切记使用了大括号包裹的 JavaScript 表达式时就不要再到外面套引号了。 JSX 会将引号当中的内容识别为字符串而不是表达式。 ``` ``` jsx 中如果想给html标签属性插入变量 那么必须必须必须不能加双引号 加了会把其中的内容当字符串解析 用{你想插入的变量} ``` ```jsx
``` ## JSX定义内部样式 ``` 定义样式对象, 以style属性引用一个对象 样式名以驼峰命名法表示, 如text-align需写成textAlign 默认像素单位是 px 外层的{}是jsx解析js的语法 里面的{}是js对象的花括号 如果想使用行内样式的话 那么必须在多个单词的时候把-变成大写 ``` ```jsx
``` ## JSX引用外部样式 ``` 引用外部样式时, 不要使用class作为属性名, 因为class是js的保留关键字。 JSX 语法上更接近 JavaScript 而不是 HTML, 所以 React DOM 使用 className(小驼峰命名)来定义属性的名称, 而不使用 HTML 属性名称的命名约定。 ``` ```jsx Document
``` ## jsx规则扩展 ``` 1.html的value属性要写成:defaultValue 2.html的checked属性要写成:defaultChecked 3.style 里面写对象 4.class 写className ``` # React列表渲染/循环 ``` 在实际应用中 数据展示 是最基本的功能。 React中使用的map()进行列表的渲染 ``` ## map()方式-基本写法 ```jsx
``` ## map()方式-按照w3c规范写 ``` 必须按照w3c的格式写,比如下面不写就会报错 ``` ```jsx
``` ## map()方式-抽离代码 ``` 将代码抽离出去,变得简洁 ``` ```jsx
``` ## map()方式-复杂写法 ```jsx
``` ## for循环 ``` 创建一个新数组,追加到里面 ``` ```jsx
``` ## for..in. ```jsx
``` ## forEach ```jsx
``` # React 列表渲染--Keys ``` Keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。 因此要给数组中的每一个元素赋予一个确定的标识。 一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串 ``` ```jsx
``` # return换行 ``` 在return后面的代码要换行,要用()包裹起来 ``` ```jsx ``` # render重新渲染 ``` 默认情况下 react中数据改变了 页面是不会自动发生变化的 原因是因为初始化 render已经渲染到页面中了 如果想数据改变页面发生改变的话 那么就要重新的调用render来进行页面修改 ``` ## 点击变色 ```jsx
``` ## 事件绑定 ``` 在react中 如果项进行事件的绑定 那么就需要使用 传统js的事件来进行 但是在事件调用的时候 第二个单词的首字母必须大写 ``` # 对象取值 ``` js的对象取值方式有两种 第一种 就是用 .点的方式 第二种 就是用 []的方式区别点的方式就是正常取值方式方括号取值 实在数据为变量的时候 在实际项目中一般使用点,会方便许多,但是如果key是变量的话就不能使用点了, js会理解变量为对象的key值,造成混淆 ``` ```jsx let obj={ name:"xx", age:18, sex:"true" } obj.name obj["name"] ``` # Object方法 ``` Object方法object.keys()返回一个数组类型 值是方法中对象的键(key) Object.values()返回一个数组类型 值是方法中对象的值(value) Object.entries()返回一个数组类型 值是方法中对象的键和值 ``` ```jsx
``` ```js Array(3) 0: "xx" 1: 18 2: "true" length: 3__proto__: Array(0) Array(3) 0: "name" 1: "age" 2: "sex" length: 3__proto__: Array(0) Array(3) 0: (2) ["name", "xx"] 1: (2) ["age", 18] 2: (2) ["sex", "true"] length: 3__proto__: Array(0) ``` # 遍历对象 ## Object.values(obj) ```jsx
``` ## Object.keys(obj) ```jsx
``` # 模块与模块化 ``` 模块:向外提供特定功能的js文件,提高js的复用率简化编写提高循行效率 模块化:当应用的js都是用js模块编写的,这个应用就是模块化应用 ``` # 组件与组件化 ``` 组件:用来实现页面局部功能效果的代码合集(html/css/js)简化复杂页面的编码,提高运行效率 组件化:当应用多使用组件的方式来完成,这个应用就是一个组件化应用 ``` # 函数组件-无状态组件 ## 函数基本组件 ``` 组件 首字母大写 首字母大写 首字母大写并且其后每个字母首字母大写 ``` ```jsx
``` ## 多行标签组件 ``` 多行代码必须有一个根标签 ``` ```jsx
``` ## 函数组件的props ``` 一个组件被重复使用但是显示内容却不同 完成正向传值 Props对于使用它的组件来说,是只读的。 一旦赋值不能修改。也就是说props的值是不可变的只能在渲染的时候传入无法动态赋值。 ``` ``` props 是组件对外的接口。使用props就可以从外部向组件内部进行数据传递 完成父组件传值给子组件 注意:Props对于使用它的组件来说,是只读的。一旦赋值不能修改。也就是说props的值是不可变的只能在渲染的时候传入无法动态赋值。 组件无论是使用无状态组件还是通过 类组件声明,都决不能修改自身的 props。 当一个组件被注入Props 值时,属性值来源于它的父级元素,从父级到子元素。 ``` ```jsx
``` ## props传递多个参数 ``` 在参数较多的时候可以把Object类型进行传递 如果子组件需要多个props 那么我们在传递的时候可以使用扩展运算符 进行高效的数据传递 子组件在定义props的时候 如果有多个 那么就需要写多次的 props.xxxx 所以 在这个时候需要用 解构赋值来进行优化代码 ``` ```jsx
``` ## props的默认值 ### 用||来进行判断 ``` 兼容模式 使用||的方式来进行默认值额设置 ``` ```jsx
``` ### Props默认值-defaultProps ``` 新语法 使用组件名.defaultProps={}的写法 有些时候, 我们需要设置一些默认属性, 在父级组件未向子级组件传递数据时, 填充默认值。defaultProps ``` ```jsx
``` ## props验证 ``` 15.5之前Props 验证使用 propTypes,它可以保证我们在应用组件的时候可以正确的传递值 在15.5版本之核心已经在核心包中移除了 如果想使用 1npm install --save prop-types2 组件名.propTypes={ 验证的内容: PropTypes.数据类型 } 传入的类型不对,不会影响代码的正常执行,但是会报错 ``` ```jsx Document
``` # 类组件 ## 类组件基本写法 ``` 需要注意类名的首字母必须大写 一个组件类必须必须必须要实现一个 render 方法。这个方法必须要返回一个jsx元素。 必须要用一个外层的jsx元素把所有的内容包裹起来,返回并列的多个元素需要有个父元素包裹 ``` ```html
``` ## 类组件props传参 ``` 使用this.props.xx 接收值 创建的时候 直接使用 this.props.xxx即可创建成功 ``` ```jsx
``` ## defaultProps设置默认属性 ``` 类方式创建使用defaultPropos进行默认值设置两种方式 static defaultProps = {xiaoming:"xiaoming默认值"} Zi.defaultProps={xiaoming:"我是默认值"} ``` ```jsx
``` ## props验证 ``` Zi.propTypes={ name:PropTypes.string } ``` ```jsx
``` # state状态机 ## 状态机 ``` 在react中开发者只需要关心数据。数据改变页面就会发生改变 数据等同于状态。状态改变-页面绑定的数据就由react进行改变 组件被称之为“状态机”,视图与状态一一对应 ``` ``` 使用状态必须先初始化:this.state={} 读取状态:this.state.key1 使用--更新状态 更新状态---》组件界面更新:this.setState({}) 小知识:setState()是异步的会自动触发render函数的重新渲染 ``` ## constructor--super - ES6的继承规则得知,不管子类写不写constructor,在new实例的过程都会给补上constructor。 - 可以不写constructor,一旦写了constructor,就必须在此函数中写super(),super调用父类的构造方法,此时组件才有自己的this,在组件的全局中都可以使用this关键字,否则如果只是constructor 而不执行 super() 那么以后的this都是错的!!! - super()继承父组件的 this - 当想在constructor中使用this.props的时候,super需要加入(props), - 此时用props也行,用this.props也行,他俩都是一个东西。(不过props可以是任意参数,this.props是固定写法) ## state状态基本使用 ```jsx
``` ## setState是异步的 ``` state状态 他在修改的时候使用的是setState 使用setState修改之后页面会自动更新 原因是因为 setState会自动触发render方法进行页面渲染 setState是一个异步操作 如果想看到修改后的值 就在setState回调中查看 ``` ```jsx
``` ## 插入字符串标签 ``` 如何往react中渲染字符串标签 dangerouslySetInnerHTML={{ __html(html前面是双_):你要插的数据 }} 使用react给我们提供的方法 dangerouslySetInnerHTML = {{ __html:你要插入的字符串 }} ``` ```jsx
``` # refs ``` 标识组件内部的元素 注意不能在函数组件中使用 React的ref有3种用法: 1. 字符串(官方不推荐使用) 2. 回调函数(官方推荐) 3. React.createRef() (React16.3新提供) ``` ## 使用字符串 ``` 最早的ref用法。使用this.refs.xxx来进行访问 ``` ```jsx
``` ## 回调函数 ``` 回调函数就是在dom节点或组件上挂载函数,函数的入参是dom节点,达到的效果与字符串形式是一样的,都是获取其引用。 ``` ```jsx
``` ## React.createRef() ``` 在React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上该ref的current属性将能拿到dom节点或组件的实例 ``` ```jsx
``` # React受控组件 ``` React负责渲染表单的组件。同时仍然控制用户后续输入时所发生的变化。 值是来自于state控制的 输入表单元素称为“受控组件”。 被react负责渲染的表单组件 react仍然控制用户后续输入时所发生的变化 ``` ```jsx
``` # 事件绑定/事件处理 ``` React事件绑定属性的命名采用小驼峰式写法。 绑定函数的过程中不加() 否则函数会立即执行 在调用事件的时候使用的是小驼峰 命名法 ``` ## 阻止默认行为 ``` React中阻止默认行为使用preventDefault(); ``` ## 修改this指向 ``` 方式1:通过bind方法进行原地绑定,从而改变this指向 方式2:通过创建箭头函数 方式3:在constructor中提前对事件进行绑定 方式4:将事件调用的写法改为箭头函数的形式 ``` ### 1-通过bind方法进行原地绑定 ```jsx
``` ### 2-通过创建箭头函数 ```jsx
``` ### 3-在constructor中提前对事件进行绑定 ```jsx
``` ### 4-将事件调用的写法改为箭头函数的形式 ```jsx
``` ## 函数的参数传递 ``` 向事件处理函数中传递参数。 方式1(推荐):通过 bind 的方式进行传递 在bind中进行参数的传递 函数.bind(this,参数1,参数2,。。。。。。) 方式2:通过箭头函数传递。注意使用箭头函数调用事件对象必须显式的进行传递 在调用的时候使用箭头函数调用方法 ()=>{ this.你要调用的方法(参数1,参数2,..........) } ``` ```jsx
``` # React组件生命周期 ## React 生命周期 每个组件都包含“生命周期方法”,可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法 React 生命周期分为三种状态: - 挂载阶段 - 更新阶段 - 卸载阶段 ## React 生命周期--挂载阶段 ``` constructor()中完成了React数据的初始化, componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。 componentDidMount() 组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染 componentWillUnmount在组件从 DOM 中移除之前立刻被调用。 ``` ## React 生命周期--更新阶段 ``` componentWillReceiveProps。 (nextProps)在组件接收到一个新的prop时被调用。 shouldComponentUpdate。判定组件是否要更新html 主要用于性能优化(部分更新) 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后, state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新 componentWillUpdate()。组件即将更新html时候调用shouldComponentUpdate返回true以后,组件进入重新渲染的流程 componentDidUpdate。 在组件完成更新后立即调用。 render()函数会插入jsx生成的dom结构,react会生成一份虚拟dom树, 在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树, 比较以后,找到最小的有差异的DOM节点,并重新渲染。 ``` ## React 生命周期--卸载组件 ``` ReactDOM.unmountComponentAtNode(document.getElementById("demodiv"));//卸载组件 ``` ## React 生命周期纵览 ``` componentWillMount 组件渲染之前调用 componentDidMount 组件渲染之后调用在第一次渲染后调用 componentWillReceiveProps在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用。 shouldComponentUpdate 判定组件是否要更新html componentWillUpdate组件即将更新html时候调用 componentDidUpdate 在组件完成更新后立即调用。 componentWillUnmount在组件从 DOM 中移除之前立刻被调用。 ReactDOM.unmountComponentAtNode(document.getElementById("demodiv"));//卸载组件 ``` ```jsx
``` # 条件渲染 React 中的条件渲染和 JavaScript 中的一致,使用 JavaScript 操作符 if 或条件运算符来创建表示当前状态的元素,然后让 React 根据它们来更新 UI。 ## 条件渲染-if 语句 在React中使用if语句条件渲染是最简单的,但是注意jsx中不允许有if ```jsx
``` ## 条件渲染-三目运算符 ```jsx
``` ## 条件渲染-&& 条件渲染的另一种方法是使用 JavaScript 的条件运算符&&但是注意该种方法只能渲染一个组件 ```jsx
``` # 脚手架 ## 创建项目 ``` create-react-app 是facebook官方开发 ``` ``` create-react-app安装 npm install -g create-react-app 安装脚手架 create-react-app --version 查看版本 create-react-app 项目名 创建项目 cd 项目名 切换到创建好的项目中 npm start 启动运行 ``` ## 创建组件 ``` 创建组件可以使用快捷键 rcc即可创建 但是注意 这个快捷键是在大家对组件的创建记忆非常熟练之后在使用 千万不叫只记住rcc了 ``` ## 使用组件 ``` 使用1.引用2 ``` ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import Home from "./components/home/home.jsx" import reportWebVitals from './reportWebVitals'; ReactDOM.render( , document.getElementById('root') ); ``` ## 设置样式 ### 传统方式 ``` 1.创建css文件2.在想用的组件中 使用 import “css路径”即可使用问题:没有当前样式仅对当前组件生效 容易造成页面样式污染 less sass也是一个很好的解决方案 ``` ### styled-components ``` 组件化的css 解决了传统css的 样式冲突 类名混乱 代码复用性低 代码冗余 等问题使用 1.下载 npm install --save styled-components 2.创建用于存放的文件夹于文件 3.编写代码 注意 暴露的样式必须必须大驼峰命名法 4.在需要的地方引用 使用 包裹需要设置样式的范围即可 ``` ```js import styled from "styled-components" export let ShopStyled=styled.div` div{ color:red; } ` ``` ```jsx import React, { Component } from 'react' import {ShopStyled} from "../../styled/components/shoped.js" export default class shop extends Component { render() { return (
我是shop的组件
我是外面的
) } } ``` ## React脚手架使用-多行标签 ``` 在react组件中 如果我们想写多行标签的话 必须要有一个容器包裹这些多行标签 传统方式 在多个标签的最外层加一个 div 这种方式会增加我们的垃圾冗余代码 空标签 方式1 不推荐 <></> 什么都没有的标签 方式2推荐Fragment 或者 React.Fragment,要使用先引用 ``` ```jsx import React, { Component,Fragment } from 'react' export default class user extends Component { constructor(props){ super(props) this.state={ text:"我是state的数据", arr:[11111,222222,333333,444444,555555] } } fun(){ this.setState({ text:"我被改了" }) } showlist=()=>{ return this.state.arr.map((v,i)=>{ return (
  • {v}
  • ) }) } render() { return (

    我是user1

    我是user2

    {this.state.text}

    ) } } ``` ## state ``` state状态的使用和本地模式没有区别 ``` ## props ``` props 正向传值来说 使用方式和本地模式没有区别 但是 他的props验证 可以使用快捷键 rccp即可快速创建出带有props验证的组件 ``` ## props验证,使用prop-types ```jsx import React, { Component } from 'react' import PropTypes from 'prop-types' export default class propsdemo extends Component { static propTypes = { prop: PropTypes } render() { return (
    ) } } //propsdemo组件 ``` ```jsx import React, { Component } from 'react' import Shop from "../shop/shop.jsx" import User from "../user.jsx" import Propsdemo from "../propsdemo.jsx" import Childrendemo from "../childrendemo.jsx" export default class home extends Component { constructor(props){ super(props) } render() { return (
    你好么么哒

    asdasdasdasdas1

    asdasdasdasdas2

    asdasdasdasdas3

    ) } } ``` ## this.props.children ``` 如果在组件调用的开标签和关标签之中写入内容是否会渲染呢? this.props对象的属性与组件的属性是一一对应的,但是有一个例外就是this.props.children属性,它表示 组件的所有子节点this.props.children的值有三种可能: 1.如果当前组件没有子节点,它就是undefined; 2.如果有一个子节点,数据类型是Object; 3.如果有多个子节点,数据类型就是array。 ``` ``` 使用this.props.children就可以得到组件的所有子节点 当然其中也可以传入组件使用this.props.children也可以得到 ``` ```jsx import React, { Component } from 'react' export default class childrendemo extends Component { render() { return (
    我是props.children的组件
      { this.props.children.map((v,i)=>{ return (
    1. {v}
    2. ) }) }
    ) } } //childrendemo组件 ``` ```jsx import React, { Component } from 'react' import Shop from "../shop/shop.jsx" import User from "../user.jsx" import Propsdemo from "../propsdemo.jsx" import Childrendemo from "../childrendemo.jsx" export default class home extends Component { constructor(props){ super(props) } render() { return (
    你好么么哒

    asdasdasdasdas1

    asdasdasdasdas2

    asdasdasdasdas3

    ) } } ``` # 函数的实参传递 ## 使用bind的方式进行实参的传递 ```jsx import React, { Component } from 'react' export default class home extends Component { fun(data){ console.log(data) } render() { return (

    脚手架环境下函数实参传递

    ) } } ``` ## 使用箭头函数的方式进行相关参数的传递 ```jsx import React, { Component } from 'react' export default class home extends Component { fun(data){ console.log(data) } render() { return (

    脚手架环境下函数实参传递

    ) } } ``` # forceUpdate()-强制刷新 ``` forceUpdate()就是重新调用render渲染。有些变量不在state上, 当时你又想达到这个变量更新的时候,刷新render; ``` ``` 传统的数据状态定义 就是把数据定义在react中的state上 那么我们修改数据的时候必须使用 this.setState来进行数据的修改如果我就是 不想把数据定在state之上 怎么进行修改 在脚手架中 如果我们想修改不在state上面的数据 同时还想进行页面的更新 那么调用forceUpdate()就是重新调用render渲染 ``` ```jsx import React, { Component } from 'react' export default class home extends Component { constructor(props){ super(props) this.state={ text:"我是state的数据" } this.xiaoming="我不是在state上的数据" } funb=()=>{ this.xiaoming="我被小改了" // 调用render渲染 this.forceUpdate() } render() { return (

    {this.xiaoming}

    ) } } ``` # 组件传值 ## props正向传值 ``` 正向传值 父----》子 使用props传递 但是注意 props的值是不能修改的 如果想修改的话 ``` ```jsx import React, { Component } from 'react' export default class propsdemo extends Component { render() { // render是一个生命周期的钩子 在初始化的时候render就会进行调用从而自动的进行解构 let {text}=this.props return (

    正向传值

    {text}

    {this.props.title}

    ) } } ``` ```js import Propsdemo from "./components/propsdemo.jsx" function App() { return (
    ); } export default App; ``` ## 逆向传值 ``` 默认不被允许 必须通过事件进行触发 1.就是需要使用事件来进行调用 2.使用props来接收一个参数 就是子组件需要的函数 并且在上面绑定bind把你的数据传递进去 3在父组件给子组件 使用props传递一个参数 这个参数需要是一个函数 4.在传递的这个函数的形参就是子组件的数据 ``` ```jsx import React, { Component } from 'react' export default class zi extends Component { constructor(props){ super(props) this.state={ zitext:"我是子组件的数据" } } render() { return (
    zizizizizziziziz---{this.state.zitext} {/* */}
    ) } } ``` ```jsx import React, { Component } from 'react' import Zi from "./zi.jsx" export default class fu extends Component { constructor(props){ super(props) this.state={ futext:"" } } // fun=( val就是子组件发送过来的数据 )=>{ // } fun=( val )=>{ console.log("子组件传递过的数据是"+val) this.setState({ futext:val }) } render() { return (
    fufufufufufufufufufufufufufu----{this.state.futext} {/* */}
    ) } } ``` ## 删除功能-正逆传值 ### 父组件 ```jsx import React, { Component } from 'react' import Zi from "./zi.jsx" export default class fu extends Component { constructor(props){ super(props) this.state={ arr:[ {name:"xixix11",age:181}, {name:"xixix12",age:182}, {name:"xixix13",age:183}, {name:"xixix14",age:184}, {name:"xixix15",age:185} ] } } fun=(val)=>{ console.log("子组件传递的数据"+val) let newarr=this.state.arr; newarr.splice(val,1) this.setState({ arr:newarr }) } render() { return (
    fufuffufuf { this.state.arr.map((v,i)=>{ return ( ) }) }
    ) } } ``` ### 子组件 ```jsx import React, { Component } from 'react' export default class zi extends Component { render() { let {name,age,xiabiao}=this.props return (

    名字是:{name}---年龄:{age}

    ) } } ``` ## 兄弟同胞传值-pubsub-js ``` 1.下载 npm install --save pubsub-js 2.需要在一个兄弟组件中进行抛出动作使用pubsub-js.publish( "抛出的事件名","你要抛出的数据" )自定义事件 3.接收抛出的自定义事件pubsub-js.subscribe(你要监听的事件名,(a 你监听的事件名,b 你传递过来的数据)=>{}) ``` ``` 同级传值使用pubsub-js 下载 npm install --save pubsub-js 在第一个组件中进行数据抛出 PubSub.publish("事件名","数据") 在第二个组件中接收 PubSub.subscribe("监听的事件",(事件,数据)=>{}) ``` ```jsx //a组件 import React, { Component } from 'react' import PubSub from "pubsub-js" export default class zia extends Component { fun=()=>{ // 抛出操作 PubSub.publish("pao","我是zia的数据") } render() { return (
    zia
    ) } } ``` ```jsx //b组件 import React, { Component } from 'react' import PubSub from "pubsub-js" export default class zib extends Component { // PubSub不能自动执行,所以在钩子函数里触发 componentDidMount(){ PubSub.subscribe("pao",(a,b)=>{ console.log(a)//就是监听的事件名 console.log(b)//就是传递过来的数据 }) } render() { return (
    zib
    ) } } ``` # 跨组件传值 ## 上下文对象context react 组件间传递数据是通过 props 向下,是单向传递的,从父级一层一层地通过 props 地向下传递到子子孙孙,有的时候我们组件一层一层的嵌套多层,这样这种方式一层一层传递麻烦,如果想跃层传递,这就会用到 context。 跨层级传值 ``` context:上下文对象 context很好的解决了跨组件传值的复杂度。可以快速的进行跨组件数据的传递。 想要使用context进行跨组件传值那么就要使用createContext()方法同时方法中给我们提供了两个对象: Provider对象 生产者---->用来生产数据 Consumer对象 消费者---->用来使用数据 ``` ## context使用 ### 先创建 ``` 在src下创建文件与文件夹用来容纳context对象 创建内容并且引用createContext对象 ``` ```jsx //创建一个context文件夹,在里面创建了一个index.js文件,文件里写的是jsx语法 import React, { Component,createContext } from 'react' class MyContext extends Component { render() { return (
    {this.props.children}
    ) } } export {MyContext,Consumer} ``` ### 在根组件中使用 ``` 根组件是src下的index.js,替换 ``` ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import {MyContext} from "./context/index.js" ReactDOM.render( // // // , , document.getElementById('root') ); reportWebVitals(); ``` ### 创建Provider生产者与Consumer消费者对象并且创建数据 ```jsx import React, { Component, createContext } from 'react' // 接收createContext,并且解构 let context=createContext() let {Provider,Consumer}=context class MyContext extends Component { render() { // value如果需要传递多个值,可以传递一个对象 return (
    {this.props.children}
    ) } } // 暴漏出去MyContext,在src下的index.js中解构 export{MyContext,Consumer} ``` ### 在需要的组件中进行使用 ```jsx import React, { Component } from 'react' import { Consumer } from "../../context" export default class zib extends Component { render() { return (
    zib

    在使用上下文对象

    { // val就是传递过来的数据 (val) => { return (
    我是上下文对象数据:{val}
    ) } }
    ) } } ``` # 样式 在设置样式类名的时候 千万注意要设置className # ref使用 ``` 在脚手架环境中使用createRef() ``` ```jsx import React, { Component,createRef} from 'react' export default class refdemo extends Component { constructor(props){ super(props) this.state={ refdemo:createRef() } } fun=()=>{ this.state.refdemo.current.style.color="red"; console.log(this.state.refdemo) } render() { return (

    我是用来写ref的组件

    ) } } ``` # React数据请求/交互 ``` React本身没有独有的获取数据的方式。(使用原生或者第三方的方式) React 组件的数据可以通过 componentDidMount 方法中的 Ajax 来获取,当从服务端获取数据时可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。 ``` ## 常用的ajax请求库 ``` jQuery 比较重,需要外部引用不建议使用 axios 轻量级,建议使用 fetch 原生函数,但老版本浏览器不支持 ``` ``` 假接口 http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187 ``` ## axios ``` 下载axios模块 npm install --save-dev axios 引用在根页面引用 import axios from "axios" 设置请求:axios.get/post ``` ### 拦截器 ``` 在src下创建util文件夹,新建一个js文件 ``` ```js import axios from "axios" // 创建axios 赋值给常量service const service = axios.create(); // 添加请求拦截器(Interceptors) service.interceptors.request.use(function (config) { // 发送请求之前做写什么 return config; }, function (error) { // 请求错误的时候做些什么 return Promise.reject(error); }); // 添加响应拦截器 service.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); export default service ``` ### 请求封装 ``` 在src下创建api文件夹,新建一个js文件 ``` ```js import service from "../util/service.js" export function getlink(url){ return new Promise((resolve,reject)=>{ service.request({ url, method:"get" }).then((ok)=>{ resolve(ok) }).catch((err)=>{ reject(err) }) }) } ``` ### 发送请求 请求可以放在 componentDidMount方法中,是在组件已经完全挂载到网页上才会调用被执行,所以可以保证数据的加载。此外,在这方法中调用setState方法,会触发重新渲染。所以,官方设计这个方法就是用来加载外部数据用的 在react16.0以后,componentWillMount可能会被执行多次。所以最好不要在此钩子中请求 ```jsx import React, { Component } from 'react' import {getlink} from "../api/getapi.js" export default class axiosdemo extends Component { componentDidMount() { getlink("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187").then((ok)=>{ console.log(ok) }).catch((err)=>{ console.log(err) }) } render() { return (

    我是测试axios的数据请求组件

    ) } } ``` ## jquery ``` 下载jquery模块 npm install --save-dev jquery 引用在根页面引用 import $ from "jquery" ``` ### 引用发送请求 ```jsx import React, { Component } from 'react' import $ from "jquery" export default class jquerydemo extends Component { componentDidMount() { $.ajax({ url:"http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187", type:"get", dataType:"json", success(ok){ console.log(ok) } }) } render() { return (

    jquery进行数据请求

    ) } } ``` ## fetch ---数据请求--es6 ``` XMLHttpRequest的最新替代技术——Fetch API, 它是W3C的正式标准 (xx.json转换成json字符串) ``` ### 发送post ```jsx fetch("地址", { method: "POST", headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: "key=val&key=val" }) .then(req=>req.json()) .then(function(ok) {console.log(ok)}) ``` ### 发送get ```jsx fetch("http://localhost:8888/get?key=val", { method: "get" }) .then(req=>req.json()) .then(function(ok) {console.log(ok)}) } ``` ## fetch VS ajax VS axios ``` 传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术, 隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话, 就会出现回调地狱。JQuery ajax 是对原生XHR的封装 axios 是一个基于Promise ,本质上也是对原生XHR的封装, 只不过它是Promise的实现版本,符合最新的ES规范, fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。 ``` # 跨域 ## 正向代理 开发环境是一个位于客户端和目标服务器之间的服务器(代理服务器),为了从目标服务器取得内容,客户端向代理服务器发送一个请求并指定目标,然后代理服务器向目标服务器转交请求并将获得的内容返回给客户端。 ## 反向代理 上线环境代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。 ## 正向代理配置 ``` 找到项目目录下/node_modules/react-scripts/config/webpackDevServer.config.js ``` 然后在其中找到proxy并修改成如下: ``` proxy:{ "/api(可以随便写)":{ target:"请求地址", changeOrigin:true, "pathRewrite":{ "^/api(和上面一样)":"/" } } }, ``` ```js proxy:{ "/api":{ target:"http://www.weather.com.cn/", changeOrigin:true, "pathRewrite":{ "^/api":"/" } } }, ``` 请求的时候修改请求地址 ```jsx getlink("/api/data/cityinfo/101320101.html").then((ok)=>{ console.log(ok) }).catch((err)=>{ console.log(err) }) } ``` # 弹射 解决跨域的时候配置文件隐藏过深非常麻烦 ,但是eject 操作是不可逆的,执行之后会把所有细节都暴露在我们面前,让项目目录变得很庞大 ``` 在项目目录执行:npm run eject 注意可能会报错 执行npm run eject报错如上,应该是没装好git,或者装了但是没提交到仓库 先执行 git add . 后执行 git commit -m "自己的提交信息" 在运行 npm run eject ``` 报错解决 ``` 把node_modules删除了 重新npm install ``` # json-server ## 下载 ``` 我们在开发中并不想使用简单的静态数据,而是希望自己起一个本地模拟请求以及请求回来的过程。json-server就是用来完成模拟数据的 下载:npm install json-server -g 查看版本: json-server --version ``` ## 创建数据 ``` 在src下创建一个mock文件夹,在里面创建一个json文件 ``` ```json { "user":[ {"name":"xixi1","age":181}, {"name":"xixi2","age":182}, {"name":"xixi3","age":183}, {"name":"xixi4","age":184}, {"name":"xixi5","age":185} ], "userb":{ "title":"我是第二个请求" }, "userc":{ "title":"我是第3个请求" } } ``` ## 启动 ``` json-server默认端口为3000 我们不能直接启动会和react脚手架冲突 所以我们启动的时候需要修改端口 1.cd 到mock文件夹路径下 2.在命令行输入 json-server --watch json的名字 --port 4000(端口号) 进行启动 3.在浏览器中测试一下 http://localhost:4000/数据的key ``` ``` 如果有多个数据直接写在对象里,运行后会有对应的接口地址 ``` # 路由 ``` 根据不同的url 来切换对应的组件 实现spa(单页面应用)应用: 整个项目只有一个完整页面 页面切换不会刷新页面(不会感觉页面的闪烁 更加贴近原声应用的体验) ``` ## 路由-分类 React-Router:提供了一些router的核心API,包括Router, Route, Switch等,但是它没有提供 DOM 操作进行跳转的API。 React-Router-DOM:提供了 BrowserRouter, Route, Link等 API,我们可以通过 DOM 的事件控制路由。例如点击一个按钮进行跳转,大多数情况下我们是这种情况,所以在开发过程中,我们更多是使用React-Router-DOM。 ## HashRouter 和 BrowserRouter -路由模式 ### HashRouter (hash模式) ``` url中会有个#,例如localhost:3000/#,HashRouter就会出现这种情况,它是通过hash值来对路由进行控制。如果你使用HashRouter,你的路由就会默认有这个#。刷新不会丢失 ``` ### BrowserRouter(历史记录模式 ) ``` 是通过历史记录api来进行路由的切换的很多情况下我们则不是这种情况,我们不需要这个#,因为它看起来很怪,这时我们就需要用到BrowserRouter。刷新会丢失404(上线中会出现问题 本地开发中不会) ``` # 路由基本使用 ``` React-Router-DOM模式 ``` ## 1-下载 ``` 下载路由模块 npm install --save react-router-dom ``` ## 2-在index.js引用路由模块 ``` 可以是:import { BrowserRouter} from 'react-router-dom'; 或者是:import { HashRouter} from 'react-router-dom'; ``` ### 路由模式 ``` HashRouter (hash模式) BrowserRouter(历史记录模式 ) ``` ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { BrowserRouter} from 'react-router-dom'; ReactDOM.render( , document.getElementById('root') ); reportWebVitals(); ``` ## 3-在index.js使用路由模式包裹根组件 ``` 如果想要改变路由模式,引入不同路由模式,包裹APP就行 import { BrowserRouter} from 'react-router-dom'; import { HashRouter} from 'react-router-dom'; ``` ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { BrowserRouter} from 'react-router-dom'; ReactDOM.render( , document.getElementById('root') ); reportWebVitals(); ``` ## 4-在src下新建路由文件夹(router) 与路由文件(index.js) ## 5-需要在原来的index.js全局文件中 把原来的app组件的引用替换成 刚才创建的路由文件 ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; //替换 import App from './router/index.js'; import reportWebVitals from './reportWebVitals'; import { BrowserRouter} from 'react-router-dom'; ReactDOM.render( , document.getElementById('root') ); reportWebVitals(); ``` ## 6-在路由文件index.js中配置 ``` 在路由文件index.js中 引用Route: import {Route} from "react-router-dom" Route的作用就是路由出口与路由规则的综合体 ``` ```jsx import React, { Component } from 'react' import {Route} from "react-router-dom" export default class index extends Component { render() { return (
    ) } } ``` ## 7-创建路由页面,在路由文件中配置引用 ``` src下新建一个 views/pages文件夹,里面创建页面组件 ``` ```jsx import React, { Component } from 'react' import {Route} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" export default class index extends Component { render() { return (
    ) } } ``` ## 8-路由导航 ### 声明式 ``` 使用Link组件即可 to属性就是地址 Link需要引用 ``` ```jsx import React, { Component } from 'react' import {Route,Link} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    ) } } ``` 也可以把路由导航封装在一个组件里,那个页面需要就在那个页面引用 ```jsx import React, { Component } from 'react' import {NavLink} from "react-router-dom" export default class topbar extends Component { render() { return (
    home   phone   shop  
    ) } } ``` ```jsx import React, { Component } from 'react' //引用路由导航组件 import Topbar from "../components/topbar.jsx" export default class home extends Component { render() { return (
    首页
    ) } } ``` ### 编程式 #### 基本使用 ``` this.props.history.push("/xxx") replace() 替换当前路径 goBack()后退 goForward()前进 ``` ``` 坑 相同的编程式导航 放在不同的组件页面中 有的时候出现错误,有的时候又是好的 因为不是被路由所管理的页面 如果写编程式导航 就会报push没有定义的错误,出现错误的原因是 如果我们在不是被路由所管理的页面中使用this.props.location /match /history是不存在的 所以会报错 如果是被路由所管理的页面 写编程式导航就不会报错 但是想让编程式导航在不是被路由的页面中进行使用的话 怎么办? 使用withRouter 这个高阶组件 就是让不是被路由所管理的页面也有this.props.location/ match /history这个属性 history 历史 location 当前 match 是一个匹配路径的对象 ``` ```jsx //在被路由管理的页面里使用编程式导航不会出错 import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" export default class home extends Component { fun(){ this.props.history.push("/phone") } render() { return (
    首页
    ) } } ``` #### 引用withRouter组件 ,解决不是被路由所管理的页面报错问题 ``` withRouter作用是让不是路由切换的组件也具有路由切换组件的三个属性(location match history) withTouter 让不是理由所跳转的页面也具有路由的三个属性 ``` ``` 引用withRouter组件 修改导航组件删除组件的暴漏 在最底部重新暴漏组件 ``` ```jsx import React, { Component } from 'react' //引用withRouter组件 import {Route,Redirect,Switch,withRouter} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" import No from "../views/no.jsx" //修改导航组件删除组件的暴漏 class index extends Component { fun(){ // 编程式导航 //console.log(this.props) this.props.history.push("/phone") } render() { return (
    ) } } //在最底部重新暴漏组件 export default withRouter(index) ``` # 多级路由 ## 二级路由 ### 第一步 ``` 先创建路由页面:比如有三个页面,分别对应: import Era from "./er/era" import Erc from "./er/erc" import Erd from "./er/erd" ``` ### 第二步 ``` 在子页面中引用路由模块 import {Route,Link} from 'react-router-dom'; 2.设置相关规则 与路由导航 最重要的就是在指定的一级路由中设置 Route 或者是路由导航 ``` ```jsx import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" import {Route,Link} from "react-router-dom" import Era from "./er/era" import Erc from "./er/erc" import Erd from "./er/erd" export default class shop extends Component { render() { return (
    购物
    era erc erd
    ) } } ``` # 路由进阶 ## 路由导航-NavLink ``` NavLink 这个组件 和link的功能大致相同都是用来继续路由页面的跳转 但是他有一些扩展功能 就是可以设置选中项的类名 方便添加样式 同时我们可以设置 activeClassName属性来修改选中的类名 注意: 如果在写navlink的时候没有自动添加类名 那么久需要你在启动项目的时候不要再vscode中启动 从外部cmd中启动项目即可 如果不喜欢默认的类名active 可以手动设置选中Class方便样式设置: activeClassName="xiaoming" ``` ````jsx import React, { Component } from 'react' import {Route,Link,NavLink} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    ) } } ```` ## exact 精准匹配 ``` 必须有path才行 exact代表当前路由path的路径采用精确匹配, 比如说Home的path如果不加上exact, 那么path="/about"将会匹配他自己与path="/“这两个, 所以一般path=”/"这个路由一般会加上exact, 另外需要注意一点的是嵌套路由不要加exact属性, 如果父级路由加上,这里例如topics加上该属性, 他下面的子路由将不会生效,因为外层强制匹配了。 ``` ```jsx ``` ```jsx import React, { Component } from 'react' import {Route,Link,NavLink} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    ) } } ``` ## 404页面 ``` 有的时候用户可能会错误修改相关url 但是并没有相关路由 解决方式设置404路由组件 但是有问题不管到那个页面都会有这个404路由组件 解决办法: switch 加 重定向 ``` ```jsx import React, { Component } from 'react' import {Route,Link,NavLink} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" import No from "../views/no.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    {/* 404路由规则的最下面 */}
    ) } } ``` ## Switch唯一渲染 ``` 为了解决route的唯一渲染,它是为了保证路由只渲染一个路径。 是唯一的,因为它仅仅只会渲染一个路径,当它匹配完一个路径后,就会停止渲染了。 ``` ```jsx import React, { Component } from 'react' import {Route,Link,NavLink,Switch} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" import No from "../views/no.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    {/* 404路由规则的最下面 */}
    ) } } ``` ## Redirect 重定向 ``` 导入Redirect import {Redirect } from 'react-router-dom' 定义重定向路径 ``` ```jsx import React, { Component } from 'react' import {Route,Link,NavLink,Switch,Redirect} from "react-router-dom" import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import Shop from "../views/shop.jsx" import No from "../views/no.jsx" export default class index extends Component { render() { return (
    点我去home 点我去phone 点我去shop
    {/* 设置路由重定向 */} {/* 404路由规则的最下面 */}
    ) } } ``` # 路由传参 ## params方式 ### 1-在 Router标签后面拼接传递参数的名字 ```jsx import React, { Component } from 'react' import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import User from "../views/user.jsx" import No from "../views/no.jsx" import {Route,Redirect,Switch,Link} from "react-router-dom" export default class index extends Component { render() { return (
    {/* 2-设置发送的数据 */} 点我去user并且传递参数 {/* 1-在 Router标签后面拼接传递参数的名字 */}
    ) } } ``` ### 2-设置发送的数据 ```jsx import React, { Component } from 'react' import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import User from "../views/user.jsx" import No from "../views/no.jsx" import {Route,Redirect,Switch,Link} from "react-router-dom" export default class index extends Component { render() { return (
    {/* 2-设置发送的数据 */} 点我去user并且传递参数 {/* 1-在 Router标签后面拼接传递参数的名字 */}
    ) } } ``` ### 3-在需要接收的路由组件中接收this.props.match.params.name ```jsx import React, { Component } from 'react' export default class user extends Component { render() { return (
    {/* 3-在需要接收的路由组件中接收this.props.match.params.name */}

    我是user---{this.props.match.params.xiaoming}

    ) } } ``` ### 传参params方式优缺点 ``` 优势 : 刷新地址栏,参数依然存在 缺点 : 只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。 ``` ## state方式 ### 1-在Link中设置发送的数据 ``` 点我去d ``` ```jsx import React, { Component } from 'react' import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import User from "../views/user.jsx" import No from "../views/no.jsx" import {Route,Redirect,Switch,Link} from "react-router-dom" export default class index extends Component { render() { return (
    {/*在Link中设置发送的数据 */} 点我去phone state传参
    ) } } ``` ### 2-在需要接收的路由组件中接收 ``` this.props.location. state.XXX ``` ```jsx import React, { Component } from 'react' export default class phone extends Component { render() { return (
    我是phone---{this.props.location.state.xiaohong}
    ) } } ``` ### state 优缺点 ``` 优势:传参优雅地址栏不显示传递的数据,传递参数可传对象; 缺点:刷新地址栏,参数丢失 ``` # 路由render渲染 ## 基本写法 ``` 修改Route 里面的组件调用方式为: render={(props)=>{return <组件/>}} render调用一个函数那么我们就可以决定什么时候渲染他 同时传入props那么就可以在路由组件中使用history: {…}, location: {…}, match: {…}这几个对象 ``` ```jsx //这是一个测试组件renderdemo import React, { Component } from 'react' export default class renderdemo extends Component { render() { return (

    我是用来测试render渲染的组件

    可以让路由在渲染之间 先进一重判断

    ) } } ``` ```jsx import React, { Component } from 'react' import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import User from "../views/user.jsx" import No from "../views/no.jsx" import Renderdemo from "../views/renderdemo.jsx" import {Route,Redirect,Switch,Link} from "react-router-dom" export default class index extends Component { render() { return (
    {/*路由render渲染*/} {return true?: }}>
    ) } } ``` ## 路由render渲染写法传递数据 ### 1-只需要向组件内部传递即可 正向传值 ``` {return true?: }}> ``` ### 2-取值--- this.props.xxx ``` this.props.text ``` # 性能优化 ## 小案例-点击变色 ### 父组件 ```jsx import React, { Component } from 'react' import Zi from "./zi.jsx" export default class fu extends Component { constructor(props){ super(props) this.state={ arr:[ {title:"标题1",style:false}, {title:"标题2",style:false}, {title:"标题3",style:false}, {title:"标题4",style:false}, {title:"标题5",style:false}, {title:"标题6",style:false} ] } } fun=(val)=>{ console.log(val) let newarr=this.state.arr; newarr[val].style=!newarr[val].style this.setState({ arr:newarr }) } render() { return (
    { this.state.arr.map((v,i)=>{ return ( ) }) }
    ) } } ``` ### 子组件 ```jsx import React, { Component } from 'react' export default class zi extends Component { render() { console.log("我是子组件") let {text,style,num}=this.props return (
    {text}
    ) } } ``` ### 问题 ``` 在子组件的render中添加一个输出 在运行的时候发现每次运行的时候子组件都被调用了多次 那么发现当前我们写的有严重的性能问题; 原因是因为 我们在更新数据的时候使用setState修改整个数据 那么数据变了 便利的时候所有内容都要被重新渲染。 数量少没有关系 如果便利出来的数据很多 那么就会严重影响我们的性能 ``` ## 提高性能--方案1 ``` 解决方式1 使用生命周期的: shouldComponentUpdate(nextProps最新的props,nextState最新的state) 判定组件是否要更新 ``` ```jsx import React, { Component } from 'react' export default class zi extends Component { shouldComponentUpdate(nextProps){ return nextProps.style!=this.props.style } render() { console.log("我是子组件") let {text,style,num}=this.props return (
    {text}
    ) } } ``` ## 提高性能--方案2 ``` 解决方式2 纯组件(PureComponent)--类组件中使用 PureComponent是优化react应用程序最重要的方法,组件发生更新时,组件的props和state没有改变,render方法就不会触发 .可以减少不必要的render次数,提高性能。 省去虚拟dom生成和对比的过程,其实就是react自动帮忙做了一个浅比较(它只比较props和state的内存地址,如果内存地址相同,则shouldComponentUpdate生命周期就返回false。) ``` ```jsx import React, { Component,PureComponent } from 'react' export default class zi extends PureComponent { render() { console.log("我是子组件") let {text,style,num}=this.props return (
    {text}
    ) } } ``` ## 提高性能--方案3 ``` 解决方式3 React.memo() -- 无状态组件中使用 类似于PureComponent,不同于React.memo是函数/无状态组件,React.PureComponent是类组件。memo依然是一种对象的浅比较 ``` # 高阶组件HOC - 在React组件的构建过程中,常常有这样的场景,有一类功能需要被不同的组件公用。可以复用在react组件中的代码与逻辑 - HOC--参数是组件同时返回值也是组件 这类组件我们叫做高阶组件(HOC) - 高阶组件的本质是高阶函数 比如js中的 map() filter()forEach...... - 高阶组件(HOC)是 React 中用于重用组件逻辑的高级技术。 HOC 本身不是 React API 的一部分。 它们是从 React 构思本质中浮现出来的一种模式。 - 例如封装一个功能在很多个组件中都想使用 就是每个组件都带一个版权信息 u ## 基本使用 ### 封装一个基本高阶组件 ```jsx import React, { Component } from 'react' let hoc=(Com)=>{//因为形参接收的是一个组件 所以首字母必须大写 return class index extends Component { render() { return (
    我是高阶组件的内容
    ) } } } export default hoc ``` ### 使用 ```jsx import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" //引入高阶组件 import hoc from "../hoc" //修改暴漏 class home extends Component { render() { return (
    首页
    ) } } //利用高阶组件暴漏 export default hoc(home) ``` ## 高阶组件HOC接受props ``` 如果组件是被高阶组件导出的 那么在正向传值的时候需要在高阶组件中进行传递 如果组件正常调用了高阶组件 那么他的父组件就没有办法正常传递props参数 我们需要在高阶组件中 使用{...this.props} 对参数进行传递设置 ``` ```jsx import React, { Component } from 'react' let hoc=(Com)=>{ return class index extends Component { render() { return (
    我是高阶组件的内容
    ) } } } export default hoc ``` ## 反向继承 ``` 本质就是HOC高阶组件的 条件渲染渲染劫持 根据我们传递过来的参数的不同来渲染不同的组件内容 反向继承最核心作用,是渲染劫持(拦截了渲染可以让我们进行条件渲染) super.render()是调用父类的render()渲染 在使用高阶组件,暴漏组件的时候需要传递参数 ``` ```jsx //反向继承高阶组件 import React, { Component } from 'react' let hocb=(Com,num)=>{ //名字要改成第一个形参的名字,不能使用Component return class indexb extends Com { render() { if(num<10){ return (
    参数小于10了
    ) }else{ return ( {/*渲染当前这个高阶组件的内容*/} ) } } } } export default hocb ``` ```jsx import React, { Component } from 'react' import hocb from "../hoc/indexb.js" class hocdemob extends Component { render() { return (

    我是用来测试高阶组件的反向继承的组件

    ) } } //在使用高阶组件,暴漏组件的时候需要传递参数 export default hocb(hocdemob,11) ``` ```jsx import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" import Hocdemob from "../components/hocdemob.jsx" export default class phone extends Component { render() { return (

    {/*调用使用高阶组件暴漏的组件*/} 手机
    ) } } ``` # redux ## redux是什么 ``` Redux是为javascript应用程序提供一个状态管理工具 集中的管理react中多个组件的状态 redux是专门作状态管理的js库(不是react插件库可以用在其他js框架中例如vue,但是基本用在react中) ``` ## 什么时候用redux ``` 需求场景: 某个组件的状态需要共享的时候 某个组件的状态需要在任何地方都可以拿到 一个组件需要改变全局状态 一个组件需要改变另一个组件的状态 ``` ## redux三大原则 - 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中 - State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。 - 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers(一些纯函数,它接收先前的 state 和 action,) ## redux常用概念 - Store:管理着整个应用的状态,可以通过getState()来重新获得最新的状态(state)。 - Action:是唯一可以改变状态(state)的方式,服务器的各种推送、用户自己做的一些操作,最终都会转换成一个个的Action,而且这些Action就是修改的动作,可以通过dispatch()方法来进行调用 - Reducer:Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。(纯函数就是只要传入相同的参数,每次都应返回相同的结果) ## redux常用方法 ``` createStore()作用:创建一个Redux store来存放应用中所有的state, 一个应用只能有个store。函数返回store对象。 getState()作用:获取数据 dispatch()分发action,这是改变state的唯一方法。 subscribe()添加一个变化监听器,每当改变store的时候就会执行 ``` ## redux基本使用 ### 1-下载创建 ``` 下载:npm install --save redux 建议:在项目中创建一个sotre文件夹,用来保存与redux相关的内容 ``` ### 2-在sotre中创建index.js ```js // 1.使用redux,先创建store对象 先引用 import {createStore} from "redux" // 5.创建数据 let xiaoming={ name:"xixi", age:18 } // 4.创建redecer reducer是一个纯函数 // state数据源 actions 等同于vuex的mustations就是修改的动作 let reducer=(state=xiaoming,actions)=>{ // 6return state数据 return state } // 2.创建store对象 3.向store对象中传递reducer let store=createStore(reducer) // 3.暴漏 export default store ``` ### 3-•在src下的index.js中使用 ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './router'; import reportWebVitals from './reportWebVitals'; import {BrowserRouter} from "react-router-dom" //使用store import store from "./store" console.log(store.getState().name) ReactDOM.render( , document.getElementById('root') ); reportWebVitals(); ``` ### 4-在组件中使用 ```jsx import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" import hoc from "../hoc" //引用store import store from "../store/index" class home extends Component { constructor(props) { super(props) this.state = { //读取数据并赋值给state text: store.getState().age } } render() { return (
    首页---{this.state.text}
    ) } } export default hoc(home) ``` ## redux修改操作 ### 1-在reducer中添加action任务 ```jsx // 1.使用redux,先创建store对象 先引用 import {createStore} from "redux" // 5.创建数据 let xiaoming={ name:"xixi", age:18 } // 4.创建redecer reducer是一个纯函数 // state数据源 actions 等同于vuex的mustations就是修改的动作 let reducer=(state=xiaoming,actions)=>{ // 6return state数据 // return state //修改 //actions.type 修改的任务名 //type是从页面传过来的名字,用actions打点接收 switch (actions.type) { case "ADD": console.log({...state,age:state.age+1}) //es6扩展运算符 return {...state,age:state.age+1} break; case "DEL": return {...state,age:state.age-1} break; default: return state break; } } // 2.创建store对象 3.向store对象中传递reducer let store=createStore(reducer) // 3.暴漏 export default store ``` ### 2-dispatch()方法来进行调用修改动作 /subscribe() 监听数据变化 ``` dispatch()方法来进行调用修改动作后 在页面点击按钮后,数据并没有修改(constructor是在初始化自动执行,redux数据改变了 但是组件并没有重新渲染) subscribe()订阅一个变化监听器,每当改变store的时候就会执行 ``` ```jsx import React, { Component } from 'react' import Topbar from "../components/topbar.jsx" import hoc from "../hoc" import store from "../store/index" class home extends Component { constructor(props) { super(props) this.state = { text: store.getState().age } store.subscribe(()=>{ this.setState({ text:store.getState().age }) }) } add=()=>{ // 派发动作的名字在store中,可以用actions打点 接收 store.dispatch({type:"ADD"}) } del=()=>{ store.dispatch({type:"DEL"}) } render() { return (
    首页---{this.state.text}
    ) } } export default hoc(home) ``` ## 使用actionCreator统一创建action ### 没有分派前 #### store的index.js ```js // 1 引用 import {createStore} from "redux" // 6 创建数据 let data={ num:9527 } // 5 创建reducer 7注入数据 let reducer=(state=data,actions)=>{ // return state //type是从页面传过来的,用actions打点接收 switch (actions.type) { case "NUM_DATA_ADD": // number是从页面传过来的,用actions打点接收 return {...state,num:state.num+actions.number} break; case "NUM_DATA_DEL": return {...state,num:state.num-actions.number} break; default: return state break; } } // 2 创建store对象 4 创建数据与修改动作 let store = createStore(reducer) // 3 暴漏 export default store ``` #### 页面引用 ```jsx import React, { Component } from 'react' import store from "../store" export default class phone extends Component { constructor(props){ super(props) this.state={ phonenum:store.getState().num } store.subscribe(()=>{ this.setState({ phonenum:store.getState().num }) }) } add=()=>{ // 派发动作的名字在store中,可以用actions打点 接收 store.dispatch({type:"NUM_DATA_ADD",number:10}) } del=()=>{ store.dispatch({type:"NUM_DATA_DEL",number:20}) } render() { return (
    手机---{this.state.phonenum}
    ) } } ``` ``` 今后会有很多的修改动作都直接在组件中的store.dispatch()创建,当项目比较复杂的时候会比较难以管理。 redux希望使用专门的工厂函数来创建action ``` ### 封装派发动作 ``` 把原来写在redux文件中的修改动作分离出去独立管理 ``` #### 1-创建一个js文件 封装派发动作 ```js // 用来存储派发动作 export let addaction=(number)=>{ return {type:"NUM_DATA_ADD",number} } export let delaction=(number)=>{ return {type:"NUM_DATA_DEL",number} } ``` #### 2-在页面引用 ```jsx import React, { Component } from 'react' import store from "../store" //引用封装派发动作的文件 import {addaction,delaction} from "../store/actionCreator.js" export default class phone extends Component { constructor(props){ super(props) this.state={ phonenum:store.getState().num } store.subscribe(()=>{ this.setState({ phonenum:store.getState().num }) }) } add=()=>{ // 派发动作的名字在store的actions中可以接收 store.dispatch(addaction(2)) } del=()=>{ store.dispatch(delaction(10)) } render() { return (
    手机---{this.state.phonenum}
    ) } } ``` ### 封装派发动作名(action任务名) ``` 在actionCreator.js与reducer中出现了两次type名 为了方便管理 我们对任务名也进行修改 ``` #### 1-创建一个js文件 封装任务名 ```js // 封装任务名 export const NUM_DATA_ADD="NUM_DATA_ADDA" export const NUM_DATA_DEL="NUM_DATA_DELA" ``` #### 2-在需要的地方使用 ```js //在封装派发动作的文件中引用 import {NUM_DATA_ADD,NUM_DATA_DEL} from "./actionType.js" // 用来存储我们的派发动作 export let addaction=(number)=>{ return {type:NUM_DATA_ADD,number} } export let delaction=(number)=>{ return {type:NUM_DATA_DEL,number} } ``` ```jsx // 1 引用 import {createStore} from "redux" // 在store中使用 import {NUM_DATA_ADD,NUM_DATA_DEL} from "./actionType.js" // 6 创建数据 let data={ num:9527 } // 5 创建reducer 7注入数据 let reducer=(state=data,actions)=>{ // return state // actions.type是从页面传过来的 switch (actions.type) { case NUM_DATA_ADD: // actions.number是从页面传过来的 return {...state,num:state.num+actions.number} break; case NUM_DATA_DEL: return {...state,num:state.num-actions.number} break; default: return state break; } } // 2 创建store对象 4 创建数据与修改动作 let store = createStore(reducer) // 3 暴漏 export default store ``` ### 封装reducer模块 ``` 今后当项目体积逐渐变大会出现一种情况。就是所有的操作全部写在一个reducer中,非常难以管理。那么在这个时候可以把组件自己的reducer单独剥离出来进行管理。 ``` #### 1-封装reducer模块的内容 ``` 创建一个reducer文件夹,存放不同的reducer模块文件, 把原本在store文件夹中reducer里面的内容放到刚创建的reducer文件夹的文件中 ``` ```js import {NUM_DATA_ADD,NUM_DATA_DEL} from "../actionType.js" let data={ num:9527,name:"xixi" } let phonereducer=(state=data,actions)=>{ switch (actions.type) { case NUM_DATA_ADD: return {...state,num:state.num+actions.number} break; case NUM_DATA_DEL: return {...state,num:state.num-actions.number} break; default: return state break; } return state } export default phonereducer ``` #### 2-合并 reducer ```js // 用来合并每个组件的reducer import phonereducer from "./reducers/phonereducer.js" import {combineReducers} from "redux" let reducer=combineReducers({ //拆分的reducers模块名 phonereducer }) export default reducer ``` #### 3-在index.js中引入合并后的reducer ```js // 1 引用 import {createStore} from "redux" import reducer from "./reducer.js" // import {NUM_DATA_ADD,NUM_DATA_DEL} from "./actionType.js" // // 6 创建数据 // let data={ // num:9527 // } // // 5 创建reducer 7注入数据 // let reducer=(state=data,actions)=>{ // // return state // // actions.type是从页面传过来的 // switch (actions.type) { // case NUM_DATA_ADD: // // actions.number是从页面传过来的 // return {...state,num:state.num+actions.number} // break; // case NUM_DATA_DEL: // return {...state,num:state.num-actions.number} // break; // default: // return state // break; // } // } // 2 创建store对象 4 创建数据与修改动作 let store = createStore(reducer) // 3 暴漏 export default store ``` #### 4-在页面组件使用要加模块名 ```jsx import React, { Component } from 'react' import store from "../store" import {addaction,delaction} from "../store/actionCreator.js" export default class phone extends Component { constructor(props){ super(props) this.state={ //使用数据要加模块名 phonenum:store.getState().phonereducer.num } store.subscribe(()=>{ this.setState({ //使用数据要加模块名 phonenum:store.getState().phonereducer.num }) }) } add=()=>{ // 派发动作的名字在store的actions中可以接收 store.dispatch(addaction(2)) } del=()=>{ store.dispatch(delaction(10)) } render() { return (
    手机---{this.state.phonenum}
    ) } } ``` # 闭环请求 ## 数据 ``` 使用json-server模拟数据 在命令行输入 json-server --watch json的名字 --port 4000(端口号) 进行启动 ``` ```js { "user":[ {"name":"xixi1","age":181}, {"name":"xixi2","age":182}, {"name":"xixi3","age":183}, {"name":"xixi4","age":184}, {"name":"xixi5","age":185} ] } ``` ## 网络请求模块 ### 拦截器-service .js ```js import axios from "axios" // 创建axios 赋值给常量service const service = axios.create(); // 添加请求拦截器(Interceptors) service.interceptors.request.use(function (config) { // 发送请求之前做写什么 return config; }, function (error) { // 请求错误的时候做些什么 return Promise.reject(error); }); // 添加响应拦截器 service.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); export default service ``` ### 请求模块-getlink ```js import service from "../util/service.js" export function getlink(url){ return new Promise((resolve,reject)=>{ service.request({ url, method:"get" }).then((ok)=>{ resolve(ok) }).catch((err)=>{ reject(err) }) }) } ``` ## reducers模块 ### 1-派发动作名文件--actionType.js ```js export const DATALIST_ARR_USER="DATALIST_ARR_USER" ``` ### 2-派发动作文件--actionCreator.js ```js import {DATALIST_ARR_USER} from "./actionType.js" export const updata=(data)=>{ return {type:DATALIST_ARR_USER,data} } ``` ### 3-封装reducer模块--homereducer.js ```js import {DATALIST_ARR_USER} from "../actionType.js" let data={ arr:[] } export let homereducer=(state=data,actions)=>{ switch (actions.type) { case DATALIST_ARR_USER: return {...state,arr:actions.data} break; default: break; } return state } ``` ### 4-合并封装的reducer模块文件--reducer.js ```js // 合并reducer的 // 引用合并的文件进来 import {homereducer} from "./reducers/homereducer.js" import {combineReducers} from "redux" // 开始合并 export let reducer=combineReducers({ homereducer }) ``` ### 5-主文件--index.js ```js import {createStore} from "redux" import {reducer} from "./reducer.js" let store=createStore(reducer) export default store ``` ## 组件中发送请求 ```jsx import React, { Component } from 'react' import {getlink} from "../api/getapi.js" import store from "../store" import {updata} from "../store/actionCreator.js" export default class home extends Component { constructor(props){ super(props) this.state={ arr:store.getState().homereducer.arr } } componentDidMount() { store.subscribe(()=>{ this.setState({ arr:store.getState().homereducer.arr }) }) getlink("http://localhost:4000/user").then((ok)=>{ console.log(ok) {/*把请求到的文件发送到reducer中*/} store.dispatch(updata(ok.data)) }) } render() { return (

    我需要从模拟数据里面请求数据 然后传递给redux中 并且最终展示在页面里

      { this.state.arr.map((v,i)=>{ return (
    • {v.name}-----{v.age}
    • ) }) }
    ) } } ``` # react-redux ``` redux与react的耦合度过于高。代码不够简洁(组件中出现了大量的store对象) 一个react的插件 专门用来简化react应用中使用redux ``` ## 下载 ``` npm install --save react-redux cnpm install --save react-redux ``` ## react-redux常用概念 ``` 组件:把 store 提供给其子组件 connect 高阶组件:链接 链接react组件和redux(组件状态要从redux中获取) ``` ## react-redux使用 ### 1--在index.js中 创建Provider ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/oneshop/fu.jsx'; import reportWebVitals from './reportWebVitals'; //引用"react-redux" import {Provider} from "react-redux" //引用store对象 import store from "./store" ReactDOM.render( //有路由包裹在路由外面,没路由直接替换 //第一个store是属性,第二个是引入的 , document.getElementById('root') ); reportWebVitals(); ``` ### 2--在需要使用数据的组件中引入 ```jsx import React, { Component } from 'react' // connect是一个高阶组件 import {connect} from "react-redux" export default class phone extends Component { render() { return (

    我是使用react-redux来进行数据的读取

    ) } } ``` ### 3--修改暴漏 ``` 因为connect是一个高阶组件,所以用高阶组件的方式来暴漏 ``` ```jsx import React, { Component } from 'react' // connect是一个高阶组件 import {connect} from "react-redux" class phone extends Component { render() { return (

    我是使用react-redux来进行数据的读取

    ) } } // 有两个(),因为connect是一个方法 当这个方法被调用的时候返回的才是一个高阶组件 export default connect()(phone) ``` ### 4--向connect里面传递数据 ``` 生成数据在props中 形参数是state的数据 当前函数必须return一个对象 ``` ```jsx import React, { Component } from 'react' // connect是一个高阶组件 import {connect} from "react-redux" class phone extends Component { render() { return (

    我是使用react-redux来进行数据的读取

    ) } } // 为什么要有两个() // connect是一个方法 当这个方法被调用的时候返回的才是一个高阶组件 export default connect( // 传递数据 state=>({state}) )(phone) ``` ### 5--使用数据 ```jsx {this.props.state.xxx} {this.props.state.reducer模块名.age} ``` ```jsx import React, { Component } from 'react' // connect是一个高阶组件 import {connect} from "react-redux" class phone extends Component { render() { return (

    我是使用react-redux来进行数据的读取

    {this.props.state.homereducer.age}

    ) } } // 为什么要有两个() // connect是一个方法 当这个方法被调用的时候返回的才是一个高阶组件 export default connect(state=>({state}))(phone) ``` # react-redux 修改数据 ## store文件夹中 ### 1-派发动作名文件--actionType.js ```js export const ADD_LIST_REDUX="ADD_LIST_REDUX" ``` ### 2--派发动作文件--actionCreator.js ```js import {ADD_LIST_REDUX} from "./actionType.js" export function add(num){ return {type:ADD_LIST_REDUX,num} } ``` ### 3--封装reducer模块--homereducer.js ```js import {ADD_LIST_REDUX} from "../actionType.js" // 中多个reducer的模块其中一个 let data={ name:"xixi", age:18 } export let homereducer=(state=data,action)=>{ switch (action.type) { case ADD_LIST_REDUX: return {...state,age:state.age+action.num} break; default: return state break; } } ``` ### 4-合并封装的reducer模块文件--reducer.js ```js // 引用所有的reducer模块 import {homereducer} from "./reducers/homereducer.js" // 引用合并reducer的功能 import {combineReducers} from "redux" let reducer = combineReducers({ homereducer }) export default reducer ``` ### 5-主文件--index.js ```js import {createStore} from "redux" import reducer from "./reducer.js" let store=createStore(reducer) export default store ``` ## 组件内修改数据 ### 组件中修改 ``` this.props.dispatch(派发动作) ``` ```jsx import React, { Component } from 'react' // connect是一个高阶组件 import {connect} from "react-redux" //引入派发动作 import {add} from "../store/actionCreator.js" class phone extends Component { add=()=>{ this.props.dispatch(add(2)) } render() { return (

    我是使用react-redux来进行数据的读取

    {this.props.state.homereducer.age}

    ) } } // 为什么要有两个() // connect是一个方法 当这个方法被调用的时候返回的才是一个高阶组件 export default connect(state=>({state}))(phone) ``` # immutable不可变对象 Facebook 工程师使用3年时间打造,与React同期出现,但是没有被默认放到React工具集中,它内部实现了一套完整的数据持久化 里面有很多常见的数据类型Collection List Map Set等 ## 主要有三种数据结构 ``` Map: 键值对集合,对应于Object Es6中也有专门的Map对象 List: 有序可以重复的列表,对应于Array set: 无序并且不可重复key的数组 ``` ## 作用 ``` 主要完成的就是数据的持久化 ``` ## immutable原理 ``` Immutable 实现的原理是(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免深拷贝把所有节点都复制一遍带来的性能损耗,Immutable 使用了(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。 ``` ## immutable介绍 ``` Immutable Data就是一但创建,就不能在被改变的数据,对于immutabe对象的任何修改或者添加删除操作都会返回一个新的immutable对象 为什么每次都要返回一个新的immutable对象呢? 因为redux中数据是只读的 如果任意一个使用的位置都可以直接修改redux中的数据 那么可能会影响其他位置引用的内容,造成现实错误 immutable对象一旦发生了改变 就会返回一个新的immutable对象 ``` ## 下载 ``` npm install --save immutable cnpm install --save immutable ``` ## 传统对象修改 ```html ``` ## immutable-Map ``` 键值对集合 ``` ### 创建map对象 ```js let {Map}=require("immutable") // Map格式对应的是传统的obj对象 // 1.创建 let demo=Map({ name:"xixi", age:18 }) ``` ### 读取get() ```js let {Map}=require("immutable") // Map格式对应的是传统的obj对象 // 1.创建 let demo=Map({ name:"xixi", age:18 }) // 读取要使用get()方法进行读取 console.log(demo.get("name")) ``` ### 修改set() ```js let {Map}=require("immutable") let demo=Map({ name:"xixi", age:18 }) // 修改 使用set(你要修改的key,修改的值) // immutable对象一旦发生了改变 就会返回一个新的immutable对象 let newdemo=demo.set("name","haha") console.log(demo.get("name")+"-----"+newdemo.get("name")) ``` ### 删除一个delete () ```js let {Map}=require("immutable") let demo=Map({ name:"xixi", age:18 }) //只能删除一个 let newdemo=demo.delete("name") console.log(newdemo.get("name")) ``` ### 删除多个deleteAll () ```js let {Map}=require("immutable") let demo=Map({ name:"xixi", age:18 }) // 删除多个 let newdemo=demo.deleteAll(["name","age"]) console.log(newdemo.get("age")) ``` ### 清空clear() ```js let {Map}=require("immutable") let demo=Map({ name:"xixi", age:18 }) let newdemo=demo.clear() ``` ### 合并merge() ```js let {Map}=require("immutable") let a=Map({ name:"黄璜" }) let b=Map({ sex:"男" }) let newdemo=a.merge(b) console.log(newdemo.get("sex")) ``` ### 转换 ``` Map: 键值对集合。 toJS把Map转换成原生的Object,深转换(无论有多少层级都会转换) toJSON/toObject把Map转换成原生的Object,浅转换(只转换第一层) ``` ```js let {Map}=require("immutable") let a=Map({ b:Map({ c:Map({ text:"你好么么哒" }) }) }) //toJS() 深转换 let newdemob=a.toJS() console.log(newdemob) // toJSON()/toObject() 浅转换(只转换第一层) let newdemo=a.toJSON() console.log(newdemo) ``` ### toArray ()转换成数组 浅转换 ```js let {Map}=require("immutable") let demo=Map({ name:"xixi", age:18 }) let newdemo=demo.toArray() ``` ## immutable-List ``` 有序可以重复的列表,对应于Array ``` ### 创建List ```js let {List}=require("immutable") // 1.创建 let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); // 第二种方式List.of() let arr1=List.of(1,2,3,4) ``` ### size获取list长度 ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); console.log(arr.size) ``` ### set(下标,值)用于设置指定下标的值 ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.set(1,"我被修改了") let newarr=arr.set(10,"我被修改了")//可以大于数组长度 let newarr=arr.set(-1,"我被修改了")//负数从右往左 ``` ### delect(下标)删除指定下标 ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.delect(1,) let newarr=arr.delect(-1,)//负数从右往左 ``` ### insert()用来更新指定下标的值 ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.insert(1,"我被修改了") let newarr=arr.insert(-1,"我被修改了")//负数从右往左 ``` ### update(下标,回掉函数)用于更新指定下标的值 ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.update(1,x=>x+"更新了") ``` ### clear()清空并且返回一个空list ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.clear() ``` ### setSize()重新设置数组长度 ``` setSize()重新设置数组长度,小于原始list会被截取 ,大于会用undefined填充 ``` ```js let {List}=require("immutable") let arr=List(["aaaa","bbb","ccc","ddd","eee","fff"]); let newarr=arr.setSize(2) let newarr=arr.setSize(10) ``` ### 其他 ``` push pop unshift shift 和数组方法项功能相同 concat() 把多个list拼接成一个list,类似数组拼接 merge()是concat()别名,和concat()作用一样 ``` # immutable在react中使用 ```jsx import React, { Component } from 'react' //引用子组件 import Zi from "./zi.jsx" import {Map} from "immutable" export default class fu extends Component { constructor(props){ super(props) this.state={ list:Map({}) } } add(){ console.log(this.inputa.value+"----"+this.inputb.value) // this.state.list[this.inputa.value]=this.inputb.value let newlist=this.state.list.set(this.inputa.value,this.inputb.value) this.setState({ // list:this.state.list list:newlist },()=>{ console.log(this.state.list) }) } render() { return (

    low的一匹的购物车

    low的一匹的购物车

    商品名: {this.inputa=input}}/>
    数量: {this.inputb=input}}/>
    ) } } ``` ```jsx import React, { Component } from 'react' export default class zi extends Component { render() { let {zilist}=this.props return (

      {/* 如何遍历对象 */} { Object.keys(zilist.toJS()).map((v)=>{ return (
    • 商品名{v}----数量:{zilist.get(v)}
    • ) }) }
    ) } } ``` # typescript + react ## 什么是TypeScript TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上TypeScript扩展了JavaScript的语法解决JavaScript的“痛点”:弱类型和没有命名空间,导致很难模块化。 TypeScript是js的超集,可以编译为纯js Ts支持es6的标准,是微软开发的,是开源的,可以在任何操作系统上运行 ## 为什么要用TypeScript ### 开源 ### 简单 TypeScript 是 JavaScript 的超集,这意味着他支持所有的 JavaScript 语法。 ### 兼容性好 TScript 是 JS的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript 代码。由于最终在浏览器中运行的仍然是 JS,所以 TScript 并不依赖于浏览器的支持,也并不会带来兼容性问题。任何现有的JS程序可以不加改变的在TScript下工作。 ## 与js相比的优势 ``` TypeScript工具使重构更变的容易、快捷。 TypeScript 引入了 JavaScript 中没有的“类”概念。 TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中 类型安全功能能在编码期间检测错误,这为开发人员创建了一个更高效的编码和调试过程。 ``` ## 安装 ``` node //服务器 TypeScript解释器 npm install -g typescript //-g 全局安装 ,在本机上 哪里都能用 tsc 空格 -v //命令用来测试是否安装成功 ``` ## 文件声明与使用 ``` 文件后缀 :文件名称.ts 每次修改.ts文件都需要编译 使用命令: tsc 文件名 会自动生成xxx.js文件 如果想使用必须在HTML文件中使用script标签引用新生成的xxx.js文件 ``` ## 变量 ``` 创建变量的时候需要指定数据类型:let num:number=123; ``` ``` let/var 变量名称:数据类型 = value let(块变量)和const(常量)是JavaScript里相对较新的变量声明方式。 注意:let变量不能重复声明 注意:const它拥有与 let相同的作用域规则,但是不能对它们重新赋值。 注意:除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格 ``` ## 数据类型 ### 普通的 ```js // 数据类型 let text:string="我是一个字符串";//单引号双引号都是可以的 let num:number=123; let bool:boolean=false; console.log(text+"---"+num+"----"+bool) ``` ### 数组 ``` 数组 :number[]或 :Array ``` ```js // 创建数组 let arr:number[]=[11,222,333,444,555] let arrb:Array=[99,88,77,66,55] ``` ### 枚举 enum ``` 枚举 enum 类型是对JavaScript标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字。枚举表示的是一个命名元素的集合值 没有加"" 默认情况下,从0开始为元素初始值。 你也可以手动的指定成员的数值。 枚举:用于定义数值的集合 ``` ```js enum user {xiaoming,xiaohong,xiaohuang} enum user {xiaoming="小明",xiaohong="小红",xiaohuang="小黄"} console.log(user.xiaoming+"---"+user.xiaohong) ``` ### Any任意类型 ``` Any任意类型 在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any类型来标记这些变量 ``` ```js let text:any=true ``` ### 元组 ``` 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同(赋值的顺序不能变) 表明一直元素数量和类型的数组各个元素的类型可以不同 但是对应的位置必须相同 ``` ```js let demo:[string,number,boolean]=["字符串",1,true] console.log(demo[0]) ``` ### never ``` never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。 ``` ### Void ``` Void没有任何类型 某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void 声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null: ``` ## 解构/析构 ``` 解构。结构是一种打破数据结构,将其拆分为更小的部分的过程 从对象和数组中获得特定的数据并且赋值给变量,编写出了很多同质化的代码。 ``` ### 解构/析构--对象 ```js let o = { a:"aa", b:"bb", c:"cc" } let {a,b} = o ``` ``` 如果我们要把一个对象当中的值以此赋值给多个变量那么我们可以使用对象解构 a和b是声明的变量,也是从obj对象读取响应值的属性名称(变量名和属性名必须相同) ``` ### 解构/析构--数组 #### 1-如果我们要把一个数组当中的值以此赋值给多个变量那么我们可以使用对象解构 ```js var aa:string[]=["aa","bb","ccc"] function fun(){ console.log(aa[1]); } ``` #### 2-解构作为参数传递 ```js var arr:string[]=["aa","bb","ccc"] function fun([a,b,c]=arr){ console.log(a) } fun() ``` #### 3-忽略数组中某些元素(只需要把忽略的位置空缺就好) ```js let arr:number[]=[1,2,3,4,5]; let [a,,c,d,e]=arr; console.log(c); ``` ## 展开 ``` 展开和解构正好相反,将一个数组展开为另一个数组,或将一个对象展开为另一个对象。 ``` ### 数组展开 ```js let arra:number[]=[1,2,3]; let arrb:number[]=[4,5,6]; let arr:number[]=[...arra,...arrb,100]; console.log(arr); ``` ### 对象展开 ```js var obj={ name:"xixi", age:19 } var newobj={...obj,sex:"男"} console.log(newobj.name) ``` ## 函数 ### 函数分类 ``` 函数的分类 JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。 ``` ```js // 命名函数 function 函数名称(参数1,参数2):number{//返回值类型number return 111;返回 } // 匿名函数 var 函数名称 = function(参数1,参数2):number{ } // 注意:参数类型必须与定义一致 // 返回值类型与函数类型一致 ``` 命名函数与匿名函数 可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常省略它。 ```js function test(x:number,y:number):number{ let a=x+y; return a } var sum = text(3,3); console.log(sum); ``` ### 带有默认值的参数 ```js function test3(name="小伙",love="钱"){ var s:string = name+"喜欢"+love; return s; } ``` ### 可变参数 当函数中的参数不确定个数的时候使用可变参数 ```js // 可变参数---->数组(集合)传参 function text(one:string,...arr:string[]){ var str = one+"-----"+arr.join("-"); return str; } var atr4 = test("aa","aaa","hhh","jl") ``` ```js // 函数 function fun ():void{ console.log("函数") } function funb():string{ return "我是字符串的返回值" } // 函数形参默认值 function func(num="我是默认值"):void{ console.log(num) } func() ``` ## 接口 ``` 接口:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用 使用interface关键字定义 接口一般首字母大写 有的编程语言中会建议接口的名称加上 I 前缀 接口使用:使用:号接口名来进行使用 注意:定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的。赋值的时候,变量的形状必须和接口的形状保持一致。 可选属性:可选属性的含义是该属性可以不存在 有时候不要完全匹配一个接口,那么可以用可选属性。使用?号 ``` ``` interface IProps{ text:string, } ``` ## ts与react ### 创建项目 ``` 创建新工程 create-react-app 3.x.x 创建项目:create-react-app my-app --typescript ``` ``` 创建新工程create-react-app 4.x.x 最新 如果不是最新的会报错 解决方式卸载掉原有的create-react-app npm uninstall -g create-react-app 重新全局下载create-react-app 创建项目 create-react-app 项目名 --template typescript ``` ``` 创建项目:npx create-react-app 项目名 --template typescript 在 npm version >= 5.2.0 开始,自动安装了npx。 npx 这条命令会临时安装所依赖的环境。命令完成后原有临时下载的会删掉,不会出现在 全局中。下次再执行,还是会重新临时安装。不用全局安装,不用担心长期的污染。 也就是说 npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装 ``` ### 文件结构 ``` tsconfig.json包含了工程里TypeScript特定的选项。 tslint.json保存了代码检查器,TSLint将要使用的设置。 package.json包含了依赖,还有一些命令的快捷方式,如测试命令,预览命令和发布应用的命令。 public包含了静态资源如HTML页面或图片。除了index.html文件外,其它的文件都可以删除。 src包含了TypeScript和CSS源码。index.tsx是必须的入口文件 ``` ### 组件创建 ``` 创建一个tsx后缀名的文件 在需要的位置引用使用 但是引用的时候可能会出错 可能出现一下错误 在引用时候不要加后缀名 ``` ## 数据传递 ### 父传子 ``` 在进行数据传递的时候要使用 interface接口对类型限制 在子组件中进行限制设定 ``` ```jsx //子组件 import React, { Component } from 'react' interface IProps{ text:string, } export default class zi extends Component { render() { return (

    我是子组件---{this.props.text}

    ) } } ``` ```jsx //父组件 import React, { Component } from 'react' import Zi from "./zi" export default class home extends Component { render() { return (

    我是ts的组件

    ) } } ``` ``` 在接口中定义的内容必须全部传递不能只定义不传值 ``` ```jsx import React, { Component } from 'react' // 定义的接口默认情况下都要进行使用 // 但是如果我定义的接口数据我不知道他是不是要是用怎么办? // 就是在定义接口数据的时候使用? 来表示可选参数 interface IProps{ text:string, num?:number } export default class zi extends Component { render() { return (

    我是子组件---{this.props.text}

    ) } } ``` ### 子传父 ```jsx //父组件 import React, { Component } from 'react' import Zi from "./zi" interface IState{ text:string } export default class fu extends Component<{},IState> { public constructor(props:any){ super(props) this.state={ text:"" } } public fun=(val:any)=>{ console.log(val) this.setState({ text:val }) } render() { return (

    我是接收逆向传值的父组件--{this.state.text}

    ) } } ``` ```jsx //子组件 import React, { Component } from 'react' interface IProps{ demofun:any } export default class zi extends Component { public fun=()=>{ this.props.demofun("我是子组件的数据!!") } render() { return (

    我是给父组件进行逆向传值

    ) } } ``` ## 状态 ### 定义状态 ``` 直接定义状态会出现问题,因为没有设置数据类型 所以定义状态必须定义接口类型 ``` ```jsx import React, { Component } from 'react' import Zi from "./zi" interface IState{ num:number } // 组件在接收接口的时候 第一位是props的 第二位才是state的,如果是空,可以加个空对象 export default class home extends Component<{},IState> { public constructor(props:any){ super(props) this.state={ num:123 } } public render() { return (

    我是一个ts的组件


    {this.state.num}

    ) } } ``` ### 修改 ```jsx import React, { Component } from 'react' import Zi from "./zi" interface IState{ num:number } // 组件在接收接口的时候 第一位是props的 第二位才是state的 export default class home extends Component<{},IState> { public constructor(props:any){ super(props) this.state={ num:123 } } public fun=()=>{ this.setState({ num:9527 }) } public render() { return (

    我是一个ts的组件


    {this.state.num}

    ) } } ``` ## 请求数据 ``` http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187 ``` ```jsx import React, { Component } from 'react' interface IState{ data:any } export default class fetchdemo extends Component<{},IState> { public constructor(props:any){ super(props) this.state={ data:[] } } componentDidMount() { fetch("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187") .then(res=>res.json()) .then((ok)=>{ console.log(ok.data.commentList) this.setState({ data:ok.data.commentList }) }) } render() { return (

    请求数据

      { //第一次有可能是空的,判断length,有数据再遍历 this.state.data.length!=0? this.state.data.map((v:any,i:any)=>{ return (
    • {v.commentTxt}
    • ) }):

      请稍等

      }
    ) } } ``` # umiJS ``` Umi 是蚂蚁金服的底层前端框架 中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备。同时有强大的插件扩展各种功能 umi可以更好的组织路由 放到配置文件中统一管理 ``` ## 什么时候不用 umi ``` 需要支持 IE 8 或更低版本的浏览器 需要支持 React 16.8.0 以下的 React 需要跑在 Node 10 以下的环境中 有很强的 webpack 自定义需求和主观意愿 需要选择不同的路由方案 ``` ## 不同 ``` create-react-app 是基于 webpack 的打包层方案,包含 build、dev、lint 等,他在打包层把体验做到了极致,但是不包含路由,不是框架,也不支持配置。如果想基于他修改部分配置,或者希望在打包层之外的事情,就会遇到困难。 ``` ## 使用 ``` 确保电脑上有nodejs 10.13 或以上 ``` ## yarn ### 下载 ``` npm install -g yarn ``` ### yarn与npm ``` 初始化 : yarn init npm init 安装依赖: yarn install 或者 yarn npm install 新增依赖: yarn add xxx npm install xxx --save 删除依赖 : yarn remove xxxx npm uninstall xx --save 全局安装全局: yarn global add xxx npm install xx -g 同时下载多个: yarn add xxx xxx npm install --save xxx xxx ``` ### yarn更换镜像源 ``` yarn config set registry https://registry.npm.taobao.org ``` ## umi下载安装 ``` npm install -g umi / yarn global add umi 全局安装 查看版本 umi -v 安装项目 npm或yarn create @umijs/umi-app 安装依赖 cnpm install 或 yarn 启动项目 npm 或 yarn start 运行 http://localhost:8000 ``` ## HTML模板 ``` 修改默认模板 新建 src/pages/document.ejs,umi 约定如果这个文件存在,会作为默认模板 作用:如果要引用第三方js/css文件 可以直接在HTML模板中进行配置。有很多第三方的插件都没有react版本的 所以采用import的方式是没有办法进行引用的 也就只能采取上面的方式进行引用 ``` ```html Document ``` ## 页面创建 ``` umi g命令创建页面或者组件 在项目根路径下执行 umi g page xxx(页面名)或umi g page xxx/xxx umi g page xxx--typescript --less 创建ts页面与less 也可以自己创建react 类组件 ``` ## 路由 在 Umi 中,应用都是单页应用,页面地址的跳转都是在浏览器端完成的,不会重新请求服务端获取 html,html 只在应用初始化时加载一次。所有页面由不同的组件构成,页面的切换其实就是不同组件的切换,你只需要在配置中把不同的路由路径和对应的组件关联上。 ## 配置路由 ``` 在配置文件.umirc.ts中通过 routes 进行配置,格式为路由信息的数组。 path:路径 component:组件路径 @代表src路径 exact:路径精准匹配 ``` ```jsx import { defineConfig } from 'umi'; export default defineConfig({ nodeModulesTransform: { type: 'none', }, routes: [ { path: '/', component: '@/pages/index' }, { path: '/home', component: '@/pages/home' }, { path: '/phone', component: '@/pages/phone' }, ], fastRefresh: {}, }); ``` ### title:当前页面标题 ## 页面跳转 在 umi 里,页面之间跳转有两种方式:声明式和命令式。 ### 声明式 通过 Link 使用,通常作为 React 组件使用 ```jsx import React, { Component } from 'react' import {Link} from "umi" export default class home extends Component { fun=()=>{ history.push("/phone") } render() { return (
    home 点我去phone
    ) } } ``` ### 命令式 /编程式 通过 history 使用,通常在事件处理中被调用。 ```jsx import React, { Component } from 'react' import {history} from "umi" export default class home extends Component { fun=()=>{ history.push("/phone") //返回上一个路由 // history.goBack() } render() { return (
    home
    ) } } ``` ## 重定向使用redirect ```jsx import { defineConfig } from 'umi'; export default defineConfig({ nodeModulesTransform: { type: 'none', }, routes: [ {exact:true,path:"/",redirect:"/index"}, { path: '/index', component: '@/pages/index',title:"首页" }, { path: '/home', component: '@/pages/home' ,title:"home" }, { path: '/phone', component: '@/pages/phone',title:"phone" }, ], fastRefresh: {}, }); ``` ## 二级路由 ``` routes:配置路由的子路由 然后在 一级路由中 中通过 props.children 渲染子路由 ``` ```jsx import { defineConfig } from 'umi'; export default defineConfig({ nodeModulesTransform: { type: 'none', }, routes: [ {exact:true,path:"/",redirect:"/home"}, { path: '/index', component: '@/pages/index',title:"首页" }, { path: '/home', component: '@/pages/home' ,title:"home" }, { path: '/phone', component: '@/pages/phone', title:"我是phone", routes:[ {path:"/phone/era",component:"@/pages/er/era"}, {path:"/phone/erc",component:"@/pages/er/erc"}, ] }, fastRefresh: {}, }); ``` ```jsx import React from 'react'; import styles from './phone.css'; import {Link} from "umi" export default function Page(props) { return (

    Page phone

    era    erc    {props.children}
    ); } ``` ## 约定式路由 除配置式路由外,Umi 也支持约定式路由。约定式路由也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。 如果没有 routes 配置,Umi 会进入约定式路由模式,然后分析 src/pages 目录拿到路由配置。 但是实际上你写了文件之后,手动在浏览器地址栏输入路由没有发生跳转 原因: 使用脚手架搭建的项目会在配置文件中对路由进行配置。 解决删除.umirc.ts路由配置文件 ## 约定式路由--动态路由 ``` 约定 [] 包裹的文件或文件夹为动态路由。 ``` ```jsx //接收 import React, { Component } from 'react' export default class xiaoming extends Component { render() { return (

    我是约定是路由接收参数--{this.props.match.params.xiaoming}

    ) } } ``` ```jsx import React, { Component } from 'react' import {Link} from "umi" export default class home extends Component { render() { return (
    home {/*发送参数*/} 点我去user并且传递参数
    ) } } ``` # Hook React Hooks是React 16.8.0版本推出的新特性 主要的作用就是让无状态组件 可以使用状态和react的其他特性。(在react开发中状态的管理是必不可少的 以前为了进行状态管理需要使用类组件或者redux等来管理) Hook在class中没有作用 ``` 传统的无状态组件/函数组件 不能使用state状态 也不能使用生命周期等内容 hook 就是让无状态组件可以使用状态等react的其他特性 这个是react16.8新增一个特性 ``` ## useState() ``` useState()就是React提供最基础、最常用的Hook,主要用来定义和管理本地状态。 useState返回的是一个数组(长度为2),数组的第一个对象表示当前状态的值, 第二个对象表示用于更改状态的函数,类似于类组件的setState。 let [val(当前状态的值),setVal(改变状态的函数)]=useState(当前状态的初始值) ``` ``` useState 在使用的时候必须创建一个无状态组件 也就是函数组件 函数组件的首字母大写首字母大写首字母大写首字母大写 创建: useState() 返回的是一个数组 使用的时候 let [变量名(获取数据的时候使用的),修改的方法名(修改数据的时候使用的)]=useState(初始化数据) 如果想创建多个状态数据 那么有两种写法 1.多次创建 2.传递一个对象进行创建 ``` ### useState使用 ```jsx import React, { Component ,useState} from 'react' let Demoa=()=>{ let [num,setNum]=useState(9527) return (

    我使用来测试usestate的hook组件

    使用useState的数据-----{num}

    ) } export default Demoa ``` ### useState修改 ```jsx import React, { Component ,useState} from 'react' let Demoa=()=>{ let [num,setNum]=useState(9527) let del=()=>{ setNum(num-1) } return (

    我使用来测试usestate的hook组件

    使用useState的数据-----{num}

    {/*修改*/}
    ) } export default Demoa ``` ### useState多个状态 #### 1-对象类型 ```jsx let[num,setNum]=useState({ num1:1, num2:2, num3:3 }) ``` #### 2-多次声明 ```jsx let [num,setNum]=useState(9527) let [num1,setNum1]=useState(9528) let [num2,setNum2]=useState(9529) ``` ## useRef() ``` hooks中可以通过 useRef()获取Dom节点 ``` ```jsx import React, { Component,useRef } from 'react' let Demob=()=>{ //创建 let u=useRef(null) let fun=()=>{ //使用 console.log(u.current.value) } return (

    我是来测试useRef的组件

    {/*绑定*/}
    ) } export default Demob ``` ## useEffect() ``` useEffect:函数组件中没有生命周期,那么可以使用 useEffect 来替代。 可以把 useEffect Hook 看做 componentDidMount, componentDidUpdate, componentWillUnmount, 这三个函数的组合。 ``` ```jsx import React, { Component,useEffect } from 'react' let Democ=()=>{ useEffect(()=>{ console.log("我是useEffect自动调用了") document.title="我修改标题" }) return (

    我是用来测试useEffect的组件

    ) } export default Democ ``` ## useReducer () ``` useReducer这个跟redux一点关系都没有…它的主要作用是防止state里面的数据操作太复杂。减少state的数据操作复杂度 ``` ```jsx import React, { Component,useReducer } from 'react' let Demod=()=>{ let reducer=(state,action)=>{ switch (action.type) { case "ADD": return state+1 break; case "DEL": return state-1 break; default: return state break; } } // 创建 9527是初始化值 //state当前状态值 //dispatch 修改操作,dispatch一个对象 let [state,dispatch] = useReducer(reducer,9527) let add=()=>{ dispatch({type:"ADD"}) } let del=()=>{ dispatch({type:"DEL"}) } return (

    我是用来测试useReducer的组件

    {state}

    ) } export default Demod ``` # dva ## 为什么学习dva ``` React 本身只是一个 DOM 的抽象层,使用组件构建虚拟 DOM。 如果开发大应用,还需要解决一个问题。 通信:组件之间如何通信? 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等 ``` ## 通信问题 ``` 组件会发生三种通信。 向子组件发消息 向父组件发消息 向其他组件发消息 React 只提供了一种通信手段:传参。对于大应用,很不方便 ``` ## dva 是什么 ``` dva 是体验技术部开发的 React 应用框架,将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷。 dva = React-Router + Redux + Redux-saga dva名字来自于守望先锋的D.Va(是守望先锋中的一个英雄) ``` ## 安装 dva-cli ``` 全局安装dva-cli npm install dva-cli -g 查看版本:dva -v 创建新应用:dva new 项目名 启动命令:npm start ``` ## 定义路由 ``` 路由页面创建在dva提供的router文件夹下创建 添加路由信息到路由表,编辑 router.js + import Products from './routes/Products'; ... + ``` ### 1-在router下创建路由页面 ```jsx import React, { Component } from 'react' export default class home extends Component { render() { return (

    我是home页面

    ) } } ``` ### 2-在router.js中引用配置 ```jsx import React from 'react'; import { Router, Route, Switch } from 'dva/router'; import IndexPage from './routes/IndexPage'; //引用页面 import Home from "./routes/home" function RouterConfig({ history }) { return ( {/*配置路由*/} ); } export default RouterConfig; ``` ## 定义路由导航 ``` 在dva中使用路由导航要从dva/router中进行引用 ``` ### 声明式 ```jsx import React, { Component } from 'react' //引用link import {Link} from "dva/router" export default class home extends Component { render() { return (

    我是home页面

    {/*配置路由导航*/} 点我去欢迎页面
    ) } } ``` ### 编程式 ```jsx import React, { Component } from 'react' //引用link import { Link } from "dva/router" export default class home extends Component { fun = () => { // 编程式导航 this.props.history.push("/") } render() { return (

    我是home页面

    {/*配置路由导航*/} 点我去欢迎页面 {/*使用编程式导航*/}
    ) } } ``` ## 定义路由模式 ``` 切换 history 为 browserHistory 需要下载history 依赖:npm install --save history 然后修改入口文件: import { createBrowserHistory as createHistory } from 'history'; const app = dva({ history: createHistory(), }); ``` ### 在src下的index.js文件中修改 ```js import dva from 'dva'; import './index.css'; // 1. Initialize // 原本的默认模式 // const app = dva(); //切换 history 为 browserHistory import { createBrowserHistory as createHistory } from 'history'; const app = dva({ history: createHistory(), }); // 2. Plugins // app.use({}); // 3. Model // app.model(require('./models/example').default); // 4. Router app.router(require('./router').default); // 5. Start app.start('#root'); ``` ## 定义组件 ``` 组件在components下进行定义 在需要的地方引用使用 定义组件的方式可以定义无状态组件 无状态组件的创建形式使代码的可读性更好,并且减少了大量冗余的代码, 精简至只有一个render方法,大大的增强了编写一个组件的便利 ``` ### 无状态组件的特点 ``` 组件不会被实例化,整体渲染性能得到提升 因为组件被精简成一个render方法的函数来实现的,无状态组件就不会在有组件实例化的过程,无实例化过程也就不需要分配多余的内存,从而性能得到一定的提升。 组件不能访问this对象 无状态组件由于没有实例化过程,所以无法访问组件this中的对象,若想访问this就不能使用这种形式来创建组件 组件无法访问生命周期的方法 ``` ## Model ``` model 用来处理数据和逻辑。可以把数据和交互都放到model中方便使用 ``` ## 使用Mosel示例 ### 1-创建两个组件 #### demoa组件 ```jsx import React, { Component } from 'react' export default class demoa extends Component { render() { return (

    我是一个类组件

    ) } } ``` #### demob组件 ```jsx import React from 'react' let Demob=(props)=>{ return (

    我是一个函数组件

    ) } export default Demob ``` ### 2-引用上面两个组件 ```jsx import React, { Component } from 'react' import Demoa from "../components/demoa.jsx" import Demob from "../components/demob.jsx" export default class home extends Component { render() { return (

    我是home页面

    ) } } ``` ### 3-在models文件夹中创建 对应的model文件 #### demoamodel.js文件 ```js export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 //保存数据的 state: { age:18 }, }; ``` ### 4-在全局文件 index.js中进行引用 ```js import dva from 'dva'; import './index.css'; // 1. Initialize // 原本的默认模式 // const app = dva(); //切换 history 为 browserHistory import { createBrowserHistory as createHistory } from 'history'; const app = dva({ history: createHistory(), }); // 2. Plugins // app.use({}); // 3. Model // app.model(require('./models/example').default); //引用demoamodel.js app.model(require('./models/demoamodel').default); // 4. Router app.router(require('./router').default); // 5. Start app.start('#root'); ``` ### 5-在需要使用的组件中使用connect进行连接 #### demoa中使用 ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { render() { return (

    我是一个类组件---{this.props.state.age}

    ) } } export default connect(state=>({state:state.demoamodel}))(demoa) ``` #### demob中使用 ```jsx import React from 'react' import {connect} from "dva" let Demob=(props)=>{ return (
    {/*3使用数据,无状态组件不需要this*/}

    我是一个函数组件--{props.name}

    ) } //1设置数据 const mapState=(state)=>{ return { name:state.demoamodel.age } } //传递HOC export default connect(mapState)(Demob) ``` # dva-reducers ## 修改-Model数据 ### 1-在model文件中定义reducers 属性来进行修改动作 ```js export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return state } } }; ``` ### 2-在组件中使用dispatch调用reducer修改动作 ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { fun=()=>{ //调用修改 this.props.dispatch({ //type:"命名空间/reducers的名字" type:"demoamodel/upage" }) } render() { return (

    我是一个类组件---{this.props.state.age}

    ) } } export default connect(state=>({state:state.demoamodel}))(demoa) ``` ### 3-传递修改数据 ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { fun=()=>{ //调用修改 this.props.dispatch({ //type:"命名空间/reducers的名字" type:"demoamodel/upage", data:"我是传递的修改的数据" }) } render() { return (

    我是一个类组件---{this.props.state.age}

    ) } } export default connect(state=>({state:state.demoamodel}))(demoa) ``` ### 4-reducers中接收数据并且修改 ```js export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return {...state,age:payload.data} } } }; ``` # dva-effects ## es6  Generator ``` Generator 主要是异步编程,用来封装一个异步任务,是一个异步任务的容器。 特点:交出普通函数的执行权(可以让函数在调用的时候按照我们的需要执行或者是暂停) 普通函数:在调用的时候函数中的内容会全部执行。 generator函数 可以让函数体内的内容随着我们的需要走走停停 在声明函数的function关键字和函数名之间有一个*号(用于区别普通函数) yield(是异步不同阶段的分割线)在generator函数体内进行使用,可以定义不同的内部状态 使用next()来执行generator函数 调用generator函数的时候会返回一个generator对象 使用next()方法来执行generator函数 可以在next()方法中方法中传递参数 可以吧值传递到函数中对应的yield中。第一次不会传递 ``` ```html Document ``` # dva--异步操作 ``` effect 在 dva 框架下就是用来处理异步操作的 ``` ## 1-在model文件中写入effects属性 ```js export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return {...state,age:payload.data} } }, //异步操作 effects:{ *eff({payload},{put,call}){ yield console.log("我是effect异步操作") } } }; ``` ## 2-在组件内进行调用 触发方式也是dispatch ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { fun=()=>{ //调用修改 this.props.dispatch({ //type:"命名空间/reducers的名字" type:"demoamodel/upage", data:"我是传递的修改的数据" }) } funb=()=>{ this.props.dispatch({ // type:"命名空间/effects的名字" type:"demoamodel/eff" }) } render() { return (

    我是一个类组件---{this.props.state.age}

    ) } } export default connect(state=>({state:state.demoamodel}))(demoa) ``` # dva-基本异步请求 ## 1--在services文件夹下封装自己的请求 ``` 异步请求在services文件夹下 新建文件封装我们自己的请求 ``` ```js //从example.js复制 import request from '../utils/request'; export function query() { return request('/api/users'); } ``` ## 2-传入接口 ``` 传入请求地址 中国天气网测试请求地址(需要解决跨域)http://www.weather.com.cn/data/cityinfo/101320101.html ``` ```js import request from '../utils/request'; export function query() { return request('http://www.weather.com.cn/data/cityinfo/101320101.html'); } ``` ## 3-在组件内调用封装的请求 ```jsx import React, { Component } from 'react' //引用link import { Link } from "dva/router" import Demoa from "../components/demoa.jsx" import Demob from "../components/demob.jsx" //引用封装的请求 import * as homeapi from "../services/homeapi.js" export default class home extends Component { //在生命周期里面请求数据 componentDidMount() { homeapi.query().then((ok)=>{ console.log(ok) }) } fun = () => { // 编程式导航 this.props.history.push("/") } render() { return (

    我是home页面

    {/*配置路由导航*/} 点我去欢迎页面
    ) } } ``` ## 4-解决跨域 ### 4.1-在.webpackrc文件中设置跨域 并且修改请求 ``` { "proxy":{ "/api":{ "target":"http://www.weather.com.cn", "changeOrigin":true, "pathRewrite":{ "^/api":"/" } } } } ``` ### 4.2-修改请求 ```js import request from '../utils/request'; export function query() { return request('api/data/cityinfo/101320101.html'); } ``` # dva-model异步请求 ### 1-在model中发送请求 ``` 如果需要在model中进行异步请求的话需要在effects中的call进行一步操作的发送 ``` ```js //引用封装的请求 import * as homeapi from "../services/homeapi.js" export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return {...state,age:payload.data} } }, //异步操作 effects:{ *eff({payload},{put,call}){ //请求数据 let api = yield call(homeapi.query) console.log(api) } } }; ``` ### 2-页面内调用带有异步请求的effects ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { fun=()=>{ //调用修改 this.props.dispatch({ //type:"命名空间/reducers的名字" type:"demoamodel/upage", data:"我是传递的修改的数据" }) } funb=()=>{ this.props.dispatch({ // type:"命名空间/effects的名字" type:"demoamodel/eff" }) } render() { return (

    我是一个类组件---{this.props.state.age}

    ) } } export default connect(state=>({state:state.demoamodel}))(demoa) ``` # dva-model异步请求修改state ## 1-可以通过put的方式修改state ```js import * as homeapi from "../services/homeapi.js" export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return {...state,age:payload.data} } }, //异步操作 effects:{ *eff({payload},{put,call}){ let api = yield call(homeapi.query) console.log(api.data.weatherinfo.city) if(api.data){ yield put({ // 调用reducer修改动作 type:"upage", data:api.data.weatherinfo.city }) } } } }; ``` ## 2-创建修改reducers ```js import * as homeapi from "../services/homeapi.js" export default { namespace: 'demoamodel',//命名空间 就是给当前的模块起个名字 必须写 //创建state state: { age:18 }, //修改数据的 reducers:{ upage(state,payload){ console.log("我是修改动作") //必须返回 return {...state,age:payload.data} } }, //异步操作 effects:{ *eff({payload},{put,call}){ let api = yield call(homeapi.query) console.log(api.data.weatherinfo.city) if(api.data){ yield put({ // 调用reducer修改动作 type:"upage", data:api.data.weatherinfo.city }) } } } }; ``` ## 3-页面引用state数据给props并且展示 ```jsx import React, { Component } from 'react' import {connect} from "dva" class demoa extends Component { fun=()=>{ //调用修改 this.props.dispatch({ //type:"命名空间/reducers的名字" type:"demoamodel/upage", data:"我是传递的修改的数据" }) } funb=()=>{ this.props.dispatch({ // type:"命名空间/effects的名字" type:"demoamodel/eff" }) } render() { return (
    {/*展示数据*/}

    我是一个类组件---{this.props.state.age}

    ) } } //引用数据 export default connect(state=>({state:state.demoamodel}))(demoa) ``` # dva-subscription订阅 model中的subscription相当于一个监听器,可以监听路由变化,鼠标,键盘变化,服务器连接变化,状态变化等,这样在其中就可以根据不同的变化做出相应的处理,在这个subsription中的方法名是随意定的,每次变化都会一次去调用里面的所有方法,所以一边会加相应的判断。