React教程(由浅到深)

news2024/11/16 13:23:08

文章目录

  • 1. 基本语法
    • 1.1 初体验Hello React
    • 1.2 JSX语法的基本使用
      • 1.2.1 语句与表达式说明
    • 1.3. React面向组件编程
      • 1.3.1 函数组件与类组件
    • 1.4 组件实例的三大特性
      • 1.4.1 state数据存储状态
      • 1.4.2 props的使用
        • 1.4.2.1基本使用
        • 1.4.2.2 做限制类型,默认值使用
        • 1.4.2.3 简写方式
        • 1.4.2.4 函数组件中使用props
      • 1.4.3 refs的使用
        • 1.4.3.1 直接使用ref绑定字符串
        • 1.4.3.2 回调函数的形式
        • 1.4.3.3 createRef的方式
    • 1.5. React事件处理与委托
    • 1.6.受控组件与非受控组件
      • 1.6.1 非受控组件
      • 1.6.2受控组件
    • 1.7. 高阶函数与函数柯里化
      • 1.7.1 柯里化回调
      • 1.7.2 高阶回调
    • 1.8. 组件的生命周期
      • 1.8.1 旧生命周期
      • 1.8.2 新生命周期
      • 1.8.3 _getSnapshotBeforeUpdate的使用场景
  • 2. react脚手架基本配置
    • 2.1 创建项目与项目基本结构
    • 2.2 全局样式与模块样式
    • 2.3 配置代理,fetch与axios发送请求
      • 2.3.1 代理配置
      • 2.3.2 fetch发送网络请求
  • 3 组件间的通信
    • 3.1 父子通信,props ,事件
    • 3.2 refs 与 事件冒泡
    • 3.3 详解通信与类型限制
    • 3.4 消息订阅-发布机制
      • 3.4.1 React18 eventBus使用
  • 4. 路由
    • 4.1 路由的基本使用
    • 4.2 Navlink 的选中样式
      • 4.2.1标签体的内容children
    • 4.3 Swith 单一匹配
    • 4.4 模糊路由与精准匹配
    • 4.5 路由重定向`Redirect`
    • 4.6 嵌套路由
    • 4.7 路由组件中的传参
      • 4.7.1 params传参
      • 4.7.2 Search传参
      • 4.7.3 state传参
      • 4.7.4 总结
    • 4.8 编程式导航
    • 4.9 BrowserRouter与HashRouter的区别
  • 4.react-router6+版本
    • 4.1 .Component
      • 4.1.1 ` 与 `
      • 4.1.2 `<Link> 与<NavLink>`
      • 4.1.3. `<Navigate>`
      • 4.1.4 `<Outlet>`
    • 4.2 路由Hooks
      • 4.2.1 useRoutes()
      • 4.2.2 useNavigate()编程式导航
      • 4.2.3 useParams()
      • 4.2.4 ueSearchParams()
      • 4.2.5 useLocation() state传参
      • 4.2.6. useMatch()
      • 4.2.7 useInRouterContext()
      • 4.2.8 useNavigationType()
    • 4.3 补充 类组件增强 withRouter
  • 5. redux
  • 6. React Hooks 及其扩展
    • 6.1 setState的两种用法
      • 6.1.1 对象更新
      • 6.1.2 函数回调式更新
    • 6.2 Hooks 之 useState
    • 6.3 Hooks之useEffect
    • 6.4 Hooks之useRef
    • 6.5 Fragment代替根标签
    • 6.6 Context的使用
      • 6.6.1 提供的API
      • 6.6.2 注意点
      • 6.3 useContext
    • 6.7 PureComponent 拒接子组件render重新渲染
    • 6.8 render props 插槽
      • 6.8.1 `this.props.children `渲染
      • 6.8.2 `this.props` 渲染
      • 6.8.3 `render props` 渲染
    • 6.9 错误异常边界

1. 基本语法

1.1 初体验Hello React

   <div id="app"></div>
	<!-- 引入react核心库 -->
	<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>
	<script type="text/babel">
		const demo = <span>Hello Word</span>
		ReactDOM.render(demo, document.querySelector('#app'))
	</script>

1.2 JSX语法的基本使用

  • jsx语法规则:
  1. 渲染定义的元素,表达式需要{ }
const name = 'Hello Word'
<span style={{color:'red',fontSize:'55px'}}>{name}</span>
  1. 样式的类名,要用className
<div className="className" id={ID}>
	<span style={{color:'red',fontSize:'55px'}}>{name}</span>
</div>
  1. 内联样式,要用style={{key:value}}的形式去写。
	<span style={{color:'red',fontSize:'55px'}}>{name}</span>
  1. 只有一个根标签
  2. 标签必须闭合
  3. undefined/null/Boolean 类型
 <h2>{String(aaa)}</h2>
 <h2>{bbb + ""}</h2>
 <h2>{ccc.toString()}</h2>

1.2.1 语句与表达式说明

  • 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
    下面这些都是表达式:
    (1). a
    (2). a+b
    (3). demo(1)
    (4). arr.map()
    (5). function test () {}

  • 语句(代码):
    (1).if(){}
    (2).for(){}
    (3).switch(){case:xxxx}

  • 混入map 表达式

const data = ['Vue', 'React', 'Animation']
		// 只能渲染数组
		const VDOM = (
			<div>
				<h1>HEllo REACT</h1>
				<h2>React遍历对象与数组</h2>
				<ul>
					{
						data.map((v, index) => {
							return <li key={index}>{v}</li>
						})
					}
				</ul>
			</div>
		)
ReactDOM.render(VDOM, document.querySelector('#test'))

1.3. React面向组件编程

1.3.1 函数组件与类组件

  • 函数组件
  • 注意点
    • 函数式组件定义时首字母必须大写
    • render渲染时必须使用标签
function MyComponent(){
   console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
 return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
 }
		
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  • 类组件
  • 注意点
    • 类组件必须继承React.Component
    • 必须写render函数
    • 必须有返回值
class MyComponent extends React.Component {
	render(){
				//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
				//this指向?—— MyComponent的实例对象 <=>MyComponent组件实例对象。
				console.log('render中的this:',this);
				return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
			}
		}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))

1.4 组件实例的三大特性

1.4.1 state数据存储状态

注意点 React默认开启严格模式

  • 普通函数的形式直接在事件中调用 this的指向undefined 可以在构造函数中利用bind,applycall 改变this的指向
  • setState 用于更新state中的数据,里面包含一个对象要改变的值 (注意点,setState是异步的,可以传递对象或者函数,且每次调用 render函数都会重新渲染)
// state使用 
		class Wether extends React.Component {
			// 1. 继承React组件实例上添加的属性
			//  2. 构造器的this指向构造函数的实例对象
			//  3. render() 函数中的this也指向构造函数的实例对象
			constructor(props) {
				// super继承父组件属性
				super(props)
				this.state = { isHost: false, wind: '炎热' }
				// 改变this的指向
				this.demo = this.demo.bind(this)
			}
			render() {
				const { isHost } = this.state
				// this.function 是直接调用this指向window
				return (
					<div onClick={this.demo} >{isHost ? '雨天' : '晴天'}</div>
				)
			}
			demo() {
				// this.state.isHost = !this.state.isHost   // 取反 状态不能直接更改(React响应捕捉不到)
				let isHost = this.state.isHost
				// 修改状态需要用setState
				this.setState({ isHost: !isHost })
			}

		}
		ReactDOM.render(<Wether />, document.querySelector('#test'))
  • 简写的方式
class Wether extends React.Component {
			// constructor(props) {
			// 	super(props)

			// 	// 改变this指向
			// 	this.speck = this.speck.bind(this)
			// }
			// 简写: 直接原型上追加属性
			state = {
				name: '张三',
				isName: true
			}
			render() {
				let { isName, name } = this.state
				return (
					<div onClick={this.speck} style={{ fontSize: '24px', color: 'red' }}> {isName ? '显示名字' : `不显示${name}`}</div>
				)
			}
			// 直接追加到原型上这里的this就是指原型
			speck = () => {
				let isName = this.state.isName
				// setState里面必须包含一个对象
				this.setState({ isName: !isName })
			}
		}
		ReactDOM.render(<Wether />, document.querySelector('#test'))

1.4.2 props的使用

1.4.2.1基本使用

  • 基本使用 props直接在实例上的 key=value 会追加到React实例props上
  • 对象解构的方式使用

class Person extends React.Component{
			render(){
				// console.log(this);
				const {name,age,sex} = this.props
				return (
					<ul>
						<li>姓名:{name}</li>
						<li>性别:{sex}</li>
						<li>年龄:{age+1}</li>
					</ul>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))
		ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
		const p = {name:'老刘',age:18,sex:'女'}
		// 对象解构的方式使用 
		ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))

1.4.2.2 做限制类型,默认值使用

  • 实例.propTypes={ } 对象里面包含要限制的数据类型
  • 实例.defaultProps={ } 对象里面包含的是默认的属性值
