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.js2. 编写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"/> <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> <button onClick={this.forward}>前进</button> <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个属性:
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数
store
将 state 、 action 、reducer联系在一起的对象 如何得到此对象?
- import {createStore} from 'redux'
- import reducer from './reducers'
- const store = createStore(reducer)
此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state
- 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
可以接收到state
和props
。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(开发用的少,封装插件用的多)
相关文章:

React核心原理与实际开发
学习目标 React是啥? 官方定义:将前端请求获取到的数据渲染为HTML视图的JavaScript库。 一、React入门 1、React项目创建 直接创建react,使用初始化会创建package.json npm init -y再安装 2、React基本使用 使用纯JS创建ReactDOM&#…...

Springboot+vue的企业OA管理系统(有报告),Javaee项目,springboot vue前后端分离项目。
演示视频: Springbootvue的企业OA管理系统(有报告),Javaee项目,springboot vue前后端分离项目。 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的企业OA管理系统,采用M(m…...

3、字符设备驱动框架和开发步骤
一、Linux内核对文件的分类 Linux的文件种类 1、-:普通文件2、d:目录文件3、p:管道文件4、s:本地socket文件5、l:链接文件6、c:字符设备7、b:块设备 Linux内核按驱动程序实现模型框架的不同&…...

[MySQL]基础篇
文章目录 1. MySQL基本使用1.1 MySQL的启动和登录1.1.1 MySQL的启动1.1.2 MySQL的客户端连接 1.2 数据模型 2. SQL2.1 SQL类型2.1.1 数值类型2.1.2 字符串类型2.1.3 日期类型 2.2 DDL2.2.1 数据库操作2.2.2 表操作 - 查询2.2.3 表操作 - 创建表2.2.4 表操作 - 修改 2.3 DML2.3.…...
Meta Semantic Template for Evaluation of Large Language Models
本文是LLM系列文章,针对《Meta Semantic Template for Evaluation of Large Language Models》的翻译。 大型语言模型评估的元语义模板 摘要1 引言2 相关工作3 方法4 实验5 结论 摘要 大型语言模型(llm)是否真正理解语言的语义,或者只是记住训练数据?…...

Git相关知识(1)
目录 1.初识Git 1.基础知识 2.centos中下载 2.基本操作 1.创建本地仓库 2.配置本地仓库 3.版本库、工作区、暂存区 4.添加文件 5.add和commit对git文件的作用 6.修改文件 7.版本回退 8.撤销修改 9.删除文件 3.分支操作 1.HEAD与分支 2.创建分支 3.删除分支 …...

pytorch中nn.DataParallel多次使用
pytorch中nn.DataParallel多次使用 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader# 定义模型 class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.fc nn.Linear(10, 1)def forwa…...

制作电商页面(Html)
任务 制作一个电商页面,要求所卖物品清晰,页面色调清晰,要有主页和详情页。 网站所买物品:书籍 色调:#FF2400 橙红色 代码 主页HTML代码: <html><head><meta charset"utf-8"…...
Android Sutdio依赖Snapshot版本,无法同步最新的包
起因 局域网中搭建了Nexus托管本地打包的aar,正常情况下,把修改完成的库推送到仓库后,其他项目引用Snapshot版本的依赖,同步后会马上下载最新的包,但是当第二次推送后,就没有重新下载最新的包,…...
Feign调用异常触发降级捕获异常
通过配置fallbackFactory来捕获异常信息,代码如下 FeignClient(name "user", fallbackFactory UserFallBackFactory.class) public interface UserFeign {PostMapping("/get/list")Map getList();}Component public class UserFallBackFacto…...

Springboot 音乐网站管理系统idea开发mysql数据库web结构java编程计算机网页源码maven项目
一、源码特点 springboot 音乐网站管理系统是一套完善的信息系统,结合springboot框架和bootstrap完成本系统,对理解JSP java编程开发语言有帮助系统采用springboot框架(MVC模式开发),系统 具有完整的源代码和数据库&…...

微信支付v2-02
...

企业的销售活动是什么?CRM销售管理系统给你答案
在企业业务中,销售活动是实现企业业绩目标的基本单元,起着奠基石的作用。CRM销售管理系统是销售活动管理的必备工具,帮助企业更好地开展销售活动。下面来说说企业的销售活动是什么? 什么是销售活动 简单来说,销售人员…...
【PG】PostgreSQL参数格式 配置文件格式
目录 一 PG参数格式 二 通过配置文件修改参数 postgresql.auto.conf文件 三 通过SQL修改参数 四 通过shell修改参数 五 管理配置文件内容 一 PG参数格式 所有参数名都是大小写不敏感的。每个参数都可以接受五种类型之一的值: 布尔、字符串、整数、 浮点数或枚…...

应用层协议 HTTP
一、应用层协议 我们已经学过 TCP/IP , 已然知道数据能从客户端进程经过路径选择跨网络传送到服务器端进程。 我们还需要知道的是,我们把数据从 A 端传送到 B 端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用…...

Springboot+vue的应急救援物资管理系统,Javaee项目,springboot vue前后端分离项目。
演示视频: Springbootvue的应急救援物资管理系统,Javaee项目,springboot vue前后端分离项目。 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的应急救援物资管理系统,采用M(model)V&…...
创建properties资源文件,并由spring组件类获取资源文件
1.1 创建资源文件file-upload-dev.properties #文件上传地址 file.imageUserFaceLocation=/workspaces/images/foodie/faces #图片访问地址 file.imageServerUrl=http://localhost:8088/foodie/faces1.2 创建spring组件获取资源文件类FileUpload import org.springframework.…...
你知道npm、yarn、pnpm的区别吗?
npm 嵌套的 node_modules 结构 npm 在早期采用的是嵌套的 node_modules 结构,“node_modules” 文件夹通常包含项目依赖的模块。在项目中使用多个依赖并且这些依赖本身也有它们自己的依赖时,就会出现嵌套的 “node_modules” 结构。 嵌套的 “node_mo…...

利用excel表格进行分包和组包
实际使用中,我们可能希望修改某几个数据之后,最终的数据包能够自动发动数据,类似于在给结构体变量修改数据,自动生成完整的结构体; excel语法 1:拆分数据 LEFT(A4,2) – 取A4单元格左边的两个数据 RIGHT(A4…...

Go 语言切片扩容规则是扩容2倍?1.25倍?到底几倍
本次主要来聊聊关于切片的扩容是如何扩的,还请大佬们不吝赐教 切片,相信大家用了 Go 语言那么久这这种数据类型并不陌生,但是平日里聊到关于切片是如何扩容的,很多人可能会张口就来,切片扩容的时候,如果老…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...