React核心原理与实际开发

学习目标

React是啥?

官方定义:将前端请求获取到的数据渲染为HTML视图JavaScript库

一、React入门

1、React项目创建

直接创建react,使用初始化会创建package.json

npm init -y

再安装

2、React基本使用

使用纯JS创建ReactDOM(元素)

<body>
<div id="root"></div>
<!--    1、引入js、react-dom文件--><script src="./node_modules/react/umd/react.development.js"></script><script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script>
//     2、使用JS创建react元素----虚拟DOM
//     参数:元素名称、元素属性、元素子节点const title = React.createElement('h1',null,'Hello react 我是你大爷')
//      3、渲染react元素
//      参数:要渲染的react元素、挂载点ReactDOM.render(title,document.getElementById('root'))
</script></body>

React.createElement()方法使用不是很灵活,知道就好;

ReactDOM.render()方法渲染react元素很重要!!!使用起来友好。

3、React脚手架搭建完整项目框架

使用react脚手架初始化项目,避免了使用<script>标签嵌入到html页面中很繁琐!!!

        IDEA创建React项目

1、初始化项目:项目目录下输入命令 

(base) wangjia@wangbangjia reactTest %  npx create-react-app my-app

2、启动项目:项目根目录下输入命令

(base) wangjia@wangbangjia reactTest % cd my-app
(base) wangjia@wangbangjia my-app % npm start

在index.html文件中直接按照上述编写三小步骤运行就行了

总结:使用react创建react元素是基本,至于将元素渲染到什么平台(iOS,安卓,虚拟显示等)就需要再导入对应的包即可。

4、JSX语法

1、JSX基本使用

JSX就是JavaScript XML简写,表示在JavaScript代码中写XML(HTML)格式的代码。

在浏览器实际运行时,仍然是将JSX语句转化为JS语句执行。

 // 2、创建react元素const title2 = <h3>这是JSX语法写的</h3>// 3、渲染react元素ReactDOM.render(title2,document.getElementById('root'))

2、JSX中使用JavaScript表达式

为啥子这么干???数据存储在JS中,想要显示就要在JSX中嵌入JS表达式。

语法:{JavaScript表达式}

 const jsappend = '这是嵌入的JS的数据'const age = '18'const title2 =(<h3>这是JSX语法写的 {jsappend},显示的年龄为: {age}</h3>)ReactDOM.render(title2,document.getElementById('root'))

3、JSX的条件渲染

在创建react元素时引入的JS表达式是包含if-else、三元表达式、逻辑与运算符。

const isLoading = true// if-else// const loadData = () =>{//  if(isLoading){//   return <div>loading...</div>//  }//  return <div>数据加载完成,此处显示加载后的数据</div>// }//三元表达式
//  const loadData = () =>{
//    return isLoading ?  <div>loading...</div> : <div>数据加载完成,此处显示加载后的数据</div>
//  }
//
//  逻辑与运算符const loadData=() =>{return isLoading && (<div>数据加载完成,此处显示加载后的数据</div>)}// 创建react元素  //函数调用作为表达式
const title=(<h1>条件渲染:{loadData()}</h1>
)// 渲染react元素ReactDOM.render(title,document.getElementById('root'))

4、JSX的列表渲染

创建react列表时应添加key属性,且唯一;map遍历谁就要给谁添加key属性

 使用map方法遍历列表所有元素

const  songs = [{id:1,name:'我是你大爷'},{id:2,name:'你是我好大儿'},{id:3,name:'你是我孙子'},
]const list = (<ul>{songs.map(item => <li key={item.id}> {item.name}</li>)}</ul>)ReactDOM.render(list,document.getElementById('root'))

5、JSX的样式处理

使用类名:className

css类index.css