class DataLimit extends React.Component {
			speck=()=>{
				console.log(this.props)
			}
			render() {
				const { name, age, sex } = this.props
				// 注意点为props为只读属性不能修改
				return (
					<div>
						<h2>{name}</h2>
						<h2>{age+1}</h2>
						<h2>{sex}</h2>
						<h2 onClick={this.speck}> 点击事件</h2>
					</div>
				)
			}
		}
		// propType 限制类型 (是否必传等)
		//  1.PropTypes.string 限制为字符串
		//  2.PropTypes.string.isRequired 限制为必传
		//  3. 限制方法为func
		DataLimit.propTypes = {
			name: PropTypes.string.isRequired,
			sex: PropTypes.string,
			speak: PropTypes.func
		}
		// prop传值 默认值
		DataLimit.defaultProps = {
			sex: "女"
		}
		const data = { name: '张珊珊', age: 18, sex: "男" }
		ReactDOM.render(<DataLimit {...data} />, document.querySelector('#test1'))

1.4.2.3 简写方式

注意点

  • static 关键字给类添加属性
	//类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1
      class Car {
			constructor(name,price){
				this.name = name
				this.price = price
				// this.wheel = 4
			}
			a = 1
			wheel = 4
			static demo = 100
		}
		const c1 = new Car('奔驰c63',199)
		console.log(c1);
		console.log(Car.demo);  // 100
  • . 给定类 DataLimit 添加 propTypesdefaultProps 两个属性做限制
// static 给类添加属性
		class DataLimit extends React.Component {
			speck = () => {
				console.log(this)
			}
			render() {
				const { name, age, sex } = this.props
				// 注意点为props为只读属性不能修改
				return (
					<div>
						<h2>{name}</h2>
						<h2>{age + 1}</h2>
						<h2>{sex}</h2>
						<h2 onClick={this.speck}> 点击事件</h2>
					</div>
				)
			}
			static propTypes = {
				name: PropTypes.string.isRequired,
				sex: PropTypes.string,
				speak: PropTypes.func
			}
			// prop传值 默认值
			static defaultProps = {
				sex: "女"
			}
		}
		// (DataLimit.对象) 与直接在类里面用(static 对象) 一样
		console.log(DataLimit.propTypes);
		const data = { name: '张珊珊', age: 18, sex: "男" }
		ReactDOM.render(<DataLimit {...data} />,   
	    document.querySelector('#test1'))

1.4.2.4 函数组件中使用props

注意点

  • 默认值传参 以及函数接受的值
function Person(prop) {
			const { name, age, sex } = prop
			return (
				<div>
					<li><a href="">{name}</a></li>
					<li><a href="">{age}</a></li>
					<li><a href="">{sex}</a></li>
				</div>
			)
		}
		// 函数式组件限制
		 Person.propTypes = {
				name: PropTypes.string.isRequired,
				sex: PropTypes.string,
				speak: PropTypes.func
			}
			// prop传值 默认值
		Person.defaultProps = {
				sex: "女"
			}
		const data = { name: '张珊珊', age: 18, sex: "男" }
		ReactDOM.render(<Person {...data} />, document.querySelector('#test1'))

1.4.3 refs的使用

refs 操作dom

1.4.3.1 直接使用ref绑定字符串

  • 用ref绑定的dom会被收集到 refs这个对象中
	class PersonRefs extends React.Component {
			clickRef = () => {
				console.log(this);   // {Input:dom节点 }
				console.log(this.refs.Input);
			}
			render() {
				// 字符串形式的ref
				return (
					<div>
						<input type="text" ref="Input"/>
						<button ref="button" onClick={this.clickRef}>点击Refs </button>
						<input ref="input02" type="text" />
					</div>
				)
			}
		}
		ReactDOM.render(<PersonRefs />, document.querySelector('#test'))

在这里插入图片描述

1.4.3.2 回调函数的形式

class RefsFunc extends React.Component {

			addInput = () => {

				alert(this.input.value)
				// const { input1 } = this
				// alert(input1.value)
			}
			state = {
				isShow: true
			}
			isShowEvent = () => {
				const { isShow } = this.state
				console.log(isShow);
				this.setState({ isShow: !isShow })
			}
			// ref 中写成这个只会回调一次
			CurrentEvent = (vnode) => {
				this.input02 = vnode
				console.log('xxxxxx');
			}
			render() {
				//🌎 默认回调一次
				//🌎更新时,调用两次
				// Vnode => this.input1 = Vnode  回调函数 ref 回调形式
				return (
					<div>
						<input type="text" ref={CurrentNode => { this.input = CurrentNode; console.log('更新调用两次'); }} defaultValue="默认值" />
						<input type="text" ref={this.CurrentEvent} />
						<input type="text" ref={Vnode => this.input = Vnode} defaultValue="默认值" />
						<button onClick={this.addInput}> 函数形式的Input使用 </button>
						<p>{this.state.isShow ? "更新false" : "更新true"}</p>
						<button onClick={this.isShowEvent}>切换内联函数调用</button>
					</div>
				)
			}
		}
		ReactDOM.render(< RefsFunc />, document.querySelector('#test'))

1.4.3.3 createRef的方式

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,返回一个要ref绑定的dom节点, 且key唯一

	class RefsFunc extends React.Component {
			// 实例上添加一个myInput
			myInput = React.createRef()
			componentDidMount = () => {
				console.log(this);
				console.log(this.myInput.current.value);
				// this.currentRefs.current.focusTextInput();
			}
			render() {
				return (
					<div>
						<input type="text" ref={this.myInput} />
						<button onClick={this.componentDidMount}>createRef生成容器标识refDOM节点</button>
					</div>
				)
			}
		}
		ReactDOM.render(< RefsFunc />, document.querySelector('#test'))

参看官网:createRef官网链接

1.5. React事件处理与委托

  • 操作的事件与要操作的组件数据在同一个dom节点时,利用事件委托的方式
