React
react
react基本概念
react 是由facebook开发的
react 是一个专门用于构建用户界面的javascript库
为了解决页面更新dom加载慢的问题 所以推出的react这个js库 来高效的解决大型项目中的dom加载过慢页面渲染过慢的问题
react特点
声明式设计: 我们只需要告诉程序干什么 程序会自己帮我们完成
高效
灵活
组件化
单项数据流
发展史
2013年推出的 在13年9月份 就受到了市场的追捧 15年3月 推出了一个reactNative的技术(使用react写app)
兼容性
ie8一下无效
脚手架创建-cra
cra----create-react-app
1.全局安装cra
npm install -g create-react-app
2.查看版本
create-react-app --version
3.cd到项目下
4.开始创建项目 create-react-app 项目名
5.cd到项目下
6启动 npm start
JSX
我们在react中编写内容使用的是JSX的语法 JSX=javascript and XML
遇见<就会当html解析 遇到{}当js解析 jsx的外层最好用一对()包裹
注意:jsx是一个非常严格的语法要求 标签必须遵循w3c规范完成 标签必须关闭
JSX优点:
1.安全可靠 因为在编译的时候就会发现错误
绑定变量
使用{} 来进行绑定
<h1>你坏-----{text}</h1>
html的属性插入变量
不要双引号 直接{}
{/* 属性插变量 */}
<a href={ahref}>{btntext}</a>
多行标签
多行标签必须有一个父容器来进行包裹
jsx 注释
{ /*我是jsx的注释*/ }
便利数据
在react中便利数据使用map()
{
arr.map((v,i)=>{
return (
<li key={i}>{v}</li>
)
})
}
<table>
{/* 不加会有警告 */}
<tbody>
{
obj.map((v,i)=>{
return (
<tr key={i}>
<td>{v.name}</td>
<td>{v.age}</td>
</tr>
)
})
}
</tbody>
</table>
设置样式
类样式
因为class是es6类的关键字 所以要使用className
引用样式使用 import来来进行引用
import React, { Component } from 'react'
export default class homed extends Component {
render() {
return (
<div>
{/* 在react中如果要设置类样式 class必须变成className */}
<h1 className="demoh">类样式</h1>
</div>
)
}
}
引用样式文件
import React, { Component } from 'react'
// 引用css文件
import "./homed.css"
export default class homed extends Component {
render() {
return (
<div>
{/* 在react中如果要设置类样式 class必须变成className */}
<h1 className="demoh">类样式</h1>
</div>
)
}
}
行内样式
行内样式使用对象的方式来进行设置 所以{{}} 外面的{}是jsx的语法 里面的{}是对象的语法
而且如果多个单词 那么把-去掉 后面的单词首字母大写
import React, { Component } from 'react'
// 引用css文件
import "./homed.css"
export default class homed extends Component {
render() {
return (
<div>
{/* 在react中如果要设置类样式 class必须变成className */}
<h1 className="demoh">类样式</h1>
{/* 行内样式使用对象的方式来进行设置 */}
{/* 而且如果多个单词 那么把-去掉 后面的单词首字母大写 */}
<h1 style={{color:"red",backgroundColor:"pink"}}>行内样式</h1>
</div>
)
}
}
组件
本质 自定义标签
组件的特点优势
强内聚 弱耦合
提高了代码的复用性
减低了测试难度
代码的复杂度也大大降低
组件的分类
在react中他有两种组件的写法
函数组件--无状态组件
语法:
function 函数名(组件名 但是名字首字母必须必须必须必须 必须 大写){
return (
jsx
)
}
类组件--有状态组件
语法:
class 类名(当前组件名首字母大写) extends React.Component{
render(){ render方法也叫做渲染方法 他的作用就是渲染当前的jsx
return(
jsx
)
}
}
props
正向传值
props能修改吗?
props是只读的 不能修改
语法:
props函数组件
语法 :
只需要在函数组件中添加一个props的形参 即可使用props
子组件接收
// 子组件需要把props当成形参传入
let Zi=(props)=>{
let {text,num}=props
return(
<div>
zizizziziziziz---{text}--{num}
</div>
)
}
export default Zi
父组件传递
import Fz from "./funpropszi.jsx"
let Fu=()=>{
let obj={text:"text数据",num:"num数据"}
return(
<div>
fufufufuffufufu
{/* 传递参数可是用扩展运算符 */}
<Fz {
props类组件
this.props.xxx
1.子组件设置props
import React, { Component } from 'react'
export default class propszi extends Component {
render() {
return (
<div>
zizizzizizizizzz---{this.props.text}
<br />
{this.props.num}
</div>
)
}
}
2.父组件传递
import React, { Component } from 'react'
import Pz from "./propszi.jsx"
export default class propsfu extends Component {
render() {
return (
<div>
fuffufufufufuffu
{/* 父组件传值 */}
<Pz text="我是父组件的text" num="我是父组件的num"/>
</div>
)
}
}
上面的写法太low了 改变一下
子组件接收数据的时候 使用解构赋值的方式取出this。props的数据
import React, { Component } from 'react'
export default class propszi extends Component {
render() {
// 由于this.props是一个对象 为了让代码看起来更加简洁 所以我们
// 使用解构赋值的方式 把props中的数据快速取出来
let {text,num}=this.props
return (
<div>
zizizizizizizziiz---{text}---{num}
</div>
)
}
}
父组件传递
import React, { Component } from 'react'
import Pz from "./propszi.jsx"
export default class propsfu extends Component {
render() {
let obj={text:"我是父组件的text",num:"我是父组件的num"}
return (
<div>
fuffufufufufuffu
{/* 父组件传值 使用扩展运算符可以简化我们在父组件给
子组件传递数据时候的复杂度
*/}
<Pz {
props 验证
rccp
注意:
自 React v15.5 起,React.PropTypes 已移入另一个包中。请使用 [prop-types 库](https://www.npmjs.com/package/prop-types) 代替。
// import React, { Component } from 'react'
// export default class propszi extends Component {
// render() {
// // 由于this.props是一个对象 为了让代码看起来更加简洁 所以我们
// // 使用解构赋值的方式 把props中的数据快速取出来
// let {text,num}=this.props
// return (
// <div>
// zizizizizizizziiz---{text}---{num}
// </div>
// )
// }
// }
import React, { Component } from 'react'
// 包要引
import PropTypes from 'prop-types'
export default class propszi extends Component {
// 使用PropTypes来进行props的验证
static propTypes = {
text: PropTypes.string,
num:PropTypes.number
}
render() {
let {text,num}=this.props
return (
<div>
zizizizizizizziiz---{text}---{num}
</div>
)
}
}
状态机state
状态==数据
状态机===数据机制
数据等同于状态 状态改变页面也会改变
函数组件可以使用状态码?
函数组件默认情况下不能使用状态 后面会学到高阶语法 HOOK才能使用
语法
状态创建
import React, { Component } from 'react'
export default class statedemo extends Component {
// 创建状态 需要写在constructor中
// 在es6中 子类写不写constructor 在实例的过程中 都会给补上他
// 但是如果我们写了 那么必须写super()
// super就是调用父类构造方法 只有写了子类才有自己的this
// 如果只写了constructor不写super()那么以后使用this的时候指向都是错误的
constructor(){
super()
// 创建状态
this.state={
text:"字符串",
num:18,
arr:[111,2222,33333],
obj:{name:"xxixi"}
}
}
render() {
return (
<div>
<h1>状态的使用</h1>
</div>
)
}
}
读取状态
在想使用的地方使用 this.state.xxx
import React, { Component } from 'react'
export default class statedemo extends Component {
// 创建状态 需要写在constructor中
// 在es6中 子类写不写constructor 在实例的过程中 都会给补上他
// 但是如果我们写了 那么必须写super()
// super就是调用父类构造方法 只有写了子类才有自己的this
// 如果只写了constructor不写super()那么以后使用this的时候指向都是错误的
constructor(){
super()
// 创建状态
this.state={
text:"字符串",
num:18,
arr:[111,2222,33333],
obj:{name:"xxixi"}
}
}
render() {
return (
<div>
<h1>状态的使用</h1>
{/* 使用状态 */}
<em>{this.state.num}-----{this.state.text}---{this.state.arr[1]}</em>
</div>
)
}
}
修改state
修改state不能直接使用=修改 而是要调用一个叫 setState()来进行修改
import React, { Component } from 'react'
export default class statedemo extends Component {
// 创建状态 需要写在constructor中
// 在es6中 子类写不写constructor 在实例的过程中 都会给补上他
// 但是如果我们写了 那么必须写super()
// super就是调用父类构造方法 只有写了子类才有自己的this
// 如果只写了constructor不写super()那么以后使用this的时候指向都是错误的
constructor(){
super()
// 创建状态
this.state={
text:"字符串",
num:18,
arr:[111,2222,33333],
obj:{name:"xxixi"}
}
}
// 创建函数
fun=()=>{
// 修改state数据必须使用setState()
this.setState({
num:888,
text:"我变了"
})
}
render() {
return (
<div>
<h1>状态的使用</h1>
{/* 使用状态 */}
<em>{this.state.num}-----{this.state.text}---{this.state.arr[1]}</em>
<button onClick={this.fun}>点我修改</button>
</div>
)
}
}
为什么要使用setState()来修改state
因为调用了setState之后 他会自动触发render方法重新渲染页面 从而让数据改变之后 页面页也会发生改变
setState 在修改数据的时候是异步的
由于setState是一个异步的 所以他第一个参数是一个对象 用来修改数据 第二个参数是一个回调函数 是当修改完数据之后自动触发回调函数
this.setState({
num:888,
text:"我变了"
},()=>{
// 我就是想等数据修改之后在console中打印出来
console.log(this.state.num);
})
ref
用来标识组件内部的元素 但是注意 函数组件由于没有实例所以不能使用ref
使用
1.字符串方式(已经淘汰了)
2.回调函数方式
import React, { Component } from 'react'
export default class refdemo extends Component {
fun=()=>{
console.log(this.refinput.value);
}
render() {
return (
<div>
<h1>ref回调函数的方式</h1>
{/* 回调函数方式绑定ref 需要在dom节点上挂载一个函数
函数的形参是当前dom节点 (形参)=>{随便创建一个变量=形参}*/}
<input type="text" ref={(demoinput)=>{this.refinput=demoinput}}/>
<button onClick={this.fun}>点我得到值</button>
</div>
)
}
}
3.React.createRef() react16.3新增
import React, { Component } from 'react'
export default class refdemob extends Component {
constructor(){
super()
// 1.创建createRef
this.refdemo=React.createRef()
}
fun=()=>{
// 3.使用
console.log(this.refdemo.current.value);
}
render() {
return (
<div>
<h1>createRef()</h1>
{/* 2.绑定 */}
<input type="text" ref={this.refdemo}/>
<button onClick={this.fun}>点我得到值</button>
</div>
)
}
}
事件处理机制
事件的绑定
使用小驼峰命名法来进行事件的绑定 onclick---->onClick
如果事件要调用函数 那么函数是不加()
函数实参传递
由于react中的函数是没有()的 所以传统的实参传参方式是没有办法进行的
.bind方式进行实参的传递
onClick={this.fun.bind(this,你要传递的数据)}
事件箭头函数调用函数传参
{/* 通过箭头函数调用函数传参 */}
<button onClick={()=>{this.funb("参数")}}>点我传参2</button>
事件的修饰
react中如果想阻止事件传播或者默认行为的话 使用同原生一样的方式
this指向修改
1.就是创建函数的时候使用箭头函数创建
2.通过.bind方式解决this
<button onClick={this.funb.bind(this)}>bind修改this</button>
3.把函数的调用变成使用箭头函数调用
<button onClick={()=>{this.funb()}}>箭头函数调用函数</button>
4.在constructor中提前绑定
constructor(){
super()
this.state={
text:"你好"
}
// 提前给函数绑定this
this.funb=this.funb.bind(this)
}
条件渲染
根据我们的需要 显示或者隐藏某写内容
1.三元运算符
import React, { Component } from 'react'
export default class democ extends Component {
constructor(){
super()
this.state={
bool:true
}
}
render() {
return (
<div>
<h1>条件渲染</h1>
{this.state.bool?<h1>吃了</h1>:<h1>没有吃</h1>}
</div>
)
}
}
2.if全家桶
注意注意注意:在jsx不能写if
import React, { Component } from 'react'
export default class demod extends Component {
constructor(){
super()
this.state={
num:5
}
}
render() {
let com=""
if(this.state.num==1){
com= <h1>第1</h1>
}else if(this.state.num==2){
com= <h1>第2</h1>
}else if(this.state.num==3){
com= <h1>第3</h1>
}else if(this.state.num==4){
com= <h1>第4</h1>
}else if(this.state.num==5){
com= <h1>第5</h1>
}
return (
<div>
<h1>条件渲染</h1>
{com}
</div>
)
}
}
状态提升
react中转台提升就是多个组件需要反映相同的数据变化 我们可以把这个数据提升到这几个组件的父组件之上 然后在父组件中改变数据 那么通过props分发给这几个子组件即可反映相同的数据变化
扩展----多行标签-空标签
在react中 多行标签必须有一个父容器包裹 但是往往这些包裹的容器标签是没有用 在页面渲染的时候 会造成冗余代码
1.真正的空标签
import React, { Component } from 'react'
export default class ezia extends Component {
render() {
return (
// 空标签
<>
<h1>我是h1</h1>
<h1>我是h11</h1>
</>
)
}
}
2.Fragment 空标签 今后会使用的比较多
import React, { Component,Fragment } from 'react'
export default class ezi extends Component {
render() {
return (
// Fragment空标签 别忘了引用
<Fragment>
<h2>h222222</h2>
<h2>h222222222222222</h2>
</Fragment>
)
}
}
扩展----强制刷新
react 只有使用state创建的变量 在使用setState修改之后页面才会更新。
如果我就是不想在state里面写状态 也不想使用setState修改 我怎么样创建变量修改更新呢?
import React, { Component } from 'react'
export default class demof extends Component {
constructor(){
super()
this.name="xixi"
}
fun=()=>{
this.name="haha"
console.log(this.name);
// 强制触发render()渲染
// 只要调用就会重新渲染
this.forceUpdate()
}
render() {
return (
<div>
<h1>页面强制刷新---{this.name}</h1>
<button onClick={()=>{this.fun()}}>点我修改</button>
</div>
)
}
}
组件传值
正向传值--props
详见上面内容
逆向传值
react的逆向传值 使用的也是props 只是和正向传值不同 因为正向传值传递的是数据 但是逆向传值传递的是方法
子组件
import React, { Component } from 'react'
export default class nizi extends Component {
render() {
return (
<div>
<h1>子组件</h1>
{/* 1.子组件必须通过事件来触发逆向传值 */}
{/* 2.子组件需要接收父组件传递过来的函数 并且使用bind进行函数实参传递 */}
<button onClick={this.props.fun.bind(this,"子组件的数据")}>点我逆向传值</button>
</div>
)
}
}
父组件
import React, { Component } from 'react'
import Nz from "./nizi.jsx"
export default class nifu extends Component {
// 4.创建父组件的函数
demo=(text)=>{
console.log(text);
}
render() {
return (
<div>
<h1>逆向传值</h1>
{/* 3.子组件需要一个函数 那么父组件给他传递一个函数 */}
<Nz fun={this.demo}/>
</div>
)
}
}
同胞传值---pubsub-js
1.下载pubsub-js npm install --save pubsub-js
2.在需要传递的组件中进行数据的抛出(使用自定义事件)publish("自定义事件名","传递的参数")
import React, { Component,Fragment } from 'react'
// 1.引用pubsub-js
import PubSub from "pubsub-js"
export default class pubsubzia extends Component {
fun=()=>{
// 2.抛出自定事件
PubSub.publish("pao","我是zia的数据么么哒!!!")
}
render() {
return (
<Fragment>
<h1>zia</h1>
<button onClick={this.fun}>点我同胞传值</button>
</Fragment>
)
}
}
3.在需要数据的组件内 监听自定义事件 subscribe("你要监听的事件名",(事件,数据)=>{})
import React, { Component } from 'react'
import PubSub from "pubsub-js"
export default class pubsubzib extends Component {
// 3使用生命周期自动调用subscribe()来监听自定事件
// 组件渲染之前
componentWillMount() {
PubSub.subscribe("pao",(a,b)=>{
console.log(a);//监听的事件名
console.log(b);//监听的数据
})
}
// 组件渲染之后
componentDidMount() {
}
render() {
return (
<>
<h1>zib</h1>
</>
)
}
}
跨组件传值
react中组件传递参数是通过props一层一层的进行传递的 数据也是单向传递的 如果出现了跨层级的关系 要是按照传统的方式传参 太麻烦了
方式1 context上下文对象
就是react中为了解决跨层级传值出现的一个技术 降低了数据传递的复杂度 传递起来更加的高效
上下文对象 有两个内容
Provider 生产者 ----- 》 创建要传递的数据
Consumer 消费者 ------ 》 使用传递的数据
使用:
1.使用上下文对象 我们可以创建一个文件夹用来容纳他 并且创建一个文件写一个组件
import React, { Component } from 'react'
// 建议组件名大写
export default class Index extends Component {
render() {
return (
<div>
</div>
)
}
}
2.创建出上下文对象以及他的生产者和消费者
// 1.引用进创建上下文对象的createContext
import React, { Component,createContext } from 'react'
// 2.创建上下文对象
let context=createContext()
// 3.创建生产者和消费者
let {Provider,Consumer}=context;
// 建议组件名大写
class Index extends Component {
render() {
return (
<div>
{/* 4.this.props.children 表示所有的子元素 */}
{this.props.children}
</div>
)
}
}
// 5.修改暴漏 需要暴漏当前组件与 消费着
export {Index,Consumer}
3.我们需要传递数据 那么就把当前这个上下文对象的组件变成所有组件的老大
在index.js中设置
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// 6.引用并且解构出上下文对象的组件
import {Index} from "./context/index.js"
import App from './components/ye.jsx';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
// 7.让上下文对象变成所有组件的老大
<Index>
<App />
</Index>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
4.生产数据
// 1.引用进创建上下文对象的createContext
import React, { Component,createContext } from 'react'
// 2.创建上下文对象
let context=createContext()
// 3.创建生产者和消费者
let {Provider,Consumer}=context;
// 建议组件名大写
class Index extends Component {
render() {
return (
<div>
{/* 8上产数据 */}
<Provider value={{text:"数据1",num:666}}>
{/* 4.this.props.children 表示所有的子元素 */}
{this.props.children}
</Provider>
</div>
)
}
}
// 5.修改暴漏 需要暴漏当前组件与 消费着
export {Index,Consumer}
5.消费数据
import React, { Component } from 'react'
import Sun from "./sun.jsx"
// 9引用消费者
import {Consumer} from "../context/index.js"
export default class fu extends Component {
render() {
return (
<div>
{/* 10使用消费者 */}
<Consumer>
{
// (就是生产者的数据)=>{
// 想怎么用就怎么用
// }
(data)=>{
return (
<h1>{data.num}</h1>
)
}
}
</Consumer>
爸爸组件
<Sun/>
</div>
)
}
}
方式2 redux
redux是js的一个状态管理工具
他是第三方的 可以在react中用 也可以在vue中使用
redux的三大原则
1.单一数据源 整个项目只有一个store对象
2.state是只读的 在redux想改变state那么必须使用action来进行修改 不能直接修改
3.使用纯函数来执行修改 在redux中为了要修改数据 那么我们需要编写一个reducer的纯函数
redux的执行流程
详见1203课件图片
redux中常见的属性
store : redux对象管理者整个项目的状态
reducer: 是一个函数 保存着就是数据与数据修改的方法
action: 是redux唯一可以修改数据的
createStore() 创建redux对象
getState() 获取数据
dispatch() 触发action
基本使用
1.下载 npm install --save redux
2.创建store文件夹 用来容纳redux对象
// 1.引入createStore
import {createStore} from "redux"
// 6.创建数据
let data={
name:"xixi",
age:18
}
// 4.创建reducer 存储的是数据与修改数据的动作
// state 数据
// action修改动作
// 7。把数据赋值给state
let reducer=(state=data,action)=>{
return state
}
// 2.创建出store对象
// 5.把数据与数据修改 注入到redux对象中
let store=createStore(reducer)
// 3.暴漏
export default store
读取数据
import React, { Component } from 'react'
// 1.先引用
import store from "../store/index.js"
export default class reduxdemo extends Component {
constructor(){
super()
this.state={
// 2.store.getState.xxx来读取数据
text:store.getState().name
}
}
render() {
return (
<div>
<h1>redux使用---{this.state.text}</h1>
</div>
)
}
}
修改数据
1.页面通过dispatch触发修改的动作
import React, { Component } from 'react'
// 1.先引用
import store from "../store/index.js"
export default class reduxdemo extends Component {
constructor(){
super()
this.state={
// 2.store.getState.xxx来读取数据
text:store.getState().name
}
}
fun=()=>{
// 数据的修改1 通过dispatch触发修改
// store.dispatch({type:"你要触发的修改动作名"})
store.dispatch({type:"REDUX_USER_LIST_UPDATE"})
}
render() {
return (
<div>
<h1>redux使用---{this.state.text}</h1>
<button onClick={()=>{this.fun()}}>点我修改数据</button>
</div>
)
}
}
2.来到reducer中创建数据的修改动作
// 1.先引用 创建store的方法 createStore
import {createStore} from "redux"
// 6.创建数据
let xiaoming={
text:"text数据",
num:666
}
// 5创建一个函数 包含了数据与数据的修改状态 7传入数据
let reducer=(state=xiaoming,action)=>{
// 数据修改2 在action中设置多个修改动作
switch (action.type) {
case "REDUX_USER_LIST_UPDATE":
// 修改动作
return {
当时发现页面并没有变化 是因为数据变了 但是页面并没有执行render渲染所以页面没有变
3 subscribe() redux中用来监听state数据变化的 当state的数据变化了 那么subscricbe就会触发执行
import React, { Component } from 'react'
// 1.先引用
import store from "../store/index.js"
export default class reduxdemo extends Component {
constructor(){
super()
this.state={
// 2.store.getState.xxx来读取数据
text:store.getState().num
}
}
componentDidMount() {
// 监听store的state是否变了
store.subscribe(()=>{
// 如果redux的数据变了 那么这个函数就会执行
// 我们就触发render渲染
this.setState({
text:store.getState().num
})
})
}
fun=()=>{
// 数据的修改1 通过dispatch触发修改
// store.dispatch({type:"你要触发的修改动作名"})
store.dispatch({type:"REDUX_USER_LIST_UPDATE"})
}
render() {
return (
<div>
<h1>redux使用---{this.state.text}</h1>
<button onClick={()=>{this.fun()}}>点我修改数据</button>
</div>
)
}
}
redux拆分写法
使用actionCreator来管理修改任务动作
我们会发现组件中有很多的调用修改的dispatch如下
fun=()=>{
// 数据的修改1 通过dispatch触发修改
// store.dispatch({type:"你要触发的修改动作名"})
store.dispatch({type:"REDUX_USER_LIST_UPDATE"})
}
由于组件增多 可能回后期维护比较麻烦 所以我们可以把dispach的任务动作名 提取出来创建一个文件同一管理项目的人物动作名
1.在store下创建一个文件叫actioncreator.js来保存动作名
export let REDUX_USER_LIST_UPDATE=()=>{
return {type:"REDUX_USER_LIST_UPDATE"}
}
2.组件的dispatch使用
在组件中先引用
// actioncreator引用
import {REDUX_USER_LIST_UPDATE} from "../store/actionCreator.js"
在dispatch中使用
fun=()=>{
// 数据的修改1 通过dispatch触发修改
// store.dispatch({type:"你要触发的修改动作名"})
store.dispatch(REDUX_USER_LIST_UPDATE())
}
完整
import React, { Component } from 'react'
// 1.先引用
import store from "../store/index.js"
// actioncreator引用
import {REDUX_USER_LIST_UPDATE} from "../store/actionCreator.js"
export default class reduxdemo extends Component {
constructor(){
super()
this.state={
// 2.store.getState.xxx来读取数据
text:store.getState().num
}
}
componentDidMount() {
// 监听store的state是否变了
store.subscribe(()=>{
// 如果redux的数据变了 那么这个函数就会执行
// 我们就触发render渲染
this.setState({
text:store.getState().num
})
})
}
fun=()=>{
// 数据的修改1 通过dispatch触发修改
// store.dispatch({type:"你要触发的修改动作名"})
store.dispatch(REDUX_USER_LIST_UPDATE())
}
render() {
return (
<div>
<h1>redux使用---{this.state.text}</h1>
<button onClick={()=>{this.fun()}}>点我修改数据</button>
</div>
)
}
}
使用actionType来同一管理任务动作的名字
由于这个动作的名字出现了很多次 所以我们需要把任务动作名进行封装
1.新建actionTypes.js文件
export const TYPE_REDUX_USER_LIST_UPDATE="REDUX_USER_LIST_UPDATE"
2.在action的switch中和 actionCreatr.js进行引用使用
封装reducer
今后项目的体积会逐渐变大 那么我们把所有的数据与修改写在一个文件中后期不好管理 所以我们可以把每个组件的数据与修改动作剥离出来 单独进行管理
1.创建一个文件夹用来容纳模块
2.把原来写在store文件夹下的render提取到模块文件中
import {TYPE_REDUX_USER_LIST_UPDATE} from "./actionType.js"
let xiaoming={
text:"text数据",
num:666
}
let reduxdemom=(state=xiaoming,action)=>{
switch (action.type) {
case TYPE_REDUX_USER_LIST_UPDATE:
console.log({
合并reducer
因为我们拆分成模块之后 就会出现多个reducer 那么我们需要把多个合并成一个
1.新建一个reducer.js
2.在其中开始合并
// 1.把你要合并的所有模块引进来
import reduxdemom from "./modules/reduxdemom.js"
// 2.引用合并功能 combineReducers把多个reducer合并成一个
import {combineReducers} from "redux"
// 3.开始合并
let reducer=combineReducers({
reduxdemom
})
// 4.暴漏
export default reducer
3.在store文件中引用使用
发现数据不展示了 原因是因为我们现在使用了模块 所以在读数据的时候 就需要使用
store.getState.模块名.xxxxx
react-redux
在写的时候原有的store文件夹下的所有内容同redux
因为redux是第三方的 所以我们在使用的时候会发现组件中有大量的store对象
react-redux 就是一个专门为react开发的redux状态管理工具
下载 : npm install --save react-redux
使用
1.我们可以使用react-redux中的Provider 用来把store对象分发给所有的组件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/demo.jsx';
import reportWebVitals from './reportWebVitals';
// 1.引用react-redux拿出Provider
import {Provider} from "react-redux"
// 2.引用store对象
import store from "./store/index.js"
ReactDOM.render(
// 3.使用Provider中的store属性进行传递
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
2.使用connect()把当前的组件与react-redux连接起来
import React, { Component } from 'react'
// 1.引用一下连接
// connect 是一个react中的高阶组件 高阶组件就是需要接收一个组件为参数才可以使用
import {connect} from "react-redux"
class reactredux extends Component {
render() {
return (
<div>
<h1>react-redux的使用</h1>
</div>
)
}
}
// connect是一个方法 只有当这个方法被调用之后才是一个高阶组件
export default connect(state=>({state}))(reactredux)
3.读取数据
<h1>react-redux的使用--{this.props.state.模块名.你的数据名}</h1>
修改
this.props.dispatch() 直接调用修改不需要在向redux一样去监听了
fun=()=>{
// 修改
this.props.dispatch(REDUX_UPDATE())
}
路由
根据url的不同来切换不同的组件页面
从而完成SPA单页面应用的一个技术 整个项目只有一个完整的html页面 那么基于这个页面 组件在其上面进行根据url来渲染不同的组件页面 页面切换的时候不会刷新 用户就不会感知到页面的切换 从而更加让我们的项目贴近原生的体验
路由分类
react-router 仅仅只有基本的路由功能api
react-router-dom 除了基本路由的api之外还有更多便捷性的api
下载 cnpm install --save react-router-dom@5.2.0
使用
1.新建对应的路由文件夹 router 与views文件夹
2.新建路由页面组件
3.设置路由模式 在index.js中进行设置
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
// 2.设置路由模式
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
4.配置规则与出口
<Route path="/路径" component={匹配的组件页面}/>
import React, { Component } from 'react'
// 1.引用你要配置的路由
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
// 2.引用Route来进行出口与规则的配置
import {Route} from "react-router-dom"
export default class index extends Component {
render() {
return (
<div>
{/* 3.配置 */}
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
</div>
)
}
}
5.把路由规则的组件变成跟组件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// 最后一部
import Router from './router/index.js';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
// 2.设置路由模式
<BrowserRouter>
<Router />
</BrowserRouter>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
路由模式
HashRouter 带#号 刷新不丢失 兼容性好
BrowserRouter 不带# 刷新丢失 兼容性低
路由导航
编程式
this.props.history.goBack() 后退
this.props.history.goForward() 前进
this.props.history.replace() 替换
this.props.history.push("/xxxxx")
import React, { Component } from 'react'
export default class home extends Component {
fun=()=>{
// 编程式导航
this.props.history.push("/shop")
}
render() {
return (
<div>
home
<button onClick={this.fun}>点我去shop</button>
</div>
)
}
}
但是按照上面的写法 发现编程式导航有时候可以 有时候却报 TypeError: Cannot read property 'push' of undefined 的错误
出现的原因 因为 编程式导航中使用到了路由history属性 但是不是被路由所管理的页面是没有history属性的
我就是想在不是被路由所管理的页面中使用编程式导航使用history属性怎么办?
withRouter高阶组件
withrouter高阶组件 的作用就是给不是被路由所管理的页面注入history属性
import React, { Component } from 'react'
// 1.引用
import {withRouter} from "react-router-dom"
class topbar extends Component {
fun=()=>{
// 编程式导航
this.props.history.push("/shop")
}
render() {
return (
<div>
<button onClick={this.fun}>点我去shop</button>
</div>
)
}
}
使用高阶组件
export default withRouter(topbar)
声明式
Link to属性来设置你要跳转的具体路径
import React, { Component } from 'react'
// 1.引用你要配置的路由
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
// 2.引用Route来进行出口与规则的配置
import {Route,Link} from "react-router-dom"
export default class index extends Component {
render() {
return (
<div>
{/* 不要忘了上面要引用Link才能使用 */}
<Link to="/home">点我去home</Link>
<Link to="/phone">点我去phone</Link>
<Link to="/user">点我去user</Link>
<Link to="/shop">点我去shop</Link>
{/* 3.配置 */}
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
</div>
)
}
}
NavLink to属性来设置你要跳转的具体路径 NavLink会在选中之后添加一个active的类名
import React, { Component } from 'react'
// 1.引用你要配置的路由
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
// 2.引用Route来进行出口与规则的配置
import {Route,NavLink} from "react-router-dom"
export default class index extends Component {
render() {
return (
<div>
{/* 不要忘了上面要引用NavLink才能使用 */}
<NavLink to="/home">点我去home</NavLink>
<NavLink to="/phone">点我去phone</NavLink>
<NavLink to="/user">点我去user</NavLink>
<NavLink to="/shop">点我去shop</NavLink>
{/* 3.配置 */}
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
</div>
)
}
}
注意 如果有的同学navlink的动态类名出不来 那么就不要用vscode的内置终端启动项目 在外部的cmd启动
修改NavLink动态添加的类名
使用activeClassName属性来进行修改
<NavLink to="/home" activeClassName="xiaoming">点我去home</NavLink>
404
1.新建404页面
2.配置 404页面需要放到最下面进行配置
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
{/* 404页面需要放到最下面进行配置 */}
<Route component={No}/>
注意: 当我们写上404页面之后 会发现 无论那个页面显示都会出现404 原因是因为 在react中 匹配上页面之后 他不会停止 会一直向下执行 404页面就会被渲染上
Switch 唯一渲染
路由渲染一个之后就不渲染后续的内容了
{/* 唯一渲染包裹所有的路由出口与规则 */}
<Switch>
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
{/* 404页面需要放到最下面进行配置 */}
<Route component={No}/>
</Switch>
重定向 redirect
exact精准匹配 只有路径为/的时候才能匹配到
<Redirect from="/" to="去哪里” exact/>
<Switch>
<Route path="/home" component={Home}/>
<Route path="/phone" component={Phone}/>
<Route path="/user" component={User}/>
<Route path="/shop" component={Shop}/>
{/* 重定向 */}
<Redirect from="/" to="/home" exact/>
{/* 404页面需要放到最下面进行配置 */}
<Route component={No}/>
</Switch>
二级或者多级路由
1.新建页面
2.确定是当前二级路由的父级是那个组件 然后在其当中 直接来使用Route配置规则与出口
import React, { Component } from 'react'
// 1.需要把二级路由引进来
import Era from "./er/era.jsx"
import Erc from "./er/erc.jsx"
import Erd from "./er/erd.jsx"
// 2.需要引用Route
import {Route,NavLink} from "react-router-dom"
export default class shop extends Component {
render() {
return (
<div>
{/* 二级路由的导航 */}
<NavLink to="/shop/era">点我去a</NavLink>
<NavLink to="/shop/erc">点我去c</NavLink>
<NavLink to="/shop/erd">点我去d</NavLink>
shop
{/* 二级路由的规则 */}
<Route path="/shop/era" component={Era}/>
<Route path="/shop/erc" component={Erc}/>
<Route path="/shop/erd" component={Erd}/>
</div>
)
}
}
路由传参/动态路由匹配
params
-
在规则中配置接收参数
{/* 在需要接收参数的路由规则中设置接收参数 */}
<Route path="/phone/:xiaoming" component={Phone}/> -
发送`
import React, { Component } from 'react'
import Tb from "../components/topbar.jsx"
import {Link} from "react-router-dom"
export default class home extends Component {
fun=()=>{
// 编程式方式
this.props.history.push("/phone/我是参数")
}
render() {
return (
<div>
<Tb/>
home
{/* params传参 */}
{/* 声明式 */}
<Link to="/phone/我是参数">使用parms方式进行路由的传参</Link>
{/* 编程式 */}
<button onClick={()=>{this.fun()}}>点我编程式方式params传参</button>
</div>
)
}
}
-
接收
this.props.match.params.xxx
import React, { Component } from 'react'
export default class phone extends Component {
render() {
return (
<div>
{/* 接收params传参 */}
phone---{this.props.match.params.xiaoming}
</div>
)
}
}
params方式发送数据 只能传递字符串 并且传递数据的时候url会显示参数 不好看 刷新页面数据不丢失
state
1.发送
import React, { Component } from 'react'
import {Link} from "react-router-dom"
export default class era extends Component {
render() {
return (
<div>
era
{/* state方式路由传参 */}
{/* 声明式 */}
{/* <Link to={{pathname:"你去的地址",state:{key:val}}}>点我state传递参数</Link> */}
<Link to={{pathname:"/shop/erc",state:{xiaohong:"我是state发送的数据"}}}>点我state传递参数</Link>
{/* 编程式 */}
</div>
)
}
}
2.接收
this.props.location.state.xxx
import React, { Component } from 'react'
export default class erc extends Component {
render() {
return (
<div>
{/* state接收参数 */}
erc--{this.props.location.state.xiaohong}
</div>
)
}
}
state 在发送参数的时候url是不显示传递的数据的 可以发送对象等多个数据 刷新页面数据会丢失
路由的render渲染
就是在路由跳转渲染之前 我们提供了一个写逻辑代码的场地
` `{/* render渲染写法 */}
<Route path="/rd" render={()=>{
return false?<Rd/>:<Redirect to="/home"/>
}}/>
HOC 高阶组件
在react组件的创建过程中经常需要我们把多个组件中的一类功能进行重复的使用 我们其实就可以把这一类相同的功能进行提取公用 这样一来 我们在编写组件的时候就可以重复使用这一类提取的公共内容 减少了代码量与后期代码的维护难度。
HOC高阶组件------》参数是一个组件 并且返回值还是一个组件
使用:
1.创建文件夹用来容纳HOC的代码
// hoc 参数是一个组件 返回值还是一个组件
import React, { Component } from 'react'
let Hocdemo=(El)=>{ //因为参数今后传递过来的是一个组件所以首字母大写
return class element extends Component {
render() {
return (
<div>
<El/>--来自于2111班
</div>
)
}
}
}
export default Hocdemo
2.使用 在你想使用高阶组件复用的组件之内
import React, { Component } from 'react'
// 1.先引用HOC
import Hocdemo from "../hoc/element.js"
// 2.修改暴露到最下面
class hoca extends Component {
render() {
return (
<div>
aaaaaaaaaaaaaaaaaaa
</div>
)
}
}
// 3.暴露高阶组件 并且把当前组件当成高阶组件的参数传入
export default Hocdemo(hoca)
高阶组件---反向继承
HOC的反向继承 其中最核心的就是渲染劫持(拦截了渲染 可以在高阶组件中进行条件判断)
// hoc 参数是一个组件 返回值还是一个组件
import React, { Component } from 'react'
// 高阶组件里面需要条件渲染 那么其中需要条件
let Hocdemo=(El,num)=>{
// 如果要反向继承 那么首先 我们在返回的组件中就不能使用如下写法了
// return class element extends Component {
// 而是要使用
return class element extends El {
render() {
return (
<div>
<El/>
{num>=18? <h1>欢迎您尊敬的vip!!!</h1>:<h1>您不满18禁止浏览!!!</h1>}
</div>
)
}
}
}
export default Hocdemo
使用 传递条件为参数
import React, { Component } from 'react'
// 1.先引用HOC
import Hocdemo from "../hoc/element.js"
// 2.修改暴露到最下面
class hoca extends Component {
render() {
return (
<div>
aaaaaaaaaaaaaaaaaaa
</div>
)
}
}
// 3.暴露高阶组件 并且把当前组件当成高阶组件的参数传入
export default Hocdemo(hoca,19)
扩展--插入字符串 标签
import React, { Component } from 'react'
export default class erd extends Component {
constructor(){
super()
this.state={
newhtml:"<em>你好么么哒</em>"
}
}
render() {
return (
<div>
erd
<div>
{/* 直接 渲染这个html字符串 那么不会去解析这个标签 */}
{this.state.newhtml}
</div>
{/* 我们如果想插入的话 需要使用如下语法
但是注意 html前面是 两个底杠 不要写错了
*/}
<div dangerouslySetInnerHTML={{__html:this.state.newhtml}}>
</div>
</div>
)
}
}
生命周期
可以给我们在程序执行的时候提供一个自动执行的场地
初始化部分
import React, { Component } from 'react'
export default class demob extends Component {
constructor(){
super()
console.log("1 初始化数据")
}
componentWillMount() {
console.log("2 组件准备渲染的时候")
}
componentDidMount() {
console.log("4 组件渲染完毕");
}
render() {
console.log("3 开始渲染")
return (
<div>
</div>
)
}
}
数据更新
import React, { Component } from 'react'
export default class democ extends Component {
constructor(){
super()
this.state={
text:"你好"
}
}
shouldComponentUpdate(){
console.log("1 我是判断组件是否要更新 但是我里面必须有一个返回值 true更新 false不更新")
return true
}
componentWillUpdate() {
console.log("2 数据修改之前");
}
componentDidUpdate(){
console.log("4 数据修改之后")
}
fun=()=>{
this.setState({
text:"我改了"
})
}
render() {
console.log("3 正在修改页面")
return (
<div>
<h1>{this.state.text}</h1>
<button onClick={this.fun}>点我修改</button>
</div>
)
}
}
销毁
componentWillUnmount() {
console.log("销毁之前")
}
数据请求
axios
同vue 详见vue的笔记 一模一样
fetch
跨域
代理跨域 devServer这个项目中的内置开发服务器帮助我们解决跨域
我们解决跨域的代码和vue是一样的 但是写的位置不一样
node_modules/react-scripts/config/webpackDevserver.config.js中来进行配置
在其中找到proxy 并且用如下代码替换
proxy:{
"/api":{
target:"http://www.weather.com.cn",
changeOrigin:true,
"pathRewrite":{
"^/api":"/"
}
}
},
在请求的url中使用/api替代地址
重启重启重启重启重启重启重启重启重启重启重启重启重启重启
重启重启重启重启重启重启重启重启重启重启重启重启重启重启
重启重启重启重启重启重启重启重启重启重启重启重启重启重启
就是大家发现上面咱们解决跨域的时候 路径不太好找
扩展--弹射eject
弹射在自己写着玩的时候随便弹 但是如果去公司了 要弹射之前 一定要问清领导 让不让你弹 不让弹 你就别弹 出了问题不要找我 免责声明
弹射 就是把react项目中层级很深的配置文件弹到项目的根路径下(好处就是让我们配置文件的之后好找 但是缺点就是让我们的项目文件显得很乱)弹射不可逆 弹出来就回不去了
使用npm run eject 命令弹射
默认弹射可能会被错 所以我们需要在git上把项目提交一次 再次弹射
会发现项目根路径下多了一个config与scripts文件夹 就是配置文件弹到根路径下了
样式组件化--styled-components
styled-components 样式化组件,主要作用是它可以编写实际的CSS代码来设计组件样式,也不需要组件和样式之间的映射,即创建后就是一个正常的React 组件,并且可以附加样式给当前组件。 优化react组件
通过 js 赋能解决了原生 css 所不具备的能力,比如变量、循环、函数等
CSS代码时都有哪些痛点
全局污染 - CSS的选择器是全局生效的,所以在class名称比较简单时,容易引起全局选择器冲突,导致样式互相影响。
命名混乱 - 因为怕全局污染,所以日常起class名称时会尽量加长,这样不容易重复,但当项目由多人维护时,很容易导致命名风格不统一。
样式重用困难 - 有时虽然知道项目上已有一些相似的样式,但因为怕互相影响,不敢重用。
代码冗余 - 由于样式重用的困难性等问题,导致代码冗余。
潮流
现在随着组件化概念的流行,对从组件层面维护CSS样式的需求日益增大,CSS-in-JS就是在组件内部使用JavaScript对CSS进行了抽象,可以对其声明和加以维护。这样不仅降低了编写CSS样式带来的风险,也让开发变得更加轻松。它和CSS Modules的区别是不再需要CSS样式文件。
开发
1.下载:npm install --save styled-components
2.创建文件夹荣来容纳样式组件代码
import styled from "styled-components"
//首字母大写
export let Stylehome=styled.div`
h1{
color:red;
}
`
3.在想使用样式的组件中引用 并且进行使用
import React, { Component } from 'react'
// 引用样式组件
import {Stylehome} from "../styled/index.js"
export default class demo extends Component {
render() {
return (
<React.Fragment>
{/* 当成标签的方式进行使用 */}
<Stylehome>
<h1>我是一个内容</h1>
</Stylehome>
<h1>我是2个内容</h1>
</React.Fragment>
)
}
}

浙公网安备 33010602011771号