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.rhkb.cn/news/151065.html

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

相关文章

项目_数据可视化| 折线图.散点图.随机漫步

安装matplotlib 在正式开始编写程序之前&#xff0c;需要先安装pip、matplotlib模块&#xff0c;苹果系统的安装问题在之前的文章中有相关介绍内容&#xff0c;如果pycharm运行模块报错&#xff0c;可以再次检查是否版本兼容问题。 绘制折线图 调用subplot&#xff08;&#x…

C++设计模式-单件(Singleton)

目录 C设计模式-单件&#xff08;Singleton&#xff09; 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-单件&#xff08;Singleton&#xff09; 一、意图 保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 二、适用性 当类只能有一…

机器学习 不均衡数据采样方法:imblearn 库的使用

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

BigDecimal使用方法

文章目录 引入BigDecimaBigDecima的使用舍入模式updownCEILINGFLOORhalf_UPhalf_UP BigDecimal存储原理总结 引入 知识引入:如下图 0.266小数部分二进制需要55位存储,如果我们double接收那么将丢掉最后三位,所以我们在代码中进行小鼠的加减运算结果有时候并不是我们想要的 Bi…

如何实现chatGPT批量问答,不用token

3分钟&#xff0c;教你做个GPT批量问答还不用token | 有源码 解压压缩包&#xff1b;在Pycharm打开这个文件夹 执行 pip install undetected_chromedriver 和 pip install selenium 执行第1到63行代码&#xff0c;后台会自动打开浏览器&#xff0c;需要手动登录账号和点掉系…

Visual Studio自定义模板参数、备注

模板路径&#xff1a; VS2022 x64&#xff1a;C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\ItemTemplatesVS2022 x86&#xff1a;C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\ItemTemplates 一、声明和启用模板…

想升级macOS Big Sur,但是MacBook内存空间不够该怎么办?

随着使用时间的增长&#xff0c;我们会发现Mac电脑的存储空间越来越少&#xff0c;这时候我们就需要对Mac电脑进行清理&#xff0c;以释放更多的存储空间。那么&#xff0c;Mac空间不足怎么解决呢&#xff1f; 1.清理垃圾文件 Mac空间不足怎么解决&#xff1f;首先要做的就是清…

Tomcat服务器下载、安装、配置环境变量教程(超详细)

请先配置安装好Java的环境&#xff0c;若没有安装&#xff0c;请参照如下博客上的步骤进行安装&#xff01; 安装Java环境教程Windows配置Java环境变量(下载、安装、配置环境)_第三女神程忆难的博客-CSDN博客 Tomcat部署Web项目&#xff08;一&#xff09;内嵌 Tomcat部署网站…

力扣 -- 516. 最长回文子序列

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int longestPalindromeSubseq(string s) {int ns.size();vector<vector<int>> dp(n,vector<int>(n));//记得从下往上填表for(int in-1;i>0;i--){//记得i是小于等于j的for(int ji;j&l…

vscode 乱码解决

windows 10 系统 vs code 编译运行和调试 C/C_vscode windows编译_雪的期许的博客-CSDN博客 VS Code默认文件编码时UTF-8&#xff0c;这对大多数情况是没有问题的&#xff0c;却偏偏对C/C有问题。如果以UTF-8编码保存C/C代码&#xff0c;那么只能输出英文&#xff0c;另外使用…

uniapp 实现地图头像上的水波纹效果

最近实现了uniapp 地图头像水波纹的效果&#xff0c;话不多说&#xff0c;先来看看视频效果吧&#xff1a;链接 在这里具体的代码就不放出来了&#xff0c;还是利用了uniapp的 uni.createAnimation 方法&#xff0c;因为cover-view 不支持一些css 的动画效果&#xff0c;所以这…

SQL进阶 - SQL的编程规范

性能优化是一个很有趣的探索方向&#xff0c;将耗时耗资源的查询优化下来也是一件很有成就感的事情&#xff0c;但既然编程是一种沟通手段&#xff0c;那每一个数据开发者就都有义务保证写出的代码逻辑清晰&#xff0c;具有很好的可读性。 目录 引子 小试牛刀 答案 引言 …

APP 备案公钥、签名 MD5获取方法。

一、IOS 系统获取 Bundle ID、平台公钥、签名 MD5 值的指引 获取 Bundle ID&#xff1a;使用 APP 对应的 IOS 开发者账号登录 Developer 控制台&#xff0c;找到下图标识符&#xff08;英文&#xff09;&#xff0c;单击进入 Certificates,Identifiers&Profiles 页面。 在…

如何轻松使用 ChatGPT 进行论文大纲和创作

ChatGPT能够编写复杂的代码、博客文章等&#xff0c;它可以帮助我们做很多事情。今天本篇文章分享的主要内容如何利用 ChatGPT 来撰写论文文章。下面会介绍如何轻松使用 ChatGPT 进行论文大纲和创作&#xff01; 1、使用 ChatGPT 确定主题 文章非常重要的一个部分就是主题。如…

【AI工程化】 如何让AI在企业多快好省的落地,提高生产效率?

文章目录 &#x1f33a;前言&#x1f33a;内容简介&#x1f33a;读者对象&#x1f33a;专家推荐&#x1f386;彩蛋 &#x1f33a;前言 作为计算机科学的一个重要领域&#xff0c;机器学习也是目前人工智能领域非常活跃的分支之一。机器学习通过分析海量数据、总结规律&#xf…

IDEA的使用(一) (IntelliJ IDEA 2022.1.3版本)

目录 1. IDEA项目结构 2. 模块的导入操作 2.1 正规操作 2.2 取巧操作 2.3 出现乱码 2.4 模块改名 3. 代码模板的使用 后缀补全&#xff08;Postfix Completion&#xff09;、实时模板&#xff08;Live Templates&#xff09;菜单里面什么介绍都有&#xff0c;可以自学&a…

vscode更改为中文版本

方式一 在扩展里安装chinese插件 方式二 1.Ctrl&#xff0b; Shift &#xff0b;P&#xff08;commandshiftP&#xff09; 2.输入Configure display Language 3.选择zh-cn 这时候vscode会提示需要重启&#xff0c;点击restart重启vscode&#xff0c;重启后vscode就会显示中…

MacBook 录制电脑内部声音

MacBook 录制电脑内部声音 老妈喜欢跳广场舞&#xff0c;现在广场舞音频下载都收费了&#xff01;没办法&#xff0c;只能自己录歌了&#xff0c;外录有杂音大家也都知道&#xff0c;所以就只能采用内录的方式然后再用 Audition 调整一下音量大小。 一、&#xff08;前置条件&a…

git之分支代码合并

master合并到分支 1. 切换到master主分支上 git checkout master 2. 将master更新的代码拉取到本地 git pull 3. 再切换到自己的分支dev上 git checkout dev 4. 合并master到自己的分支dev上 git merge master此处如果有冲突会给出提示哪个文件有冲突&#xff0c;修改冲…

【广州华锐互动】VR线上播控管理系统让虚拟现实教学效果更加显著

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐走进我们的生活&#xff0c;尤其在教育领域&#xff0c;VR技术的应用为学生提供了全新的学习体验。 广州华锐互动作为一家成立16年的老牌VR公司&#xff0c;开发了不少VR教学课件&#xff0c;包括物理、化学、农林、土木…