class Demo extends React.Component{
			//展示左侧输入框的数据 refs
			showData = ()=>{
				alert(this.refs.myRefs.current.value);
			}
	    //  操作的事件与要操作的组件数据在同一个dom节点时,利用事件委托的方式
			//展示右侧输入框的数据(target 处理数据)
			showData2 = (event)=>{
				alert(event.target.value);
			}
			render(){
				return(
					<div>
						<input ref={e=>this.myrefs=e} type="text" placeholder="点击按钮提示数据"/>&nbsp;
						<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
						<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
					</div>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

1.6.受控组件与非受控组件

1.6.1 非受控组件

  • 获取要提交的值为现用现取
class Login extends React.Component{
			handleSubmit = (event)=>{
				event.preventDefault() //阻止表单提交
				const {username,password} = this
				alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
			}
			render(){
				return(
					<form onSubmit={this.handleSubmit}>
						用户名:<input ref={c => this.username = c} type="text" name="username"/>
						密码:<input ref={c => this.password = c} type="password" name="password"/>
						<button>登录</button>
					</form>
				)
			}
		}
		//渲染组件
		ReactDOM.render(<Login/>,document.getElementById('test'))

1.6.2受控组件

//受控组件 , 事件触发Input中 传在数据的值
		class Login extends React.Component{
			//初始化状态
			state = {
				username:'', //用户名
				password:'' //密码
			}
			//保存用户名到状态中
			saveUsername = (event)=>{
				this.setState({username:event.target.value})
			}

			//保存密码到状态中
			savePassword = (event)=>{
				this.setState({password:event.target.value})
			}
			//表单提交的回调
			handleSubmit = (event)=>{
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
			}

			render(){
				return(
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={this.saveUsername} type="text" name="username"/>
						密码:<input onChange={this.savePassword} type="password" name="password"/>
						<button>登录</button>
					</form>
				)
			}
		}
		//渲染组件
		ReactDOM.render(<Login/>,document.getElementById('test'))

1.7. 高阶函数与函数柯里化

  1. 高阶函数
  • 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
    • 若A函数,接收的参数还是一个函数,那么A就可以称之为高阶函数。
    • 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
    • 常见的高阶函数有:Promise、setTimeout、arr.map()等等
  1. 函数柯里化 参考链描] 让函数的职责不再单一

1.7.1 柯里化回调

注意点

onChange={this.InputName('username')('xxxx')} 共调用三次,react调用,默认返回的值为target

/创建组件
		class Login extends React.Component {
			// 初始化状态
			state = {
				username: "默认值",
				password: ""
			}
			// 实时更新状态, 数据维护在state中为受控组件(相当于vue里面的v-model)
			InputName = (dataType) => {
				// onChange默认的返回一个函数
				//回调的是一个函数  (既函数的柯里化)
				return (Type) => {
					console.log([Type]);
					return (e) => {
						this.setState({ [dataType]: e.target.value })

					}
				}
			}
			InputPassWord = (e) => {
				this.setState({ password: e.target.value })
			}
			handlySubmit = (e) => {
				e.preventDefault();
				alert(`userName: ${this.state.username} passwrod : ${this.state.password}`,)
			}
			render() {
				return (
					<div>
						<form onSubmit={this.handlySubmit} >
							{ /* <div>value 绑定默认值</div>*/}
							<input type="text" value={this.state.username} onChange={this.InputName('username')('xxxx')} name="username" />
							<input type="text" onChange={this.InputPassWord} name="password" />
							<button>提交</button>
						</form>

					</div>
				)
			}
		}
		ReactDOM.render(<Login />, document.querySelector('#test'))

1.7.2 高阶回调

	class Login extends React.Component {
			// 初始化状态
			state = {
				username: "默认值",
				password: ""
			}
			// 实时更新状态, 数据维护在state中为受控组件(相当于vue里面的v-model)
			InputName = (dataType, event) => {
				// onChange默认的返回一个函数
					// [datatype]使用变量作为属性名
				this.setState({ [dataType]: event.target.value })

			}
			InputPassWord = (e) => {
				this.setState({ password: e.target.value })
			}
			handlySubmit = (e) => {
				e.preventDefault();
				alert(`userName: ${this.state.username} passwrod : ${this.state.password}`,)
			}
			render() {
				return (
					<div>
						<form onSubmit={this.handlySubmit} >
							{ /* <div>不用可里化的方式实现
							   1. onChange 先调用一个event函数在event 函数中又调用了this.InputName这个函数 
							</div>*/}
							<input type="text" value={this.state.username} onChange={(event) => {
								this.InputName('username', event)
							}
							} name="username" />
							<input type="text" onChange={this.InputPassWord} name="password" />
							<button>提交</button>
						</form>

					</div>
				)
			}
		}
		ReactDOM.render(<Login />, document.querySelector('#test'))

1.8. 组件的生命周期

1.8.1 旧生命周期

  1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
1.	constructor()
2.	componentWillMount()
3.	render()
4.	componentDidMount() =====> 常用一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  1. 更新阶段: 由组件内部this.setSate()或父组件render触发
	1.	shouldComponentUpdate()
	2.	componentWillUpdate()
	3.	render() =====> 必须使用的一个
	4.	componentDidUpdate()
  1. 卸载组件:
1. 由ReactDOM.unmountComponentAtNode()触发
2. componentWillUnmount()  =====> 常用一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

4.生命周期执行顺序代码参考

	class Count extends React.Component {
			constructor(props) {
				super(props)
				console.log('构造器,constructor');
			}
			state = {
				count: 1
			}
			handlyAdd = () => {
				let { count } = this.state
				count++;
				this.setState({ count })
			}
			// 钩子
			// 1.组件将要挂载的钩子
			componentWillMount() {
				console.log('组件将要挂载的钩子 	componentWillMount');
			}
			// 2. 挂载中 
			render() {
				console.log('挂载中  render');
				const { count } = this.state
				return (
					<div>
						<p>当前的数字 {count}</p>
						<button onClick={this.handlyAdd}>点我加一</button>
						<button onClick={this.UnMountEvent}> 卸载组件</button>
						<button onClick={this.mandatoryUpdate}> 强制更新,不改状态</button>
					</div>
				)
			}
			//3 挂载完毕
			componentDidMount() {
				console.log('挂载完毕 componentDidMount');
			}
			// 更新的组件
			//  1. 组件是否可以更新 返回值ture 或false
			shouldComponentUpdate() {
				console.log('组件是否可以更新 shouldComponentUpdate');
				// 🚗 注意点1. 这个方法不写默认可以更新  为true 
				// 2.  方法写入了 ,没有return 默认为false
				return true
			}
			// 2. 组件将要更新
			//  3. render(){}
			componentWillUpdate() {
				console.log('组件将要更新 componentWillUpdate');
			}
			// 4. 组件更新
			componentDidUpdate() {
				console.log('组件更新完毕 componentWillUpdate');
			}

			//999 卸载组件
			UnMountEvent = () => {
				console.log('卸载DOM节点  unmountComponentAtNode');
				ReactDOM.unmountComponentAtNode(document.querySelector('#test'))
			}
			// 强制更新 不走shouldComponentUpdate()函数
			mandatoryUpdate = () => {
				this.forceUpdate()
			}
		}


		// 父子组件生命周期
		class Myfalter extends React.Component {
			state = {
				name: "父组件信息"
			}
			fatherEmitSon = () => {
				const { name } = this.state
				this.setState({ name: '修改父组件的信息' })
			}
			render() {
				const { name } = this.state
				return (
					<div>
						<h2>我是父组件</h2>
						<h3>-----------------------------------</h3>
						<button onClick={this.fatherEmitSon}>修改父组件值传递给子组件 </button>
						< Myson name={name} />
					</div>
				)
			}
		}
		class Myson extends React.Component {
			render() {
				return (
					<div>
						<h2>我是子组件</h2>
						<p>我将要展示父组件的内容: <span style={{ color: 'red' }}>{this.props.name}</span></p>
					</div>
				)
			}
				componentDidMount() {
					console.log('子组件挂载时调用   componentDidMount');
				}
			componentWillReceiveProps(props) {
				//  1. (第一次接受值默认没有调用)子组件更新触发的生命周期 可以传递值
				console.log('xxxxxxxxx', props);
			}
			shouldComponentUpdate() {
				console.log('组件是否可以更新 shouldComponentUpdate');
				// 🚗 注意点1. 这个方法不写默认可以更新  为true 
				// 2.  方法写入了 ,没有return 默认为false
				return true
			}
			// 2. 组件将要更新
			//  3. render(){}
			componentWillUpdate() {
				console.log('组件将要更新 componentWillUpdate');
			}
			// 4. 组件更新
			componentDidUpdate() {
				console.log('组件更新完毕 componentWillUpdate');
			}
		}
		//  ReactDOM.render(<Count />, document.querySelector('#test'))
		ReactDOM.render(<Myfalter />, document.querySelector('#test'))

在这里插入图片描述

1.8.2 新生命周期

  1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
1.	constructor()
2.	getDerivedStateFromProps() 
3.	render()
4.	componentDidMount() =====> 常用一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  1. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.	getDerivedStateFromProps()
2.	shouldComponentUpdate()
3.	render()
4.	getSnapshotBeforeUpdate()
5.	componentDidUpdate()
  1. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.	componentWillUnmount()  =====> 常用一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
  1. 注意点 UNSAFE_componentWillMount() , UNSAFE_componentWillUpdate() 等生命周期废弃 具体参考官网

  2. 生命周期代码参考

class Count extends React.Component {
			constructor(props) {
				super(props)
				console.log('构造器,constructor');
			}
			state = {
				count: 1
			}
			handlyAdd = () => {
				let { count } = this.state
				count++;
				this.setState({ count })
			}
			// 2. 挂载中 
			render() {
				console.log('挂载中  render');
				const { count } = this.state
				return (
					<div>
						<p>当前的数字 {count}</p>
						<button onClick={this.handlyAdd}>点我加一</button>
						<button onClick={this.UnMountEvent}> 卸载组件</button>
						<button onClick={this.mandatoryUpdate}> 强制更新,不改状态</button>
					</div>
				)
			}
			// 新增加的钩子(相当于将要挂载 或将要更新的钩子)
			// 用处: state值完全取决于props
			// 注意点 写入必须返回值
			static getDerivedStateFromProps(props, state) {
				console.log("新增加的钩子  getDerivedStateFormProps");
				console.log('state', state);
				// 返回一个对象
				return props
			}
			//  新增加的钩子 (在更新之前获取快照)
			// 注意点  必须返回一个快照 或null
			getSnapshotBeforeUpdate() {
				return '更新之前的值'
			}
			//3 挂载完毕
			componentDidMount() {
				console.log('挂载完毕 componentDidMount');
			}
			// 更新的组件
			//  1. 组件是否可以更新 返回值ture 或false
			shouldComponentUpdate() {
				console.log('组件是否可以更新 shouldComponentUpdate');
				// 🚗 注意点1. 这个方法不写默认可以更新  为true 
				// 2.  方法写入了 ,没有return 默认为false
				return true
			}
			// 4. 组件更新完成(拿到之前的值,可以获取getSnapshotBeforeUpdate这个钩子return的值)
			componentDidUpdate(preProps, preState, preValue) {
				console.log('组件更新完毕 componentWillUpdate');
				console.log('组件更新完成', preProps, preState, preValue);
			}
			//999 卸载组件
			UnMountEvent = () => {
				console.log('卸载DOM节点  unmountComponentAtNode');
				ReactDOM.unmountComponentAtNode(document.querySelector('#test'))
			}
			// 强制更新 不走shouldComponentUpdate()函数
			mandatoryUpdate = () => {
				this.forceUpdate()
			}
		}


		// 父子组件生命周期
		class Myfalter extends React.Component {
			state = {
				name: "父组件信息"
			}
			fatherEmitSon = () => {
				const { name } = this.state
				this.setState({ name: '修改父组件的信息' })
			}
			render() {
				const { name } = this.state
				return (
					<div>
						<h2>我是父组件</h2>
						<h3>-----------------------------------</h3>
						<button onClick={this.fatherEmitSon}>修改父组件值传递给子组件 </button>
						< Myson name={name} />
					</div>
				)
			}
		}
		class Myson extends React.Component {
			render() {
				return (
					<div>
						<h2>我是子组件</h2>
						<p>我将要展示父组件的内容: <span style={{ color: 'red' }}>{this.props.name}</span></p>
					</div>
				)
			}
			componentDidMount() {
				console.log('子组件挂载时调用   componentDidMount');
			}
			componentWillReceiveProps(props) {
				//  1. (第一次接受值默认没有调用)子组件更新触发的生命周期 可以传递值
				console.log('xxxxxxxxx', props);
			}
			shouldComponentUpdate() {
				console.log('组件是否可以更新 shouldComponentUpdate');
				// 🚗 注意点1. 这个方法不写默认可以更新  为true 
				// 2.  方法写入了 ,没有return 默认为false
				return true
			}
			// 2. 组件将要更新
			//  3. render(){}
			componentWillUpdate() {
				console.log('组件将要更新 componentWillUpdate');
			}
			// 4. 组件更新
			componentDidUpdate() {
				console.log('组件更新完毕 componentWillUpdate');
			}
		}
		ReactDOM.render(<Count />, document.querySelector('#test'))
		// ReactDOM.render(<Myfalter />, document.querySelector('#test'))
  1. 生命周期参考图 生命周期图解官网参考

在这里插入图片描述

1.8.3 _getSnapshotBeforeUpdate的使用场景

  1. 利用上次传递的高度实现滚动官网参考链接
// scrollTop     滚动的高度
		// scrollHeight  内容区域的高度
		class NewsList extends React.Component {
			state = {
				newList: []
			}
			componentDidMount() {
				setInterval(() => {
					const { newList } = this.state
					let list = `新闻${newList.length + 1}`
					this.setState({ newList: [list, ...newList] })
				}, 1000)
			}

			getSnapshotBeforeUpdate() { 
				return this.refs.list.scrollHeight
			}
			componentDidUpdate(preProps, preState, height) {
        //  console.log(preProps, preState, height);
				// height 是 getSnapshotBeforeUpdate()传递的值
				this.refs
			}
			render() {
				return (
					<div className="list" ref="list">
						{
							this.state.newList.map((n, index) => {
								return <div key={index} className='news'> {n}</div>
							})
						}
					</div>
				)
			}
		}
		ReactDOM.render(<NewsList />, document.getElementById('test'))

2. react脚手架基本配置

2.1 创建项目与项目基本结构

  • 全局安装:

npm i -g create-react-app

  • 切换到想创项目的目录,使用命令:

create-react-app hello-react

  • 项目架构
    在这里插入图片描述
  • public中 index.html 文件说明
<meta charset="utf-8" />
		<!-- %PUBLIC_URL%代表public文件夹的路径 -->
		<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
		<!-- 开启理想视口,用于做移动端网页的适配 -->
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
    <meta name="theme-color" content="red" />
    <meta
      name="description"
      content="Web site created using create-react-app"
		/>
		<!-- 用于指定网页添加到手机主屏幕后的图标 -->
		<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
		<!-- 应用加壳时的配置文件 -->
		<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
		<!-- 若llq不支持js则展示标签中的内容 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
		<!--ID -->
    <div id="root"></div>

2.2 全局样式与模块样式

  1. 新建css文件命名时以 文件名.module.css 命名
  2. 导入时采用 import xxx from xxxx
    在这里插入图片描述

2.3 配置代理,fetch与axios发送请求

2.3.1 代理配置

  1. 第一步:创建代理配置文件

    在src下创建配置文件:src/setupProxy.js
    
  2. 编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/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': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }
    

    注意以上是react 17 以前版本 ,现在代理配置

const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    "/students",   // 匹配
    createProxyMiddleware({
      target: "http://localhost:5000",
      changeOrigin: true,
      // pathRewrite:{
      //   '^/api':""  // 忽略
      // }
    })
  );
};