.title{text-align:center;color: crimson;size: A4;background-color: #61dafb;}

导入css类 所在文件 import './index.css' ;确定类名 className="title"

 import './index.css'const test = (<h1 className="title">JSX的样式处理</h1>
)ReactDOM.render(test,document.getElementById('root'))

总结:JSX确定结构,JSX确定样式

二、React面向组件编程

学习目标:

1、组件创建俩方式

        1、函数式

    <div id="root"></div><div id="demo"></div><script type="text/babel">const title = <h1>react中的函数式组件</h1>// 1、创建普通函数式组件function MyComponent(){console.log(this);return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>;}// 2、创建箭头函数式组件const Button = () => {return <div>这是使用箭头函数创建的函数组件</div>}// 3、使用组件const content = (<div>{title};{<MyComponent/>}{<Button/>}</div>  )// 4、渲染组件到页面// ReactDOM.render(<MyComponent/>,document.getElementById('root'));ReactDOM.render(content,document.getElementById('root'));
/*
执行ReactDOM.render(<MyComponent/>怎么做的?
1、React解析组件标签,找到MyComponent组件
2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM作为真实DOM呈现在页面上*/</script>

        2、类方式

        <div id="root"></div><div id="demo"></div><script type="text/babel">const title = (<h1>React中的类组件</h1>)// 1、创建类式组件 必须继承React.Component类class MyComponent extends React.Component{render(){// render方法放在了哪里? -- MyComponent类的原型对象上,供实例使用// render中的this就是MyComponent组件实例对象return <div>类组件返回,适用于复杂组件</div>}}const content = (<div>{title}{<MyComponent/>}</div>)ReactDOM.render(content,document.getElementById('root'));/*执行ReactDOM.render(<MyComponent/>怎么做的?1、React解析组件标签,找到MyComponent组件2、发现组件是使用类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法 3、将render返回的虚拟DOM转为真实DOM呈现在页面上*/</script> 

2、组件三大属性

1、state

理解

(1)state值是对象(可包含多个key-value)

(2)组件被称为”状态机“,通过更新组件来更新对应的页面

强烈注意

1、组件中render方法中的this指向为组件实例对象

2、组件自定义方法中this为undefined,咋解决?

a、强制绑定this:通过函数bind()

b、使用箭头函数

3、状态数据,不可直接更新,要使用setState

应用场景:state也就是状态变化,多用于触发事件等

标准版本及详解->

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script type="text/javascript" src="../js/react.development.js"></script><!-- 引入react-dom.用于支持react操作DOM --><script type="text/javascript" src="../js/react-dom.development.js"></script><!-- 引入babel,用于将jsx转为js --><script type="text/javascript" src="../js/babel.min.js"></script><!-- 创建真实dom元素节点 --><div id="root"></div><div id="demo"></div><script type="text/babel">const title = <h1>组件的属性--state</h1>//  1、创建组件class Weather extends React.Component{// 借助构造器初始化状态  -- 构造器中的this肯定是指向实例对象的constructor(props){super(props)// 初始化状态this.state = {isHot : false}/*使用原型上的方法changeWeather再调用bind传参实例创建一个新的函数,而且此新函数的this已变成类Weather的实例对象,然后将此新函数传给类Weather的实例自身,并起一个新的名字。那么直接调用此函数,就可以获取到简言之:拿原型上的方法生成一个新的方法挂在实例自身上~原型上的方法 ~实例上的方法 -- 可使用指向实例的this直接调用*///    解决changeWeather中this指向问题this.test = this.changeWeather.bind(this);}render(){const {isHot} = this.state// 读出实例对象的状态并使用  return <h1 onClick = {this.test}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>}changeWeather(){//严重注意:状态(state)不可直接修改 this.state.isHot = !isHot 就是不行的const isHot = this.state.isHot;this.setState({isHot:!isHot})}/*changeWeather放在那里?  --- Weather的原型对象上,供实例使用由于changeWeather 是作为onClick的回调,不是直接通过实例调用的,是直接调用类中的方法默认开启了局部的严格模式,所以changeWeather中this为undefined*/}/*整个组件运行机制:当按点击click时就开始调用函数this.test,并且执行将changeWeather函数添加到实例对象中修改当前状态,然后由改变后的状态进行页面修改1、构造器调用几次? 1次,用作实例化对象时2、render调用几次?1+n次,1是初始化那次,n是状态更新的次数3、changeWeather调用几次? 点击几次就调用几次*//*this指向总结:1、构造器里的this指向实例对象 常识了2、render中this指向实例对象,是React悄悄 const w1=new Weather() 然后执行w1.render()也就是说render是由实例对象调用的 肯定里面的this指向实例对象了啦3、然后changeWeather就只是咱们自定义的一个函数,并不是由实例对象调用,而是作为一个事件的回调在使用,当触发事件时直接拉出来调用函数changeWeather,并且由于类中的方法默认开启了严格模式,导致this丢了,按道理讲类中的this肯定指向实例对象但是丢了就指向undefined,this都没有指向实例对象,咋可能调用方法changeWeather呢*/const content =  (<div>{title}{<Weather/>}</div>)// 3、渲染组件到页面ReactDOM.render(content,document.getElementById('root'))</script>
</body></html>

简化版本--企业开发

// 1、创建组件class Weather extends React.Component{// 初始化状态state = { isHot: false }// 触发事件render(){const {isHot} = this.statereturn <h2 onClick = {this.changeWeather}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h2>}// 自定义方法--要用赋值语句+箭头函数changeWeather = () =>{const isHot = this.state.isHot;this.setState({isHot:!isHot})}}
!!!精华:此处的changeWeather就是组件Weather的一个变量,被赋值一个函数,那么在this.changeWeather拿到的就是一个函数传给onClick,而不是直接被调用返回数值给onClick

2、props

        官方定义为 --> 单向数据流值

        作用:接收外部数据(这是别人给的,只读!)

        传递数据: 通过给组件标签添加属性

        接收数据:函数组件通过 参数 props接收数据,类组件通过 this.props接收数据

        1、对标签限制 --类型、必要性

    // 对标签属性进行类型、必要性的限制Person.propTypes = {name:PropTypes.string.isRequired,//限制name必传,且为字符串sex:PropTypes.string,age:PropTypes.number,speak:PropTypes.func//限制speak必须是函数}//指定默认标签属性值Person.defaultProps = {sex:'不男不女',age:18}

        2、向组件传递数据 -- props批量传递

    // 批量传递props//注意: 冒号: 表示键值对 用于对象中 等于号= 用于给常量赋值const p = {name:"23",age:23,sex:"男"}ReactDOM.render(<Person {...p} />,document.getElementById('test3'))

        3、简写完整版

class Person extends React.Component{// 对标签属性进行类型、必要性的限制static propTypes = {name:PropTypes.string.isRequired,//限制name必传,且为字符串sex:PropTypes.string,age:PropTypes.number,speak:PropTypes.func//限制speak必须是函数}//指定默认标签属性值static defaultProps = {sex:'不男不女',age:18}state = {}render(){const{name,age,sex} = this.props// 获取到的props数据流是只读的// this.props.name = 'jack' 会报错return(<ul><li>{name}</li><li>{sex}</li><li>{age}</li></ul>)}// 对组件标签进行限制}ReactDOM.render(<Person name= "{23}"  speak = {speak}/>, document.getElementById('test1'))ReactDOM.render(<Person name="小刘" age={12} sex="女"/>, document.getElementById('test2'))// 批量传递props//注意: 冒号: 表示键值对 用于对象中 等于号= 用于给常量赋值const p = {name:"23",age:23,sex:"男"}ReactDOM.render(<Person {...p} />, document.getElementById('test3'))function speak(){console.log(这是一段话);}</script>

实现组件通信方法    --  定义为父子组件

        将父组件的state作为子组件的props,当父组件的state改变,子组件的props也跟着改变,其实它仍旧遵循了这一定律:props是不可更改的。

子组件调用父组件的方法

(1)子组件要拿到父组件的属性,需要通过 this.props 方法。

(2)同样地,如果子组件想要调用父组件的方法,只需父组件把要被调用的方法以属性的方式放在子组件上, 子组件内部便可以通过“this.props.被调用的方法”这样的方式来获取父组件传过来的方法。

父组件传参数、函数,子组件接收实例

import React, { Component, Fragment } from "react";
//React的props传参
// 父组件
class App extends Component {render() {return (<Fragment><Child name="卡卡罗特" jineng={this.bianshen}></Child></Fragment>);}bianshen() {return "变身超级赛亚人";}
}
// 子组件
class Child extends Component {render() {return (<div>{this.props.name}{this.props.jineng()}</div>);}
}export default App;

父组件调用子组件的方法 在 ReactJS 中有个叫 ref 的属性。这个属性就像给组件起个引用名字一样,子组件被设置为 ref 之后(比如 ref=“xxx”)。父组件便可以通过 this.refs.xxx 来获取到子组件了。

3、ref

组件内的标签定义ref标识自己

字符串形式 -- 简单好用但逐渐过时

<script type="text/babel">// 创建组件class Demo extends React.Component{// 展示左侧输入框的数据showData = ()=>{const {input1} = this.refsalert(input1.value)}// 展示右侧输入框的数据showData2 = ()=>{const {input2} = this.refsalert(input2.value)}render(){return(<div><input ref="input1" type="text" placeholder="点击按钮提示数据"/><button onClick={this.showData}>点我提示左侧的数据</button><input ref="input2" onBlur={this.showData2}type="text" placeholder="失去焦点提示数据"/></div>)}}ReactDOM.render(<Demo/>,document.getElementById("test1"))</script>

回调形式 -- 麻烦

<script type="text/babel">// 创建组件class Demo extends React.Component{// 展示左侧输入框的数据showData = ()=>{const {input1} = this alert(input1.value)}// 展示右侧输入框的数据showData2 = ()=>{const {input2} = this alert(input2.value)}render(){//    使用回调函数,把input1挂载到组件实例对象this上 从而直接获取input1return(<div><input ref={cur => this.input1 = cur} type="text" placeholder="点击按钮提示数据"/><button onClick={this.showData}>点我提示左侧的数据</button><input onBlur={this.showData2} ref={cur => this.input2 = cur}  type="text" placeholder="失去焦点提示数据"/></div>)}}ReactDOM.render(<Demo/>,document.getElementById("test1"))</script>

createRef -- 官方最新

<script type="text/babel">// 创建组件class Demo extends React.Component{// React.createRef调用后返回一个容器 可存储被ref标识的节点 但只能一个myRef = React.createRef()myRef2 = React.createRef()// 展示左侧输入框的数据showData = ()=>{alert(this.myRef.current.value)}// 展示右侧输入框的数据showData2 = ()=>{alert(this.myRef2.current.value)}render(){return(<div><input  ref={this.myRef} type="text" placeholder="点击按钮提示数据"/><button onClick={this.showData}>点我提示左侧的数据</button><input  onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="点击按钮提示数据"/></div>)}}ReactDOM.render(<Demo/>,document.getElementById("test1"))</script>

3、事件处理

将发生的事件作为参数

        class Demo extends React.Component{// React.createRef调用后返回一个容器 可存储被ref标识的节点 但只能一个myRef = React.createRef()myRef2 = React.createRef()// 展示左侧输入框的数据showData = ()=>{alert(this.myRef.current.value)}// 展示右侧输入框的数据showData2 = (event)=>{alert(event.target.value)}render(){return(<div><input  ref={this.myRef} type="text" placeholder="点击按钮提示数据"/><button onClick={this.showData}>点我提示左侧的数据</button>{/*发生事件的元素刚好是要操作的元素,就可省略ref*/}<input  onBlur={this.showData2}  type="text" placeholder="点击按钮提示数据"/></div>)}// 事件处理中,点击第二个输入文本就是一个未指定的事件,在showData2函数中将点击事件作为参数获取值显示}ReactDOM.render(<Demo/>,document.getElementById("test1"))

4、React生命周期

旧版本

组件的钩子中只有三个钩子常用

1、初始化阶段:由ReactDOM.render()出发 -- 初次渲染

                        1、constructor()

                        2、componentWillMount()

                        3、render()

                        4、componentDidMount() ====》常用

             一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

2、更新阶段:有组件内部this.setState()或父组件render触发

                        1、componentWillReceiveProps()

                        2、shouldComponentUpdate()

                        3、componentWillUpdate()

                        4、render() 必用

                        5、componentDidUpdate()

3、卸载阶段:由ReactDOM.unmountComponentAtNode()触发

                        1、componentWillUnmount() ===》常用

                                一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

基本钩子实例

<script type="text/babel">class Count extends React.Component{//1、构造器constructor(props){super(props)console.log('1、Count --- Constructor')// 初始化状态this.state = {count:0}}//  各个事件的回调函数add = () => {// 获取原状态const{count} = this.state// 更新状态this.setState({count:count+1})}//卸载组件按钮的回调death = () => {ReactDOM.unmountComponentAtNode(document.getElementById('test1'))}force = () => {this.forceUpdate()}// 2、组件将要挂载的钩子 -- componentWillMountcomponentWillMount(){console.log('2、Count --- componentWillMount')}// 4、组件挂载完毕的钩子 -- componentDidMountcomponentDidMount(){console.log('4、Count --- componentDidMount')}// 4、2组件更新完毕的钩子componentDidUpdate(){console.log('4.2、Count --- componentDidUpdate')}// 5、组件将要卸载的钩子 -- componentWillUnmountcomponentWillUnmount(){console.log('5、Count --- componentWillUnmount')}// 3.1前置、判断组件是否可更新的钩子shouldComponentUpdate(){console.log('3.1前置、Count --- shouldComponentUpdate')return true}// 3.2前置、组件将要更新的钩子componentWillUpdate(){console.log('3.2前置、Count --- ComponentWillUpdate')}// 3、renderrender(){console.log('3、Count --- render')const {count} = this.statereturn(<div><h2>当前求和为:{count}</h2><button onClick={this.add}>点我+1</button><button onClick={this.death}>卸载组件</button><button onClick={this.force}>不输入数据就是要强制更新一下</button></div> )}}// 渲染组件ReactDOM.render(<Count/>,document.getElementById("test1"))

父组件与子组件

 class A extends React.Component{state = {carName:'宝马'}changeCar = () =>{this.setState({carName:'奥迪'})}render(){return(<div><div>我是A组件</div>    <button onClick={this.changeCar}>换车</button><B receiveCarName = {this.state.carName}/></div>)}}class B extends React.Component{render(){return(<div>我是B组件,接收到父组件A的车是:{this.props.receiveCarName}</div>)}}ReactDOM.render(<A/>,document.getElementById('test1'))

新版本

新版本与旧版本区别

1、新版本废弃了三个will钩子:

componentWillMount()、componentWillUpdate()、componentWillReceiveProps()

 2、增加了俩个钩子:

getDerivedStateProps()、getSnapshotBeforeUpdate()

其他的初始化组件、更新组件、卸载组件三大生命周期过程就原样。

// getDerivedStateFromProps作用:当前组件的state的值在任何时候都取决于外部传入的props  基本不用 static getDerivedStateFromProps(props,state){console.log('执行getDerivedStateFromProps')return props}// 更新之前获取快照getSnapshotBeforeUpdate(){console.log('执行getSnapshotBeforeUpdate')return 'atguigu'}

1、初始化阶段:由ReactDOM.render()出发 -- 初次渲染

                        1、constructor()

                        2、getDerivedStateProps()

                        3、render()

                        4、componentDidMount() ====》常用

一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

2、更新阶段:有组件内部this.setState()或父组件render触发

                        1、getDerivedStateProps()

                        2、shouldComponentUpdate()

                        3、render() 必用

                        4、getSnapshotBeforeUpdate()

                        5、componentDidUpdate()

3、卸载阶段:由ReactDOM.unmountComponentAtNode()触发

                        1、componentWillUnmount() ===》常用

                        一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息 -->

三、React应用(基于React脚手架)

create-react-app库提供脚手架,首先就要全局安装create-react-app库,

npm i -g create-react-app

接着就是使用库安装脚手架。

create-react-app  [脚手架名字]

详细讲解见我的另一篇React脚手架-详细解析目录与运行-CSDN博客

实例todoList    

        见代码:/Users/wangjia/Desktop/web前端/react脚手架/03_src

四、React Ajax

由于Ajax同源策略,涉及到跨域问题无法接收请求,使用代理配置--实现url转换。

方法一

在package.json中追加如下配置
"proxy":"http://localhost:5000"

说明:

1. 优点:配置简单,前端请求资源时可以不加任何前缀。
2. 缺点:不能配置多个代理。
3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

方法二

1. 第一步:创建代理配置文件
   在src下创建配置文件:src/setupProxy.js

2. 编写setupProxy.js配置具体代理规则:
   const { createProxyMiddleware } = require("http-proxy-middleware")
   module.exports = function(app) {
     app.use(
       createProxyMiddleware('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
         target: 'http://localhost:5000',              //配置转发目标地址(能返回数据的服务器地址)
         changeOrigin: true,                           //控制服务器接收到的请求头中host字段的值
         /*
             changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
             changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
             changeOrigin默认值为false,但我们一般将changeOrigin值设为true
         */
         pathRewrite: {'^/api1': ''}

                //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
       })
     )
   }
   ```

说明:

1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
2. 缺点:配置繁琐,前端请求资源时必须加前缀。

props---实现组件通信

App.js

export default class App extends Component {state = {users:[],//users初始值为数组isFirst:true,//是否为第一次打开页面isLoading:false,//标示是否处于加载中err:''//存储请求相关的错误信息} // 通用 -- 更新App的stateupdateAppState = (stateObj) => {this.setState(stateObj)}render() {return (<div className="container"><Search updateAppState={this.updateAppState}/><List {...this.state}/></div>)}
}

Search.js

export default class Search extends Component {search = () => {// 1、获取用户的输入  -- 连续解构赋值const{keyWordElement:{value:keyWord}} = this//  发送请求前通知App更新状态this.props.updateAppState({isFirst:false,isLoading:true})// 2、发送网络请求 拿到返回的数据    给谁发?url  用啥方法?method 带啥参数? params  站在3000给5000发请求 - 配代理// 站在3000给3000发,3000有代理转发给5000axios.get(`http://localhost:3000/api1/search/users2?q=${keyWord}`).then(response => {//请求成功后通知App更新状态this.props.updateAppState({isLoading:false,users:response.data.items})},error => {//请求失败后通知App更新状态this.props.updateAppState({isLoading:false,err:error.message})})}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">Search Github Users</h3><div><input  ref={cur => this.keyWordElement = cur} type="text" placeholder="enter the name you search"/>&nbsp;<button onClick={this.search}>搜索</button></div></section>)}

List.js

export default class List extends Component {render() {const {users,isFirst,isLoading,err} = this.propsreturn (// JSX使用三元表达式,不可以判断语句  多个条件要判断使用连续三元表达式<div className="row">{isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :isLoading ? <h2>Loading.......</h2> :err ? <h2 style={{color:'red'}}>{err}</h2> :users.map((userObj)=>{return(<div key={userObj.id} className="card"><a rel="noreferrer" href={userObj.html_url} target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>

1、axios-发送HTTP请求

Axios 是一个基于 promise 网络请求库。

fetch用于请求,内置的。

xhr用于发送http请求,jQuery与axios都是对xhr的封装。底层还都是xhr。只不过xhr的API太繁琐。所以要使用就要安装jQuery与axios。

也就是用来发送Ajax请求时使用的网络请求库。也就是发送HTTP请求的

const axios = require("axios");
​
// 向给定 ID 的用户发起请求
axios.get("/user?ID=12345").then(function (response) {//处理成功情况console.log(response);}).catch(function (error) {//处理错误情况console.log(error);}).then(function () {//总是会执行});

2、消息订阅机制-组件通信

消息订阅与取消 -- 在组件完成挂载时接收消息、在组件卸载时取消订阅

接收消息:PubSub.subscribe(mag,function())

// 订阅消息--接收消息componentDidMount(){this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{this.setState(stateObj)})}// 取消订阅componentWillUnmount(){PubSub.unsubscribe(this.token)}

发送消息PubSub.publish(mag,data)

PubSub.publish('atguigu',{isFirst:false,isLoading:true})
PubSub.publish('atguigu',{isLoading:false,err:error.message})

使用fetch发送请求,我这个java程序员,就不深入学喽。

五、React-router

1、路由基本概念

路由工作原理:

之前是多页面更新,(多个.html),一次刷新就要整个页面全部刷新,重新挂载dom。

如今页面更新方式,采用单页面多组件,刷新也只是页面局部刷新。

路由:就是一对映射关系(key : value)

key为路径;value为function或component

路由分类:

2、React-router-dom(前端路由)

1、路由组件基本使用

React的插件库。

使用路由套路:1、导航区在哪?(导航区内选项即为路由链接)  2、展示区在哪?

详细过程:点击导航区内选项,引起路径变化; 路径变化被前端路由器监测到,并匹配组件,从而展示。

    1.明确好界面中的导航区、展示区2.导航区的a标签改为Link标签<Link to="/xxxx">Demo</Link>3.展示区写Route标签进行路径的匹配<Route path='/xxxx’ component={Demo}/>4. <App>的最外侧包襄了一个<BrowserRouter>或<HashRouter>

1、编写路由链接

<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>

2、注册路由

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

3、使用唯一的BrowserRouter 监听

为了使唯一的监听器能够监听到 路由链接切换并注册路由,以及其他事情,将BrowserRouter直接放在index.js挂载组件的标签外

<BrowserRouter>

<App />

</BrowserRouter>

* 原生HTML中,靠<a>跳转不同的页面

 <a className="list-group-item" href="./about.html">About</a>

* 在React中靠路由链接实现切换组件

<BrowserRouter>

<Link className="list-group-item" to="/about">About</link>

</BrowserRouter>

路由组件与一般组件

路由组件:存储在pages文件夹下

                   使用路由匹配引用  <Route path="/home" component={Home}/>

                   路由组件接收到路由器传送的props中三个固定属性:history、location、match

一般组件:存储在components文件夹下

                  使用标签引用  <Header/>

                  一般组件标签内传啥,props就包含啥

路由组件关键三个属性

history:
    go: function go(n)​​
    goBack: function goBack()​​
    goForward: function goForward()
    ​​push: function push(path, state)​​
    replace: function replace(path, state)​​
​location:
    pathname: "/home"
    search: ""
    ​​state: undefined
​match:
    ​​params: Object {  }
    ​​path: "/home"
    ​​url: "/home"

组件封装 :使用 一般组件 封装 路由组件

向组件属性传值

<MyNavLink to="/about">About</MyNavLink>

<MyNavLink to="/home">Home</MyNavLink>

被封装的组件直接使用...this.props接收(固定值直接写死,变化值使用this.props传输)

<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>

注意:标签体内容是特殊的标签属性

标签体内容this.props.children可以直接在标签属性中使用children代替。

<GGB>this.props.children</GGB>   <=>  <GGB children></GGB>

使用Switch组件提高(注册路由)匹配效率

一般情况,路由器监测到路径改变后,就会与所有注册路由匹配,匹配成功后依然匹配,效率低!

使用Switch组件后,匹配成功停止匹配。

注册路由内path与component是一一对应,使用Switch进行单一匹配。

<Switch>

<Route path="/about" component={About}/>

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

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

</Switch>

严格匹配与模糊匹配

 路由链接与注册路由在匹配时,默认开启的是模糊匹配。只要路由链接开始就包含注册路由,依然可以匹配成功。

        <MyNavLink to="/home/a/b">Home</MyNavLink>

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

当有exact时,即开启了严格匹配

        <MyNavLink to="/home/a/b">Home</MyNavLink>

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

路由一旦开启严格匹配,那么其子路由全部作废,无法匹配

因此对于多级路由,根路由不可开启严格匹配

重定向默认匹配

刚打开时,localhost:3000/  其中/ 与注册路由逐一匹配,失败后就啥也不显示,但是希望可以上来就有一个默认显示的。

使用Redirect重定向,执行路由链接与注册路由逐一匹配时,当全部匹配失败,就执行Redirect,后面跟上默认匹配的注册路由,进行展示。

Redirect写在所有注册路由最下方。

{/* 注册路由 */}

<Switch>

<Route path="/about" component={About}/>

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

<Redirect to="/home"/>

</Switch>

2、嵌套路由---多级路由

1、子路由的链接与注册均要写上父路由的path值---也就是多级路由一定要完整写出。

2、路由的匹配是按照路由的注册顺序尽心匹配。先进行父路由的匹配,再进行子路由的完整匹配

<ul className="nav nav-tabs">

        <li>

<MyNavLink to="/home/news">News</MyNavLink>

        </li>

        <li>

<MyNavLink to="/home/message">Message</MyNavLink>

        </li>

</ul>

<Switch>

        <Route path="/home/news" component={News}/>

        <Route path="/home/message" component={Message}/>

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

</Switch>

3、传递路由参数

也就是在路由链接后面加上要传递到目的组件的参数。有params、search、state三大类参数

1、params参数  

{/* 向路由组件传递params参数  --- 在链接路由路径后 携带参数*/}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>{/* 声明接收params参数 --- 在路由路径后直接声明接收参数 */}
<Route path='/home/message/detail/:id/:title' component={Detail}/>// 接收params参数
const {id,title} = this.props.match.params

2、search参数

 {/* 向路由组件传递Search参数 */}
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>{/* 无需 声明接收Search参数 */}
<Route path='/home/message/detail' component={Detail}/>//接受Search参数 --- 需要将字符串转换为key-value
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))

3、state参数 

此state不是组件的状态,就是history的location下的state属性!

{/* 向路由组件传递state参数 -- 是个对象!!!! */}
<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link> {/* 无需 声明接收state参数 */}
<Route path='/home/message/detail' component={Detail}/>//接受state参数
const {id,title} = this.props.location.state

4、路由跳转俩模式push&replace

4、编程式路由导航

5、withRouter

withRouter可以加工一般组件,使其具备路由组件所特有的API,比如this.props.history

import React, { Component } from 'react'
import { withRouter } from 'react-router-dom/cjs/react-router-dom.min'
class Header extends Component {back = () => {this.props.history.goBack()}forward = () => {this.props.history.goForward()}go = () => {this.props.history.go(-2)}render() {console.log(this.props.history)return (<div className="page-header"><h2>React Router Demo</h2><button onClick={this.back}>回退</button>&nbsp;<button onClick={this.forward}>前进</button>&nbsp;<button onClick={this.go}>go</button> </div>)}
}
export default withRouter(Header)

六、BrowserRouter和HashRouter区别

六、React UI组件库

ant-design  前端UI组件库

七、redux

1、redux理解

定义:专门用作状态管理的js库(不是React插件)

作用:集中式管理React应用中多个组件共享的状态。

啥时候用?

共享:某个组件的状态,可以让其他组件随时拿到。

通信:一个组件需要改变另一个组件的状态。

使用原则:能不用就不用。实在是使用消息订阅或者props吃力才使用。

2、redux原理

action

        动作的对象

包含2个属性:

  1. type:标识属性, 值为字符串, 唯一, 必要属性
  2. data:数据属性, 值类型任意, 可选属性

例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的stateaction 产生新的state纯函数

store

state action 、reducer联系在一起的对象  如何得到此对象?
  • import {createStore} from 'redux'
  • import reducer from './reducers'
  • const store = createStore(reducer)

此对象的功能?

  1. getState(): 得到state
  2. dispatch(action): 分发action, 触发reducer调用, 产生新的state
  3. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

 八、扩展

1、setState更新状态的2种写法

总结:

1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
        (1).如果新状态不依赖于原状态 ===> 使用对象方式
        (2).如果新状态依赖于原状态 ===> 使用函数方式
        (3).如果需在setState()执行后获取最新状态数据,要在第二个callback函数中读取

1.setState(stateChange, [callback])------对象式setState

  • stateChange为状态改变对象(该对象可以体现出状态的更改)
  • callback可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
import React, { Component } from 'react'export default class Demo extends Component {state = {count:0}add = ()=>{//对象式的setState//1.获取原来的count值const {count} = this.state//2.更新状态this.setState({count:count+1},()=>{console.log(this.state.count);//1})console.log('12行的输出',this.state.count); //0}render() {return (<div><h1>当前求和为:{this.state.count}</h1><button onClick={this.add}>点我+1</button></div>)}
}

2.setState(updater, [callback])------函数式setState

  • updater为返回stateChange对象的函数。
  • updater可以接收到stateprops
  • callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用
import React, { Component } from 'react'export default class Demo extends Component {state = {count:0}add = ()=>{//函数式的setStatethis.setState( (state,props) => ({count:state.count+1}),()=>{//回调中拿到的是render之后的新数据console.log(state,props);})}render() {return (<div><h1>当前求和为:{this.state.count}</h1><button onClick={this.add}>点我+1</button></div>)}
}

2. lazyLoad

路由组件的lazyLoad   懒加载--将页面显示优先加载,资源懒加载,提高页面展示速度

	//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包const Login = lazy(()=>import('@/pages/Login'))//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面<Suspense fallback={<h1>loading.....</h1>}><Switch><Route path="/xxx" component={Xxxx}/><Redirect to="/login"/></Switch></Suspense>

 3、Hooks

 State Hook
(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:参数: 第一次初始化指定的值在内部作缓存返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
function Demo(){const [count,setCount] = React.useState(0)//加的回调function add(){//setCount(count+1) //第一种写法setCount(count => count+1 )}return (<div><button onClick={add}>点我+1</button></div>)
}
export default Demo
Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:发ajax请求数据获取设置订阅 / 启动定时器手动更改真实DOM
(3). 语法和说明: useEffect(() => { // 在此可以执行任何带副作用操作return () => { // 在组件卸载前执行// 在此做一些收尾工作, 比如清除定时器/取消订阅等}}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行(4). 可以把 useEffect Hook 看做如下三个函数的组合componentDidMount()componentDidUpdate()componentWillUnmount() 

 

 Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样

4、render props

如何向组件内部动态传入带内容的结构(标签)?

Vue中: 使用slot技术, 也就是通过组件标签体传入结构  <A><B/></A>
React中:使用children props: 通过组件标签体传入结构使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

children props

<A><B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到 

render props

<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data} 

 

5、组件通信方式总结

组件间的关系:

  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)

几种通信方式:

	1.props:(1).children props(2).render props2.消息订阅-发布:pubs-sub、event等等3.集中式管理:redux、dva等等4.conText:生产者-消费者模式

比较好的搭配方式:

	父子组件:props兄弟组件:消息订阅-发布、集中式管理祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/97640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink学习笔记(二):Flink内存模型

文章目录 1、配置总内存2、JobManager 内存模型3、TaskManager 内存模型4、图形化展示5、实际案例计算内存分配 1、配置总内存 Flink JVM 进程的进程总内存&#xff08;Total Process Memory&#xff09;包含了由 Flink 应用使用的内存&#xff08;Flink 总内存&#xff09;以…

iTunes更新iOS17出现发生未知错误4000的原因和解决方案

有不少人使用iTunes更新iOS 17时出现「无法更新iPhone发生未知的错误4000」的错误提示&#xff0c;不仅不知道iTunes升级失败的原因&#xff0c;也无从解决iPhone无法更新4000的问题。 小编今天就分享iPhone更新iOS系统出现4000错误提示的原因和对应的解决方案。 为什么iPhone…

MySQL Cluster 简介

文章目录 1.简介2.组成参考文献 1.简介 MySQL Cluster 是官方推出的基于 NDB&#xff08;Network DataBase&#xff09;存储引擎的高可用和可伸缩的分布式数据库系统。 以下是 MySQL NDB Cluster 的主要特点和能力&#xff1a; 高可用&#xff1a;MySQL Cluster 具有内置的高…

Python大数据之PySpark(七)SparkCore案例

文章目录 SparkCore案例PySpark实现SouGou统计分析 总结后记 SparkCore案例 PySpark实现SouGou统计分析 jieba分词&#xff1a; pip install jieba 从哪里下载pypi 三种分词模式 精确模式&#xff0c;试图将句子最精确地切开&#xff0c;适合文本分析&#xff1b;默认的方…

洗地机怎么选?2023年洗地机推荐

洗地机结合洗地、拖地、扫地的功能&#xff0c;在日常生活中备受关注&#xff0c;他能帮助我们更加节省时间和节省体力&#xff0c;但是面对参差不齐的洗地机市场如何选到适合自己的呢&#xff0c;下文整理了几款非常值得入手的性价比型号&#xff0c;供大家选择参考。 一、CE…

C++Day2

#include <iostream>using namespace std;class Rect { private:int width;int height; public:void init(int w,int h){width w;height h;}void set_w(int w){width w;}void set_h(int h){height h;}void show(){cout << "矩形的周长为&#xff1a;"…

Java数组:没错,不装了我就是书架。

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、数组的概念1、什么是数组&#xff1f;2、数组的创建3、数组的初始化Ⅰ、动态初始化Ⅱ、静态初始化 二、数组的使用1、数组中…

Windows系统上使用CLion远程开发Linux程序

CLion远程开发Linux程序 情景说明Ubuntu配置CLion配置同步 情景说明 在Windows系统上使用CLion开发Linux程序&#xff0c;安装CLion集成化开发环境时会自动安装cmake、mingw&#xff0c;代码提示功能也比较友好。 但是在socket开发时&#xff0c;包含sys/socket.h头文件时&am…

【Java-LangChain:使用 ChatGPT API 搭建系统-4】评估输入-分类

第三章&#xff0c;评估输入-分类 如果您正在构建一个允许用户输入信息的系统&#xff0c;首先要确保人们在负责任地使用系统&#xff0c;以及他们没有试图以某种方式滥用系统&#xff0c;这是非常重要的。 在本章中&#xff0c;我们将介绍几种策略来实现这一目标。 我们将学习…

【yolo系列:YOLOV7改进-添加EIOU,SIOU,AlphaIOU,FocalEIOU.】

yolo系列文章目录 在YoloV7中添加EIoU,SIoU,AlphaIoU,FocalEIoU,Wise-IoU. 2023-2-7 更新 yolov7添加Wise-IoUB站链接 重磅&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; YOLO系列模型改进损失函数 文章目录 yolo系列文章目录一、初始的yolov7损失函数二、首…

7346-2015 控制电机基本外形结构型式

声明 本文是学习GB-T 7346-2015 控制电机基本外形结构型式.pdf而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了控制电机的机座号、外形及安装尺寸、轴伸型式、出线方式、标记及铭牌。 本标准适用于各类控制电机(以下简称电机),其…

NFT Insider#110:The Sandbox与TB Media Global合作,YGG Web3游戏峰会阵容揭晓

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members、BeepCrypto出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据&#xff0c;艺术新闻类&#xff0c;游戏新闻类&#xff0c;虚拟世界类&#xff0…

数据结构面试常问问题--保研及考研复试

前言&#xff1a; Hello大家好&#xff0c;我是Dream。今年保研上岸山东大学人工智能专业 &#xff08;经验贴&#xff09;&#xff0c;现在将我自己的专业课备考知识点整理出来&#xff0c;分享给大家&#xff0c;希望可以帮助到大家&#xff01;这是重点知识总结&#xff0c;…

为什么短视频离不开美颜SDK?短视频领域的秘密武器

在当今的社交媒体时代&#xff0c;短视频已经成为了人们获取信息、娱乐和社交的重要方式。无论是抖音、快手&#xff0c;还是Instagram、TikTok&#xff0c;短视频都以其独特的魅力吸引着数亿用户。而在这些短视频的背后&#xff0c;有一款名为“美摄美颜SDK”的秘密武器&#…

软件项目验收测试报告-软件项目验收流程

对甲方而言&#xff0c;项目验收是正式接受项目成果&#xff0c;将项目从建设转为运营。对于乙方来说&#xff0c;则意味着项目的结束&#xff0c;项目资源的释放。 项目验收是项目收尾的重要环节&#xff0c;依据招投标文件、合同对测评相关要求内容、项目章程和项目过程中的…

国庆出游远程实测:ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性

ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性 【前言】【实测软件】【测试环境】【实操体验】1. 软件安装2. 登录速度3. 文件传输4. 操作延迟5. 画面清晰度6. 安全防护 【本文小结】 【前言】 随着科技的不断发展&#xff0c;远程控制软件已成为我们生活中不可或缺的一部分…

7344-2015 交流伺服电动机通用技术条件

声明 本文是学习GB-T 7344-2015 交流伺服电动机通用技术条件.pdf而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了交流伺服电动机的分类、技术要求和试验方法、检验规则、交付准备。 本标准适用于两相交流伺服电动机(以下简称电机…

7321-2017 定形耐火制品试样制备方法

声明 本文是学习GB-T 7321-2017 定形耐火制品试样制备方法.pdf而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了定形耐火制品制样的定义、制样部位的确定原则和试样的制备。 本标准适用于定形耐火制品试样的制备。 2 规范性引用文…

宝塔反代openai官方API接口详细教程,502 Bad Gateway问题解决

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外VPS服务…

[补题记录] Atcoder Beginner Contest 297(F)

URL&#xff1a;https://atcoder.jp/contests/abc297 目录 F Problem/题意 Thought/思路 Code/代码 F Problem/题意 给一个 H * W 的矩形&#xff0c;在其中任意放置 K 个点&#xff0c;由这 K 个点构成的最小矩形带来的贡献为该矩形的面积&#xff0c;这 K 个点构成一种…