2.3.2 fetch发送网络请求

注意点 fetch 发送请求,请求成功数据在 res.json()

  1. 版本01
   fetch(`https://api.github.com/search/users?q=${value}`)
      .then((res) => {
        console.log("服务器连接成功");
        // 1.调用res.JSON() 获取连接成功的数据并返回
        return res.json();
      })
      .then((res) => {
        console.log("获取数据成功", res);
      })
      .catch((err) => {
        console.log("失败的回调");
      });
  1. 优化版本2
  fetch(`https://api.github.com/search/users?q=${value}`)
       .then((data) => console.log(data))
       .catch((error) => console.error(error));
  1. 优化版本3
 const res = await fetch(`https://api.github.com/search/users?q=${value}`);
  const data = await res.json();
  console.log(data);

3 组件间的通信

3.1 父子通信,props ,事件

父组件在展示子组件时,会传递一些数据给子组件:采用如下方法

父组件通过 属性=值的形式来传递给子组件数据,或采用解构的形式传参
子组件通过this.props获取父组件传递过来的数据

export class App extends Component {
  constructor() {
    super()

    this.state = {
      books: [
        {name: "算法导论", price: 79},
        {name: "数据结构", price: 69},
        {name: "漫画算法", price: 59},
      ]
    }
  }
  render() {
    const { books } = this.state
    return (
      <div>
        {/* 将数据传递给子组件 */}
        <Header books={books}/>
      </div>
    )
  }
}

  • 子组件接受父组件传递的数据
export class Header extends Component {
  render() {
    // 接受父组件传递过来的参数
    const { books } = this.props
    return (
      <div>
        <ul>
          {
            books.map(item => {
              return (
                <li key={item.name}>
                  名称: {item.name} 价格: {item.price}
                </li>
              )
            })
          }
        </ul>
      </div>
    )
  }
}

回调函数,子组件向父组件传递消息:

在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;

import React, { Component } from 'react'
import ConterButton from './c-cpn/ConterButton'

export class App extends Component {
state = {conter: 100}
  changeConter() {
  let {conter}= this.state
  conter++
    this.setState({ conter })
  }
  render() {
    const { conter } = this.state
    return (
      <div>
        <h2>{conter}</h2>
        {/* 向子组件中传入一个事件 */}
        <ConterButton getConter={this.changeConter()}/>
      </div>
    )
  }
}
export default App
  • 子组件在按钮发生点击时, 对父组件的传递的函数进行回调,
import React, { Component } from 'react'

export class ConterButton extends Component {

  btnClick() {
    // 当按钮发生点击事件时, 对父组件传递过来的函数进行回调
    this.props.getConter()
  }
  render() {
    return (
      <div>
        <button onClick={this.btnClick}>+1</button>
      </div>
    )
  }
}
export default ConterButton

3.2 refs 与 事件冒泡

  • 父组件通过React.createRef()创建Ref,保存在实例属性myRef上。父组件中,渲染子组件时,定义一个Ref属性,值为刚创建的myRef。
  • 父组件调用子组件的myFunc函数,传递一个参数,子组件接收到参数,打印出参数。
  • 参数从父组件传递给子组件,完成了父组件向子组件通信。
import React, { Component, Fragment } from 'react';
class Son extends Component {
    myFunc(name) {
        console.log(name);
    }
    render() {
        return <div></div>;
    }
}

// 父组件
export default class Father extends Component {
        this.myRef = React.createRef();
    componentDidMount() {
        // 调用子组件的函数,传递一个参数
        this.myRef.current.myFunc('Jack');
    }
    render() {
        return (
            <div>
                <Son ref={this.myRef} />
            </div>
        );
    }
}

3.3 详解通信与类型限制

在这里插入图片描述1. 类型限制 安装prop-types库
2. 导入 import PropTypes from ‘prop-types’
3. 使用 static propTypes={ 要限制的key:PropTypes.类型.是否必传}

类型限制官网链接参考

3.4 消息订阅-发布机制

下载: npm install pubsub-js --save

使用: import PubSub from ‘pubsub-js’
订阅 PubSub.subscribe(‘delete’, function(data){ });
发布 PubSub.publish(‘delete’, data)

  • 组件A订阅信息
// A组件内的状态
state = {
    users:[],
    isFirst:true,
    isLoading:false,
    err:''
}
// 订阅了消息名为updateState的消息事件
componentDidMount(){
 // 方法返回两个值(第一个值是订阅与发布共有的属性,第二个是接受发布的信息)
    this.token = PubSub.subscribe('updateState',(_,data)=>{
        this.setState(data) // 将收到的状态data更新
    })
  }
// 页面销毁前删除消息订阅 以防消息泄露
componentWillUnmount(){
  PubSub.unsubscribe(this.token)
}
  • 组件B发布信息
// 发布消息名为updateState的消息事件
PubSub.publish('updateState',{isFirst:false,isLoading:true})
axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(res=>{
      PubSub.publish('updateState',{users:res.data.items,isLoading:false})
}).catch(err=>{
      PubSub.publish('updateState',{err:err.message,isLoading:false})
})

3.4.1 React18 eventBus使用

  1. 安装 npm i hy-event-store
  2. 创建实例
import { HYEventBus } from "hy-event-store"
const eventBus = new HYEventBus()
export default eventBus
  1. emit 传递事件
 nextClick() {
    eventBus.emit("bannerNext", {nickname: "kobe", level: 99})
  }
  1. on 监听事件
 componentDidMount() {
    eventBus.on("bannerNext", this.bannerNextClick, this)
  }
  1. 销毁,防止内存泄漏
 componentWillUnmount() {
    eventBus.off("bannerNext", this.bannerNextClick)
  }

4. 路由

  1. 下载 react-router-dom: npm install --save react-router-dom

4.1 路由的基本使用

  1. 在原生中页面的跳转我们可以直接使用a标签跳转,但是react中,需要使用link
  2. 导航区的a标签改为Link标签
<Link to="/xxxxx">Demo</Link>
  1. 展示区写Route标签进行路径的匹配
//  component 包裹要切换的组件
<Route path='/xxxx' component={Demo}/>
  1. <App>的最外侧包裹了一个<BrowserRouter><HashRouter>
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import Home from "./components/Home/index";
import About from "./components/About/index";
   /* BrowserRouter 只能有一个包裹因此可以调整到index中包裹 */
export default class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header">
              <h2>React Router Demo</h2>
            </div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              <Link className="list-group-item" to="/about">About</Link>
              <Link className="list-group-item" to="/home">Home</Link>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* 注册路由 */}
                <Route path="/about" component={About} />
                <Route path="/home" component={Home} />
              </div>
            </div>
          </div>
        </div>
      </BrowserRouter>
    );
  }
}

4.2 Navlink 的选中样式

  {/* Link 与NavLik的区别 activeClassName="选中的类名" */}
  <NavLink activeClassName="demo01" className="list-group-item" to="/about">About= </NavLink>
   {/* <NavLink className="list-group-item" to="/about"> About </NavLink> */}
  <NavLink className="list-group-item" to="/home"> Home</NavLink>

navlink的选中样式参考链接

4.2.1标签体的内容children

  1. 封装公共MyNavlink组件,this.props可以接受组件的传值
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
export default class MyNavLink extends Component {
  render() {
    // const { to } = this.props;
		console.log(this.props);
		// 1. this.props可以直接接受标签体的属性
		// 2. < NavLink children />  可直接展开显示children对应的value值
    return (
      <div>
        <NavLink className="list-group-item" {...this.props} />
      </div>
    );
  }
}

1.2 路由组件跳转,传值

 <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* 编写路由链接 */}
              {/* Link 与NavLik的区别 activeClassName="" */}
              {/* 1.children 对应的value about 可以不写在标签题里面 */}
              <MyNavLink to="/about" >About</MyNavLink>
              <MyNavLink to="/home" children="Home" />
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* 注册路由 */}
                <Route path="/about" component={About} />
                <Route path="/home" component={Home} />
              </div>
            </div>
          </div>
        </div>

在这里插入图片描述

4.3 Swith 单一匹配

  1. 通常情况下,path和component是一一对应的关系。
  2. Switch可以提高路由匹配效率(单一匹配)。

默认情况下,单个path相同时,会匹配component注册的所有页面
在这里插入图片描述

<Switch> 单一匹配,它的独特之处在于它专门呈现路由

在这里插入图片描述

4.4 模糊路由与精准匹配

1.默认使用的是模糊匹配:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2.开启严格匹配:<Route exact={true} path="/about" component={About}/>

4.5 路由重定向Redirect

参考链接

4.6 嵌套路由

  1. 嵌套路由可以采用模糊匹配或者精准匹配
  1. to="/home" 代表一级路由,to="/home/news" 二级路由
<div>
 <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/news"/>
		</Switch>
</div>

4.7 路由组件中的传参

4.7.1 params传参

  1. 需要link 标签传参时,在路由上必须接受传递的参数
  2. 接受传递的参数用this.props.match.params
    在这里插入图片描述

4.7.2 Search传参

  1. search传参,在路由上不需要接受传递的参数
  2. 路由组件使用传递的参数用this.props.location.search
  3. 获取到的search是urlencoded编码字符串,需要借助querystring解析

在这里插入图片描述

4.7.3 state传参

  1. state传递参数一般包含path与state,path路径,state传的属性与值
  2. 路由组件中接受传递的值使用this.props.location.state

在这里插入图片描述

4.7.4 总结

	1.params参数
		路由链接(携带参数)<Link to='/demo/test/tom/18'}>详情</Link>
		注册路由(声明接收)<Route path="/demo/test/:name/:age" component={Test}/>`
		接收参数:this.props.match.params
	2.search参数
		路由链接(携带参数)<Link to='/demo/test?name=tom&age=18'}>详情</Link>
		注册路由(无需声明,正常注册即可)<Route path="/demo/test" component={Test}/>
		接收参数:this.props.location.search
		备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
	3.state参数
		路由链接(携带参数)<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
		注册路由(无需声明,正常注册即可)<Route path="/demo/test" component={Test}/>
		接收参数:this.props.location.state
		备注:刷新也可以保留住参数

4.8 编程式导航

注意点 区分普通组件与路由组件,在普通组件中不能使用路由组件的API,比如:push,replace等,要想使用需要借助 withRouter 对普通组件进行包裹

  • 编程式导航借助this.prosp.history对象上的API对操作路由跳转、前进、后退
  • 主要有 push(),replace(),go(),goback()
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
	state = {
		messageArr:[
			{id:'01',title:'消息1'},
			{id:'02',title:'消息2'},
			{id:'03',title:'消息3'},
		]
	}
	replaceShow = (id,title)=>{
		//replace跳转+携带params参数
		//this.props.history.replace(`/home/message/detail/${id}/${title}`)
		//replace跳转+携带search参数
		// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
		//replace跳转+携带state参数
		this.props.history.replace(`/home/message/detail`,{id,title})
	}
	pushShow = (id,title)=>{
		//push跳转+携带params参数
		// this.props.history.push(`/home/message/detail/${id}/${title}`)
		//push跳转+携带search参数
		// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
		//push跳转+携带state参数
		this.props.history.push(`/home/message/detail`,{id,title})
	}
	back = ()=>{
		this.props.history.goBack()
	}
	forward = ()=>{
		this.props.history.goForward()
	}
	go = ()=>{
		this.props.history.go(-2)
	}
	render() {
		const {messageArr} = this.state
		return (
			<div>
				<ul>
					{
						messageArr.map((msgObj)=>{
							return (
								<li key={msgObj.id}>

									{/* 向路由组件传递params参数 */}
									{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}

									{/* 向路由组件传递search参数 */}
									{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}

									{/* 向路由组件传递state参数 */}
									<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>

									&nbsp;<button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
									&nbsp;<button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
								</li>
							)
						})
					}
				</ul>
				<hr/>
				{/* 声明接收params参数 */}
				{/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}

				{/* search参数无需声明接收,正常注册路由即可 */}
				{/* <Route path="/home/message/detail" component={Detail}/> */}

				{/* state参数无需声明接收,正常注册路由即可 */}
				<Route path="/home/message/detail" component={Detail}/>
				<button onClick={this.back}>回退</button>&nbsp;
				<button onClick={this.forward}>前进</button>&nbsp;
				<button onClick={this.go}>go</button>

			</div>
		)
	}
}

4.9 BrowserRouter与HashRouter的区别

  • 底层原理不一样:
    • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
    • HashRouter使用的是URL的哈希值。
  • path表现形式不一样
    • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    • HashRouter的路径包含#,例如:localhost:3000/#/demo/test
  • 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state保存在history对象中。
    • HashRouter刷新后会导致路由state参数的丢失!!!
  • HashRouter可以用于解决一些路径错误相关的问题。

4.react-router6+版本

  1. React Router 以三个不同的包发布到 npm 上,它们分别为:

    1. react-router: 路由的核心库,提供了很多的:组件、钩子。
    2. react-router-dom: 包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如 <BrowserRouter>
    3. react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:<NativeRouter>等。
  2. 与React Router 5.x 版本相比,改变了什么?

    1. 内置组件的变化:移除<Switch/> ,新增 <Routes/>等。

    2. 语法的变化:component={About} 变为 element={<About/>}等。

    3. 新增多个hook:useParamsuseNavigateuseMatch等。

4.1 .Component

  1. v6版本中移出了先前的<Switch>,引入了新的替代者:<Routes>
  2. <Routes><Route>要配合使用,且必须要用<Routes>包裹<Route>
  3. <Route> 相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。
  4. <Route caseSensitive> 属性用于指定:匹配时是否区分大小写(默认为 false)。
  5. 当URL发生变化时,<Routes> 都会查看其所有子 <Route> 元素以找到最佳匹配并呈现组件 。
  6. <Route> 也可以嵌套使用,且可配合useRoutes()配置 “路由表” ,但需要通过 <Outlet> 组件来渲染其子路由。

4.1.1 <Routes/> 与 <Route/>

     <>
            <Link to="/about">About</Link>
            <Link to="/home">Home</Link>
        <ul className="nav">
          {/* 注册路由 */}
          <Routes>
            <Route path="/about" element={<About />}></Route>
            <Route path="/home" element={<Home />}></Route>
          </Routes>
        </ul>
      </>

4.1.2 <Link> 与<NavLink>

  1. 注意: 外侧需要用<BrowserRouter><HashRouter>包裹。
  2. NavLink与<Link>组件类似,且可实现导航的“高亮”效果。
  3. 示例代码:
  <Link to="/路径">Home</Link>
  <NavLink className={computedClassName} to="路径">Home</NavLink>

NavLink高亮效果的实现

   // 注意: NavLink默认类名是active,下面是指定自定义的class
   //自定义样式
   <NavLink
       to="login"
       className={({ isActive }) => {
           console.log('home', isActive)
           return isActive ? 'base one' : 'base'
       }}
   >login</NavLink>
   /*
   	默认情况下,当Home的子组件匹配成功,Home的导航也会高亮,
   	当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果。
   */
   <NavLink to="home" end >home</NavLink>

4.1.3. <Navigate>

  1. 作用:只要<Navigate>组件被渲染,就会修改路径,切换视图。
  2. replace属性用于控制跳转模式(push 或 replace,默认是push)。
   <Routes>
            <Route path="/about" element={<About />}></Route>
            <Route path="/home" element={<Home />}></Route>
            {/* 默认为push模式,replace为true时跳转为replace  */}
            <Route path="/" element={<Navigate to="/home" replace={true} />}></Route>
  </Routes>

4.1.4 <Outlet>

  1. <Route>产生嵌套时,渲染其对应的后续子路由。既指定路由呈现位置
const RoutesList = [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "message",
        element: <Message />,
      },
      {
        path: "news",
        element: <News />,
      },
    ],
  },
];

// 二级路由
 <div className="NavContetn">
	{/* 指定路由组件呈现的位置Outlet */}
	<Outlet />
 </div>

4.2 路由Hooks

  • 动态路由的传参
    • 比如/detail的path对应一个组件Detail;
    • 如果我们将path在Route匹配时写成/detail/:id,那么 /detail/abc、/detail/123都可以匹配到该Route,并且进行显示;
// router
	{
		path: '/detail/:id/:title',
		element: <Details />
	},
//navigate 传参,注意函数式组件与类组件
 navigate(`/detail/${id}/${title}`, { replace: true })

4.2.1 useRoutes()

  1. 作用:根据路由表,动态创建<Routes><Route>
   //路由表配置:src/routes/index.js
   import About from '../pages/About'
   import Home from '../pages/Home'
   import {Navigate} from 'react-router-dom'
   export default [
   	{
   		path:'/about',
   		element:<About/>
   	},
   	{
   		path:'/home',
   		element:<Home/>
   	},
   	{
   		path:'/',
   		element:<Navigate to="/about"/>
   	}
   ]
   
   //App.jsx
   import React from 'react'
   import {NavLink,useRoutes} from 'react-router-dom'
   import routes from './routes'
   
   export default function App() {
   	//根据路由表生成对应的路由规则
   	const element = useRoutes(routes)
   	return (
   		<div>
   			......
         {/* 注册路由 */}
         {element}
   		  ......
   		</div>
   	)
   }

4.2.2 useNavigate()编程式导航

  1. 作用:返回一个函数用来实现编程式导航。
   import React from 'react'
   import {useNavigate} from 'react-router-dom'
   export default function Demo() {
     const navigate = useNavigate()
     const handle = () => {
       //第一种使用方式:指定具体的路径
       navigate('/login', {
         replace: false,
         state: {a:1, b:2}
       }) 
       //第二种使用方式:传入数值进行前进或后退,类似于5.x中的 history.go()方法
       navigate(-1)
     }
     
     return (
       <div>
         <button onClick={handle}>按钮</button>
       </div>
     )
   }

4.2.3 useParams()

  1. 作用:返回当前匹配路由的params参数,类似于5.x中的match.params
//  路由表
 children: [
          {
            path: "details/:name",
            element: <Details />,
          },
        ],
//导航路由传参
 <NavLink to={`Details?name=${UserInfo.name}`}> Details</NavLink>
// useParams接受参数
import { useParams } from 'react-router-dom'
export default function Details() {
	const add=useParams()
	return (
    <div>
         {add.name}	
    </div>
  );
}

4.2.4 ueSearchParams()

  1. 作用:用于读取和修改当前位置的 URL 中的查询字符串。
  2. 返回一个包含两个值的数组,内容分别为:当前的seaech参数、更新search的函数。
//导航路由传参
  <NavLink to={`Details?name=${UserInfo.name}`}> Details</NavLink>
//接受参数
import { useSearchParams } from "react-router-dom";
export default function Details() {
	//  setSearch 用于更新search 参数
  const [search, setSearch] = useSearchParams();
	console.log(search,setSearch);
	// setSearch('name="user"');
	//  返回的一个函数
	 const name = search.get("name");
  return (
    <div> <li>{name}</li></div>
  );
}

4.2.5 useLocation() state传参

  1. 作用:获取当前 location 信息,对标5.x中的路由组件的location属性。
//导航路由传参
 <NavLink to="details" state={{ name: UserInfo.name }}> Details</NavLink>
//接受参数
import React from "react";
import { useLocation } from "react-router-dom";
export default function Details() {
 const {state:{name}}= useLocation();
	
  return (
    <div>
        <li>{name}</li>
    </div>
  );
}

4.2.6. useMatch()

  1. 作用:返回当前匹配信息,对标5.x中的路由组件的match属性。
   <Route path="/login/:page/:pageSize" element={<Login />}/>
   <NavLink to="/login/1/10">登录</NavLink>
   export default function Login() {
     const match = useMatch('/login/:x/:y')
     console.log(match) //输出match对象
     //match对象内容如下:
     /*
     	{
         params: {x: '1', y: '10'}
         pathname: "/LoGin/1/10"  
         pathnameBase: "/LoGin/1/10"
         pattern: {
         	path: '/login/:x/:y', 
         	caseSensitive: false, 
         	end: false
         }
       }
     */
     return (
     	<div>
         <h1>Login</h1>
       </div>
     )
   }

4.2.7 useInRouterContext()

  1. 作用:如果组件在 <Router> 的上下文中呈现,则 useInRouterContext 钩子返回 true,否则返回 false。

4.2.8 useNavigationType()

  1. 作用:返回当前的导航类型(用户是如何来到当前页面的)。
  2. 返回值:POPPUSHREPLACE
  3. 备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)。
  4. 作用:给定一个 URL值,解析其中的:path、search、hash值。

4.3 补充 类组件增强 withRouter

  • 类组件不能使用hooks,因此封装高阶组件 转换为函数式组件对类组件做增强

// hoc/index.js
import { useLocation, useNavigate, useParams } from 'react-router-dom';
export function withRouter(WapperWithRouter) {
	return function (props) {
		const navigate = useNavigate()
		const params=useParams()
		const location = useLocation ()
		const router = { navigate, params, location }
		return < WapperWithRouter  {...props} router={router} />
	}
}
import React, { Component } from 'react';
import { Link, Outlet } from 'react-router-dom';
import { withRouter } from '../../hoc';
 class About extends Component {
	navigateTo(path){
   console.log(path);
	 console.log(this.props.router.navigate(path));
	}
	render() {
		return (
			<div>
				<Link to="/about/recommend">音乐列表展示</Link>
				<Link to="actor"> 音乐艺人展示</Link>
				{/* 注意路径的匹配  to 与path保持一致 */}
				<button  onClick={()=>this.navigateTo('classqa')}> 类组件如何实现跳转</button>
				<div>
					<Outlet />
				</div>
			</div>
		)
	}
}
export default withRouter(About)

至此 类组件的props就有了路由的一些属性可以用navigate

5. redux

参考
参考2

6. React Hooks 及其扩展

  1. 在函数式组件中并没有this,因此React提供的Hooks,这就让你在函数式组件中可以使用state或其他特性
  2. 常使用的Hooks有 React.useState()React.useEffect(), React.useRef()

6.1 setState的两种用法

注意点 : setState更新是异步的

6.1.1 对象更新

  • setState(stateChange, [callback])------对象式的setState
    1. stateChange为状态改变对象(该对象可以体现出状态的更改)
    2. callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
   this.setState({count},()=>{
        console.log(this.state.count);
   })
   // 或者
   this.setState({count:count+1})

6.1.2 函数回调式更新

  • setState(updater, [callback])------函数式的setState
    1. updater为返回stateChange对象的函数。
    2. updater可以接收到state和props。
    3. callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
  • 函数回调直接接受 state
 this.setState((state)=>( { count: state.count+1 }))

6.2 Hooks 之 useState

  1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
 语法: const [xxx, setXxx] = React.useState(initValue)  
  • useState()说明:
    • 参数: 第一次初始化指定的值在内部作缓存
    • 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
  • setXxx()2种写法:
    • setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    • setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
import React from "react";
// 1. 注意点 函数式组件命名 首字母大写
// 2. useState 初始调用后会把值缓存起来,不会因为函数的再次调用把count重新赋值为0
// 3.hooks 必须在最顶层使用 不能在if for 等使用
// 4.useState 如果没有传递参数,那么初始化值为undefined
// 5. 箭头函数写法  const DemoCount= React.memo(()=>{ })
export default function DemoCount(params) {
  // 第一个是返回的值, 第二个参数返回一个函数设置第一个返回的值
  let [count, setCount] = React.useState(0);
  function add() {
    // count++;
    // setCount(count); //第一种写法
    setCount(count=>count+1)
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>点击+1</button>
       <button onClick={()=>setCount(count-1)}>点击-1</button>
    </div>
  );
}

6.3 Hooks之useEffect

  1. Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类似组件中的生命周期钩子)
  • React中的副作用操作:
    • 发ajax请求数据获取
    • 设置订阅 / 启动定时器
    • 手动更改真实DOM
语法: useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
  • 可以把 useEffect Hook 看做如下三个函数的组合
  componentDidMount()
  componentDidUpdate()
  componentWillUnmount() 
// 卸载组件
import React from "react";
function App() {
  const [showChild, setShowChild] = React.useState(true);
  function unmound() {
    setShowChild(false);
  }
  return (
    <>
      {showChild && <DemoCount />}
      <button onClick={unmound}>卸载组件</button>
    </>
  );
}
// useEffect 相当于componentDidMount,或者 componentDidUpdate (主要看第二个值传不传)
function DemoCount() {
  let [count, setCount] = React.useState(0);
  React.useEffect(() => {
    let timer = setInterval(() => {
      setCount((count) => count + 1);
    }, 1000);
    return () => {
      // 返回值为清除定时器
      clearInterval(timer);
    };
  }, []); // 传一个数组,表示检测谁,(默认不传,检测所有,传空数组谁也不检测)
  function add() {
    setCount((count) => count + 1);
  }
  return (
    <div className="id">
      <h1>{count}</h1>
      <button onClick={add}>点击+1</button>
    </div>
  );
}
export default App;

6.4 Hooks之useRef

  1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
 语法: const refContainer = useRef()
function App() {
  const myrefs = React.useRef();
  function getRefsValue() {
    console.log(myrefs.current.value);
  }
  return (
    <div>
      <input ref={myrefs} type='text' />
      <button onClick={getRefsValue}>ref</button>
    </div>
  );
}

6.5 Fragment代替根标签

  1. render 函数中都都需一个根标签,这样会都渲染一个不需要的dom节点,利用Fragment代替就不会渲染
import React, { Fragment } from "react";
// Fragment 忽略标签,|| <></> 区别在于是否需要key 
function App() {
  const myrefs = React.useRef();
  function getRefsValue() {
    console.log(myrefs.current.value);
  }
  return (
      <Fragment>
        <input ref={myrefs} type="text" />
        <button onClick={getRefsValue}>ref</button>
      </Fragment>
  );
}

6.6 Context的使用

  1. Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
  2. Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差

6.6.1 提供的API

React.createContext

使用此API可以创建一个Context对象,组件会从最近的Provider中读取对应的值。只有当组件所处的树种不存在Provider时,defaultValue参数才会生效

const MyContext = React.createContext(defaultValue);

Context.Provider

  • Context对象会返回一个Provider组件
  • Provider接受一个value属性,传递给消费组件 当Provider的value属性值更变时,内部的所有消费组件都会重新渲染
  • context会根据引用标识进行重新渲染,所以当向value传递一个对象时,需要注意:当Provider重新渲染时,可能会触发Consumer意外渲染。为了防止这种情况,将value状态提升到父节点的state中
<MyContext.Provider value={某个值}/>

Context.Consumer

  1. Context对象会返回一个Consumer组件
  1. 需要一个函数作为子元素,函数接收context值,返回一个React节点
  2. 传递给函数的value值等价于组件树上方离这个context最近的Provider提供的value值。如果没有对应的Providervalue参数等同传递给createContext()的defaultValue
<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

Class.contextType

  1. 此属性可以让你使用this.context来获取最近Context上的值。你可以在任何生命周期中访问到它,包括render函数中
const MyContext = React.createContext()
class MyClass extends React.Component {
  render() {
    let value = this.context;
    /* 基于这个值进行渲染工作 */
  }
}
MyClass.contextType = MyContext
  1. 同时,你也可以使用实验性的public class fields语法中的static类属性初始化contextType ,此外一般都是使用第二种用法
const MyContext = React.createContext()
class MyClass extends React.Component {
  static contextType = MyContext
  render() {
    let value = this.context;
    /* 基于这个值进行渲染工作 */
  }
}

6.6.2 注意点

  1. 在使用时,类组件使用Provider, Consumer 可以用于类组件和函数式组件
// A->B->C , context 组件通信
import React, { Component } from "react";
// 需求C组件展示A组件的Name
// 创建上下文context
const MyContext = React.createContext();
export default class App extends Component {
  state = {
    name: "我是A组件,需要在C组件中展示",
    nameInfo: "A组件的详细信息",
  };
  render() {
    const { name, nameInfo } = this.state;
    return (
      <div>
        <h2>A</h2>
        <h5>----------------------</h5>
        <MyContext.Provider value={{ name, nameInfo }}>
          <B />
        </MyContext.Provider>
      </div>
    );
  }
}

class B extends Component {
  render() {
    return (
      <>
        <h2> B</h2>
        <h5>----------------------</h5>
        <C />
      </>
    );
  }
}
// class C extends Component {
//   // 声明接受context
//   static contextType = MyContext;
//   render() {
//     console.log(this.context); //
//     return (
//       <div>
//         <h2>C</h2>
//         {/* <a href="1">{this.context}</a> */}
//         <h5>----------------------</h5>
//       </div>
//     );
//   }
// }
// 函数式组件使用context,Provider只使用于类组件, Consumer 可以用于类组件和函数式组件
function C() {
  return (
    <div>
      <h2>C</h2>
      <MyContext.Consumer>  
        {
          value=> {
            return `${value.name}`
          }
        }
      </MyContext.Consumer>
      <h5>----------------------</h5>
    </div>
  );
}

6.3 useContext

  • Context Hook允许我们通过Hook来直接获取某个Context的值
import React, { memo, useEffect, useState } from 'react';
// import { ThemeContext } from './context/themContext';
// import { MyUserInfoContext } from './context/userInfo';
import Hooks from './hooks';
//  自定义hooks 需要以use开头
function useSumApp(Name) {
  useEffect(() => {
    console.log(Name)
    return () => {
      console.log(Name)
    }
  }, [Name])
}

const App01 = memo(() => {
  // const themeStyleContext = useContext(ThemeContext)
  // const userInfoContext = useContext(MyUserInfoContext)
  const [themeStyleContext, userInfoContext] = Hooks()
  console.log(themeStyleContext);
  useSumApp('App01')
  return (
    <div>App01</div>
  )
})
const App02 = memo(() => {
  // const themeStyleContext = useContext(ThemeContext)
  // const userInfoContext = useContext(MyUserInfoContext)
  useSumApp('App02')
  return (
    <div>App02</div>
  )
})

const App = memo(() => {
  const [isShow, setIsShow] = useState(true)

  return (
    <div>
      <button onClick={() => setIsShow(!isShow)}>updata Component</button>
      {isShow && <App01 />}
      {isShow && <App02 />}
    </div>
  )
})

export default App

hooks的封装

import { ThemeContext } from './context/themContext';
import { MyUserInfoContext } from './context/userInfo';

import { useContext } from 'react';

export default function Hooks() {
	const themeStyleContext = useContext(ThemeContext)
	const userInfoContext = useContext(MyUserInfoContext)
	return [themeStyleContext, userInfoContext]
}

6.7 PureComponent 拒接子组件render重新渲染

注意点 , 只要调用setState 父子组件中的render函数都会调用

  • 避免上述情况,可以采用的方案为:
  1. 重写shouldComponentUpdate()方法 比较新旧state或props数据, 如果有变化才返回true,
  2. 如果没有返回false
  shouldComponentUpdate(nextProps, nextState) {
    // 这里可以判断是否更新子组件的render 当nextState与this.state的值相同时,返回false不同返回ture ,简单点说就是阀门是否打开
    if (nextState.name === this.state.name) {
      return false;
    } else {
      return true;
    }
  }
  1. 使用PureComponent PureComponent重写了shouldComponentUpdate(),
  2. 只有state或props数据有变化才返回true 注意: 只是进行state和props数据的浅比较,
  3. 如果只是数据对象内部数据变了, 返回false 不要直接修改state数据, 而是要产生新数据
import React, { PureComponent } from "react";
// PureComponent 判断 子组件是否使用父组件的内容,数据更新时是否调用子组件的render
export default class App extends PureComponent {}

6.8 render props 插槽

  1. Vue中: 使用slot技术, 也就是通过组件标签体传入结构
  2. React中:使用children props: 通过组件标签体传入结构,使用render props: 通过组件标签属性传入结构, 一般用render函数属性

6.8.1 this.props.children 渲染

1. A组件使用
<B>
  <C>xxxx</C>
</B>
2.B要使用C组件中调用 {this.props.children}渲染
  • 但是上面也存在一个问题: 如果B组件需要A组件内的数据, ==> 做不到

6.8.2 this.props 渲染

// 父组件传递
 const btn = <button>按钮2</button>;
  <NavBarTwo
    leftSlot={btn}
     centerSlot={<h2>呵呵呵</h2>}
      rightSlot={<i>斜体2</i>}
   />
// 子组件
const { leftSlot, centerSlot, rightSlot } = this.props

    return (
      <div className='nav-bar'>
        <div className="left">{leftSlot}</div>
        <div className="center">{centerSlot}</div>
        <div className="right">{rightSlot}</div>
      </div>
  • 图解
    在这里插入图片描述

6.8.3 render props 渲染

<B render={(data) => <C data={data}></C>}></B>
B组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data} 
  • 图解
    在这里插入图片描述
    代码参考
 import React, { PureComponent } from "react";
export default class App extends PureComponent {
  render() {
    return (
      <div style={{ width: "1200px", height: "300px", background: "red" }}>
        <h2>A</h2>
        <h5>--------App------------</h5>
        {/* 相当于Vue里面的插槽, */}
        <B render={(name) => <C name={name} />} />
      </div>
    );
  }
}
class B extends PureComponent {
  state = { name: "我是B组件需要在C组件展示" };
  render() {
    console.log("@,render Children");
    return (
      <div
        style={{
          width: "300px",
          height: "200px",
          margin: "0 auto",
          background: "#fff",
        }}
      >
        <h2> B 组件</h2>
        <h5>----------------------</h5>
        {/* 调用C组件的render */}
        {/* {this.props.children}       */}
        {/* 第二种方式:预留插槽 */}
        {this.props.render(this.state.name)}
      </div>
    );
  }
}
class C extends PureComponent {
  render() {
    return (
      <div style={{ background: "#0f03d6", color: "#fff", height: "80px" }}>
        <h2> C 组件</h2>
        <a href="ccc" style={{color:'#ddcc00'}}> {this.props.name}</a>
      </div>
    );
  }
}

6.9 错误异常边界

  • 错误边界:用来捕获后代组件错误,渲染出备用页面
  • 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
  • 使用
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}
componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

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

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

相关文章

2、boostrap 多数据类型表单

fileinput 视频图片文本数据表单 插件下载地址&#xff1a;https://github.com/kartik-v/bootstrap-fileinput/ 1、多类型数据from测试 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</tit…

Jeston Xavier NX 模块将系统迁移到NVME存储

大家好&#xff0c;我是虎哥&#xff0c;最近完成了自己设计的第一个Xavier NX的载板设计和打样&#xff0c;虽然还有一些小的不完善的地方&#xff0c;但是可以正常使用&#xff0c;这里记录和分享一下我自己设计的载板上如何实现系统迁移。 我自己使用SDK Manager 安装了所有…

c# Invoke使用

在多线程编程中&#xff0c;我们经常要在工作线程中去更新界面显示&#xff0c;而在多线程中直接调用界面控件的方法是错误的做法&#xff0c;Invoke 和 BeginInvoke 就是为了解决这个问题而出现的&#xff0c;使你在多线程中安全的更新界面显示。 正确的做法是将工作线程中涉…

青少年机器人技术一级考试备考重点(三):简单机械

随着机器人技术的飞速发展&#xff0c;越来越多的青少年开始关注并参与其中。青少年机器人技术考试作为一项评估学生机器人技术水平的重要考试&#xff0c;备受广大青少年和家长的关注。为了更好地备战青少年机器人技术一级考试&#xff0c;了解考试的学习要点和备考重点是非常…

【C++初阶】11. list的使用及模拟实现

1. list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素。list与…

清华青年AI自强作业hw5:基于CNN实现CIFAR10分类任务

清华青年AI自强作业hw5&#xff1a;基于CNN实现CIFAR10分类任务 简述作业实现遇到的问题相关链接 一起学AI系列博客&#xff1a;目录索引 简述 hw5作业为利用深度卷积神经网络实现CIFAR_10数据集十分类问题&#xff0c;帮助理解CNN的前向传播结构。 CIFAR-10是一个常用的彩色图…

现代处理器结构

本文翻译自&#xff1a;Modern Microprocessors A 90-Minute Guide!&#xff0c;&#xff0c;我认为原文是相当好的计算机体系结构方面的概述&#xff0c;与时代相结合是国内计算机课本普遍缺失的一环&#xff0c;本文可作为一个有效的补充&#xff0c;向原作者和其他译者表示感…

青岛大学_王卓老师【数据结构与算法】Week03_09_线性表的链式表示和实现9_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c;另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础–…

软件测试面试-银行篇

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

如何在VUE项目中使用svg图标

一文带你搞定svg图标的使用&#xff01; 文章目录 前言一、SVG相较于字体图标的优点二、使用步骤1.新建一个vue2项目2.安装项目依赖3 .在src目录下新建文件夹4.创建svg-icon组件5.在main.js中引入icons下的index.js6.配置 vue.config.js7.步骤完毕&#xff0c;检验成果 总结 前…

LoadRunner负载均衡与IP欺骗

目录 前言&#xff1a; 一、IP wizard 二、一个IP欺骗测试脚本 三、Controller与Runtime setting设置 四、mdrv.dat配置调整 五、查看实现效果 【结语】 前言&#xff1a; 在使用 LoadRunner 进行负载测试时&#xff0c;负载均衡和 IP 欺骗是两个重要的概念。 这次出差…

【UE Unreal Camera】【保姆级教程】手把手教你通过UE打开摄像头/预览图像画面

【UE Unreal Camera】【保姆级教程】手把手教你通过UE打开摄像头/预览图像画面 概述 最近在做一个游戏的开发&#xff0c;需要通过UE去打开用户的摄像头&#xff08;ios,android上的手机摄像头&#xff1b;windows,mac上的电脑摄像头&#xff09;&#xff0c;预览图像&#xf…

NLP(五十七)LangChain使用Google Search Agent

大模型存在的问题 大模型在给人带来眼前一亮的表现&#xff0c;深深地震撼各行各业人们的同时&#xff0c;其本身也存在着不少问题。   以OpenAI的ChatGPT模型为例&#xff0c;其存在的问题有&#xff1a; 事实错误&#xff0c;容易一本正经地胡说八道&#xff0c;造成幻觉问…

重定向爬虫和多线程爬虫

前言 重定向爬虫是指在抓取网页时&#xff0c;如果目标网站内部存在重定向机制&#xff0c;即当你访问一个网页时&#xff0c;服务器会把你重定向到另一个目标网页。重定向爬虫可以帮助我们发现这种重定向链接&#xff0c;从而更有效地抓取目标网站的内容。 要实现重定向爬虫…

分享一些关于 CSS Grid 基础入门知识

网格系统&#xff08;CSS Grid&#xff09;是CSS中最重要的特性之一。它能够以简单的方式将元素对齐到列和行中。CSS网格使得设计复杂且响应式的网页变得更加容易&#xff0c;无需使用浮动、表格或定位。它还具有许多更强大的功能&#xff0c;如果你多加练习&#xff0c;就能发…

北京大学2018计算机学科夏令营上机考试

目录 A:计算两个日期之间的天数【暴力不水】 B:回文子串【暴力不水】 C:The Die Is Cast【DFS】 D:Euro Efficiency【看不懂】 E:重要逆序对【归并排序】 F:Tram【看不懂】 G:食物链【图】 H:DFS spanning tree【不会】 A:计算两个日期之间的天数【暴力不水】 //…

《微服务架构设计模式》第四章 使用Saga管理事务

内容总结自《微服务架构设计模式》 使用Saga管理事务 一、XA解决方案存在问题二、使用Saga管理事务Saga是什么补偿事务是什么Saga协调模式协同式Saga编排式Saga 隔离性Saga结构 三、总结 一、XA解决方案存在问题 在多个服务、数据库和消息代理之间维持数据一致性的传统方式是采…

小黑重庆归来,眼睛复查顺利,见到了三年没见的线上同门的leetcode之旅:剑指 Offer II 015. 字符串中的所有变位词

小黑代码1:滑动窗口 class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:# 字符串长度n_s len(s)n_p len(p)if n_s < n_p:return []# 差值数组arr [0] * 26# 初始窗口for i in range(n_p):arr[ord(p[i])-97] - 1arr[ord(s[i])-97] 1# 计算初始窗口…

Web服务器群集:Tomcat配置https证书

目录 一、理论 1.SSL 2.HTTPS协议和HTTP协议的区别 3.https证书配置 4.tomcat强制使用https 二、实验 1.https证书配置过程 2.tomcat强制使用https 三、总结 一、理论 1.SSL &#xff08;1&#xff09;概念 SSL是网络加密传输协议&#xff0c;是支持在网络服务器(主…

chatgpt赋能python:烧录代码过程是怎样的

烧录代码过程是怎样的 烧录代码是将编写好的程序代码烧录进内置闪存器件&#xff08;Flash&#xff09;或外部存储器&#xff08;SD卡、EEPROM等&#xff09;中的过程。本文将介绍烧录代码的具体过程和常用工具&#xff0c;以及一些注意事项。 烧录代码的步骤 步骤一&#x…