React学习[三]

news2024/9/20 14:44:53

React学习[三]

  • 组件的props
    • props的基本使用
    • props的特点
  • 组件通讯的三种方式
    • 父组件传递数据给子组件
    • 子组件传递数据给父组件
    • 兄弟组件传递
  • Context
  • props进阶
    • children属性
    • props校验
      • 约束规则
    • props的默认值
  • 组件的生命周期
    • 生命周期三个阶段
      • 创建时(挂载阶段)
      • 更新时(更新阶段)
      • 卸载时
    • 不常用的生命周期
  • render-props和高阶组件
    • render props模式
      • 使用步骤
      • 演示Mouse组件复用
      • children代替render模式
      • 代码优化
    • 高阶组件
      • 使用步骤
      • 设置`displayName`
      • 传递props
  • React原理
    • setState()的说明
      • 更新数据
      • 推荐语法
      • 第二个参数
    • JSX语法的转换过程
    • 组件更新机制
    • 组件性能优化
      • 减轻state
      • 避免不必要的重新渲染
      • 纯组件
        • 作用及使用
        • 实现原理
    • 虚拟DOM和Diff算法
      • 虚拟 DOM
      • Diff算法
  • React路由
    • React路由介绍
      • 路由的基本使用
      • 常用组件说明
    • 路由执行过程
    • 编程式导航
    • 默认路由
    • 匹配模式
      • 模糊匹配模式
      • 精准匹配

组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要共享某些数据为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯。

组件的props

props的基本使用

组件是封闭的,要接收外部数据应该通过props来实现
props的作用:接收传递给组件的数据

  • 传递数据:给组件标签添加属性
  • 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据

函数组件中

/* props */
// 2接收数据
const Hello = props => {
	// props是个对象
	console.log(props)
	return (
		<div>
			<h1>props:{props.name}</h1>
		</div>
	)
}
// 1传递数据
ReactDOM.render(<Hello name="jack" age={19}/>, document.getElementById('root' ))

类组件

class Hello extends React.Component {
	render(){
		return (
			<div>接收到的数据: {this.props.age}</div>
		)
	}
}
ReactDOM.render(< Hello name="rose" age={19} />, document.getElementById('root'))

props的特点

1.可以给组件传递任意类型的数据
2. props 是只读的对象,只能读取属性的值,无法修改对象

const Hello = props => {
  // props是个对象
  console.log(props)
  props.fn();
  // 修改props属性的值,错误演示! props只能读不能修改
  props.name = 'tom'
  return (
    <div >
      <h1> props: {props.name} </h1>
    </div>
  )
}

3.注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props!

class Hello extends React.Component {
  //推荐将props传递给父类构造函数
  constructor(props) {
    super(props)
    console.log(props)
  }
  render() {
    return <div>接收到的数据: {this.props.age}</div>
  }
}

组件通讯的三种方式

父组件传递数据给子组件

  • 父组件提供要传递的state数据
  • 给子组件标签添加属性,值为state中的数据
  • 子组件中通过props接收父组件中传递的数据
class Parent extends React.Component {
	state= { lastName:'王'}
	render () {
		return (
			<div>
				传递数据给子组件: <Child name={this.state.lastName} />
			</div>
		)
	}
}

子组件

function Child(props) {
	return <div>子组件接收到数据: {props.name}</div>
}

子组件传递数据给父组件

利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数

  • 父组件提供一个回调函数,用来接收数据
  • 将该函数作为属性的值,传递给子组件
  • 子组件通过props调用回调函数
  • 将子组件的数据作为参数传递给回调函数
class Parent extends React.Component {
  state = {
    parentMsg: ''
  }
  // 提供一个回调函数
  getChildMsg = (data) => {
    console.log('接收到子组件中传递过来的数据:', data)
    this.setState({
      parentMsg: data
    })
  }

  render() {
    return (
      <div className='parent' >
        父组件:{this.state.parentMsg}
        < Child getMsg={this.getChildMsg} />
      </div>
    )
  }
}

// 子组件
class Child extends React.Component {
  state = { msg: 'React' }
  // 子组件调用父组件中传递过来的回调函数
  handleClick = () => {
    this.props.getMsg(this.state.msg)
  }
  render() {
    return (
      <div className='child'>
        子组件:
        < button onClick={this.handleClick} > 给父组件传递数据</ button>
      </div>
    )
  }

}
ReactDOM.render(< Parent />, document.getElementById('root')) 

兄弟组件传递

  • 共享状态(数据)提升到最近的公共父组件中,由公共父组件管理这个状态
  • 思想:状态提升
  • 公共父组件职责:
    • 提供共享状态;
    • 提供操作共享状态的方法。
  • 要通讯的子组件只需要通过props接收状态或操作状态的方法
class Counter extends React.Component {
  // 提供共享状态
  state = {
    count: 0
  }
  // 提供修改状态的方法
  onIncrement = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div >
        <Child1 count={this.state.count} />
        <Child2 onIncrement={this.onIncrement} />
      </div>
    )
  }
}
// 子组件
const Child1 = props => {
  return (
    <h1>计数器:{props.count}</h1>
  )
}
const Child2 = (props) => {
  return (
    <button onClick={props.onIncrement}>+1</button>
  )
}
ReactDOM.render(<Counter />, document.getElementById('root')) 

Context

作用:跨组件传递数据(比如主题、语言等)
使用步骤:
1、调用React. createContext()创建Provider (提供数据)和Consumer (消费数据)两个组件。

const { Provider, Consumer } = React.createContext ()

2、使用Provider组件作为父节点。

<Provider>
	<div className="App">
		<Child1 />
	</div>
</Provider>

3、设置value属性,表示要传递的数据。

<Provider value="pink"></Provider>

4、调用Consumer组件接收数据。

<Consumer>
	{ data => <span>data参数表示接收到的数据 -- {data} </span> }
</Consumer>
/* 
  context
    1.如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
    2. Context提供 了两个组件: Provider 和Consumer
    3. Provider组件 :用来提供数据
    4. Consumer组件 :用来消费数据
*/
// 创建context得到两个组件
const { Provider, Consumer } = React.createContext()

class App extends React.Component {
  render() {
    return (
      <Provider value="pink">
        <div className="app">
          <Node />
        </div>
      </Provider>
    )
  }
}
const Node = props => {
  return (
    <div className="node">
      <SubNode />
    </div>
  )
}
const SubNode = props => {
  return (
    <div className="subnode">
      <Child />
    </div>
  )
}
const Child = props => {
  return (
    <div className="child">
      <Consumer>
        {data => <span>我是子节点 -- {data} </span>}
      </Consumer>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

props进阶

children属性

  • children属性: 表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
  • children属性与普通的props一样,值可以使任意值(文本、react元素、组件、甚至是函数)
/* // children为文本节点
const App = props => {
  console.log(props)
  return (
    <div>
      <h1>组件标签的子节点: </h1>
      {props.children}
    </div>
  )
}
ReactDOM.render(<App>我是子节点</App>, document.getElementById('root')) */

// // children为 jsx或react组件
// const Test = () => <button>我是button组件</button>
// const App = props => {
//   console.log(props)
//   return (
//     <div>
//       <h1>组件标签的子节点: </h1>
//       {props.children}
//     </div>
//   )
// }
// ReactDOM.render(<App>
//   <Test />
//   {/* <p>我是子节点,是一个p标签</p> */}
// </App>, document.getElementById('root'))

// children是函数
const App = props => {
  console.log(props)
  props.children()
  return (
    <div>
      <h1>组件标签的子节点: </h1>
    </div>
  )
}
ReactDOM.render(<App>
  {
    () => { console.log('这是一个函数子节点') }
  }
</App>, document.getElementById('root'))

props校验

  • 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装着需要什么样的数据
// props校验
// 错误演示
const App = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</1i>)
  return <ul>{lis}</ul>
}
ReactDOM.render(<App colors={19} />, document.getElementById('root'))
  • 如果传入的数据不对,可能会导致报错
  • 关键问题:组件的使用者不知道需要传递什么样的数据
  • props校验:允许在创建组件的时候,指定props的类型、格式等
  • 作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
App.propTypes = {
	colors: Proptypes.array
}

使用步骤

  • 安装包 prop-types (yarn add prop-types | npm i props-types)
  • 导入prop-types 包
  • 使用组件名.propTypes={} 来给组件的props添加校验规则
  • 校验规则通过PropTypes对象来指定
import PropTypes from 'prop-types'
function App (props) {
	return (
		<h1>Hi, {props. colors}</h1>
	)
}
App.propTypes = {
	// 约定colers属性为array类型
	// 如果类型不对,则报出明确错误,便于分析错误原因
	colors: PropTypes.array
}

约束规则

  • 常见类型:array、bool、func、number、object、string
  • React元素类型:element
  • 必填项:isRequired
  • 特定结构的对象:shape({ })
// 常见类型
optionalFunc: PropTypes.func,
// 必选
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape ({
	color: PropTypes.string,
	fontSize: PropTypes.number
})

约束规则大全

props的默认值

场景:分页组件→每页显示条数
作用:给props设置默认值,在未传入props时生效

// props默认值

const App = props => {
  console.log(props)
  return (
    <div>
      <h1>此处展示props的默认值: {props.pageSize}</h1>
    </div>
  )
}
// 添加props默认值
App.defaultProps = {
  pageSize: 10
}
// <App pageSize={20} />传值后生效
ReactDOM.render(<App pageSize={20} />, document.getElementById('root'))

组件的生命周期

意义:组件的生命周期有助于理解组件的运行方式、完成更复杂的组件功能、分析组件错误原因等;
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程。
生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数。
钩子函数的作用:为开发人员在不同阶段操作组件提供了时机。
只有类组件才有生命周期。

生命周期三个阶段

  • 每个阶段的执行时机
  • 每个阶段钩子函数的执行顺序
  • 每个阶段钩子函数的作用

创建时(挂载阶段)

在这里插入图片描述
执行时机:创建组件时(页面加载时)
执行顺序:在这里插入图片描述

class App extends React.Component {
	render() {
		return (
			<div>
				<h1>统计豆豆被打的次数: </h1>
				<button id="btn">打豆豆</button>
			</div>
		)
	}
}
ReactDOM.render(<App />, document.getElementById('root')
钩子函数触发时机作用
constructor创建组件时,最先执行1、初始化state;2、为事件处理程序绑定this
render每次组件渲染都会触发渲染UI(注意:不能调用setState()
componentDidMount组件挂载(完成DOM渲染)后1、发送ajax网络请求,获取远程数据;2、DOM操作
class App extends React.Component {
  constructor(props) {
    super(props)
    // 初始化state
    this.state = {
      count: 0
    }
    // 处理this指向程序

    console.warn('生命周期钩子函数:constructor')
  }
  componentDidMount() {
    const title = document.getElementById('title')
    console.log(title);
    console.warn('生命周期钩子函数:componentDidMount')
  }
  render() {
    // 错误演示, 不要在render中调用setState方法
    /* this.setState({
      count: 1
    }) */
    console.warn('生命周期钩子函数:render')
    return (
      <div>
        <h1 id='title'>统计豆豆被打的次数: </h1>
        <button id="btn" >打豆豆</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

更新时(更新阶段)

执行时机:1、seState();2、forceUpdate();3、组件接收到新的props
说明:以上三者任意一种变化,组件就会重新渲染
执行顺序
在这里插入图片描述

钩子函数触发时机作用
render每次组件渲染都会触发渲染UI(注意:不能调用setState()
componentDidUpdate组件更新,完成DOM后1、发送ajax网络请求,获取远程数据;2、DOM操作(注意:如果要setSate(),必须放在if条件中)
// 更新时
class App extends React.Component {
  constructor(props) {
    super(props)
    // 初始化state
    this.state = {
      count: 0
    }

    // 处理this指向程序
  }
  // 打豆豆
  handleClick = () => {
    this.setState({
      count: this.state.count + 1
    })
    // 演示强制更新
    // this.forceUpdate()
  }
  render() {
    console.warn('生命周期钩子函数:render')
    return (
      <div>
        <Counter count={this.state.count} />
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    )
  }
}
class Counter extends React.Component {
  render() {
    console.warn('--子组件--生命周期钩子函数:render')
    return (
      <div>
        <h1 id='title'>统计豆豆被打的次数: {this.props.count}</h1>
      </div>
    )
  }
  // 注意:如果要`setSate()`,必须放在if条件中)
  // 因为:如果直接调用setState()更新状态,也会导致递归更新
  componentDidUpdate(prevProps) {
    console.warn('--子组件--生命周期钩子函数:componentDidUpdate')
    console.log('上一次的props:', prevProps.count, ',当前的props', this.props.count);
    // 做法: 比较更新前后的props是否相同,来决定是否重新渲染组件
    if (prevProps.count !== this.props.count) {
      this.setState({
        // count: this.props.count
        // 发送ajax请求
      })
    }
    // 获取DOM
    const title = document.getElementById('title')
    console.log(title);
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

卸载时

执行时机:组件从页面中消失时

钩子函数触发时机作用
componentWillUnmount组件卸载,从页面中消失执行清理工作(比如清理定时器等)
// 卸载时
class App extends React.Component {
  constructor(props) {
    super(props)
    // 初始化state
    this.state = {
      count: 0
    }

    // 处理this指向程序
  }
  // 打豆豆
  handleClick = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        {
          this.state.count > 3 ? <p>豆豆被打死了~</p> : <Counter count={this.state.count} />
        }
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    )
  }
}
class Counter extends React.Component {
  componentDidMount() {
    // 开启定时器
    this.timerId = setInterval(() => {
      console.log("定时器正在执行~");
    }, 500)
  }
  render() {
    return (
      <div>
        <h1 id='title'>统计豆豆被打的次数: {this.props.count}</h1>
      </div>
    )
  }

  componentWillUnmount() {
    console.warn('--子组件--生命周期钩子函数:componentWillUnmount')
    clearInterval(this.timerId);// 清理定时器
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

不常用的生命周期

在这里插入图片描述

getDerivedStateFromProps()

  • getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容
  • 不管原因是什么,都会在每次渲染前触发此方法

shouldComponentUpdate()

  • 根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染
  • 当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true

getSnapshotBeforeUpdate()

  • getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()
  • 此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等

render-props和高阶组件

处理方式:如果两个组件中的部分功能相似或相同,复用相似的功能(联想函数封装)
复用:1、state;2、操作state的方法(组件状态逻辑)

  • 两种方式:
    • render-props
    • 高阶组件(HOC)
      注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)

render props模式

如何拿到该组件中复用的state:

  • 在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现)

如何渲染任意的UI

  • 使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)
<Mouse render= { (mouse) => {}/>

使用步骤

  • 创建Mouse组件 ,在组件中提供复用的状态逻辑代码( 1.状态;2. 操作状态的方法)
  • 将要复用的状态作为props.render(state)方法的参数,暴露到组件外部
  • 使用props.render()的返回值作为要渲染的内容
// 创建mouse
class Mouse extends React.Component {
  state = {
    x: 0,
    y: 0
  }
  // 鼠标移动事件的事件处理程序
  handleMouseMove = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  // 监听鼠标移动事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  render() {
    return this.props.render(this.state)
  }
}
class App extends React.Component {
  render() {
    return (
      <div >
        <h1 > render props模式 </h1>
        <Mouse render={(mouse) => {
          return (<p>鼠标位置:{mouse.x} {mouse.y}</p>)
        }} />
      </div>
    )
  }
}

ReactDOM.render(< App />, document.getElementById('root'))

演示Mouse组件复用

Mouse组件负责:封装复用的状态逻辑代码( 1.状态;2.操作状态的方法)
状态:鼠标坐标(xy)
操作状态的方法:鼠标移动事件
传入的render prop负责:使用复用的状态来渲染UI结构

class Mouse extends React.Component {
  state = {
    x: 0,
    y: 0
  }
  // 鼠标移动事件的事件处理程序
  handleMouseMove = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  // 监听鼠标移动事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  render() {
    return this.props.render(this.state)
  }
}

<Mouse render={ (mouse) => <p>鼠标当前位置{mouse.x} ,{mouse.y}</p>}/>

children代替render模式

注意:并不是该模式叫render props就必须使用名为render的prop,实际上可以使用任意名称的prop;
把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式;
推荐:使用children代替render属性

<Mouse >
	{({x, y}) => <p>鼠标的位置是{x},{y}</p> }
</Mouse>
// 组件内部:
this.props.children(this.state)

代码优化

推荐:给render props模式添加props校验;

Mouse.propTypes = {
chidlren: PropTypes.func.isRequired
}

应该在组件卸载时解除mousemove事件绑定;

componentWillUnmount() {
	window.removeEventListener ('mousemove',this.handleMouseMove)
}

高阶组件

目的:实现状态逻辑复用;
高阶组件就相当于手机壳,通过包装组件,增强组件功能;
高阶组件(HOC,Higher-Order Component) 是一个函数,接收要包装的组件,返回增强后的组件;

const EnhancedComponent = withHOC(WrappedComponent)

高阶组件内部创建了一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件WrappedComponent

//高阶组件内部创建的类组件:
class Mouse extends React.Component {
	render(){
		return <WrappedComponent {...this.state} />
	}
}

使用步骤

  1. 创建一个函数,名称约定以with开头
function withMouse(){}
  1. 指定函数参数,参数应该以大写字母开头
function withMouse(WrappedComponent){}
  1. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
function withMouse(WrappedComponent){
	class Mouse extends React.Component {}
	return Mouse
}
  1. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
return <WrappedComponent {...this.state} />
  1. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面
// 创建组件
const MousePosition = withMouse(Position)
// 渲染组件
<MousePosition />
  1. 流程
function withMouse(WrappedComponent) {
  // 该组件提供复用的逻辑
  class Mouse extends React.Component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    // 鼠标移动事件的事件处理程序
    handleMouseMove = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    // 
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    render() {
      return <WrappedComponent {...this.state}></WrappedComponent>
    }
  }
  return Mouse
}

//用来测试高阶组件
const Position = props => (
  <p>
    鼠标当前位置: (x: {props.x}, y: {props.y})
  </p>
)

const MousePosition = withMouse(Position)

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染组件 */}
        < MousePosition></MousePosition>
      </div>
    )
  }
}
ReactDOM.render(< App />, document.getElementById('root'))

设置displayName

使用高阶组件存在的问题:得到两个组件的名称相同
原因:默认情况下,React使用组件名称作为displayName
解决方式:为高阶组件设置displayName,便于调试时区分不同的组件
displayName的作用:用于设置调试信息(React Developer Tools信息)
设置方式:

Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) {
	return WrappedComponent.displayName || WrappedComponent.name || 'Component' 
}

传递props

问题:如果没有传递props,会导致props丢失问题
解决方式: 渲染WrappedComponent时,将statethis.props一起传递给组件
传递方式:

<WrappedComponent {...this.state} {...this.props} />

React原理

setState()的说明

更新数据

setState()异步更新数据的;
注意:使用该语法时,后面的setState()不要依赖于前面的setState()
可以多次调用setState(),只会触发一次重新渲染。

class App extends React.Component {
  state = { count: 1 }
  // 异步更新数据
  handleClick = () => {
    this.setState({
      count: this.state.count + 1
    })
    console.log(this.state.count) // 打印出来是1
  }
  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}
ReactDOM.render(< App />, document.getElementById('root'))

推荐语法

推荐:使用setState((state, props)=>{})
参数state:表示最新的state;
参数props:表示最新的props;

class App extends React.Component {
  state = { count: 1 }
  // 异步更新数据
  handleClick = () => {
    // 这种语法也是异步更新
    this.setState((state, props) => {
      return {
        count: state.count + 1
      }
    })
    console.log(this.state.count)
  }
  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>

      </div>
    )
  }
}
ReactDOM.render(< App />, document.getElementById('root'))

第二个参数

场景:在状态更新(页面完成重新渲染)后立即执行某个操作
语法:setState(updater[, callback])

this.setstate(
	(state, props)=>{} ,
	()=>{ 
		console.log ('这个回调函数会在状态更新后立即执行')
	}
)

案例:

class App extends React.Component {
  state = { count: 1 }
  // 异步更新数据
  handleClick = () => {
    // 这种语法也是异步更新
    this.setState(
      (state, props) => {
        return {
          count: state.count + 1
        }
      },
      () => {
        console.log('状态更新完成:', this.state.count);
        console.log(document.getElementById('title').innerText);
        document.title = '更新后的title为:' + this.state.count
      }
    )
    console.log(this.state.count) // 打印出来是1
  }
  render() {
    return (
      <div>
        <h1 id='title'>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>

      </div>
    )
  }
}
ReactDOM.render(< App />, document.getElementById('root'))

JSX语法的转换过程

JSX仅仅是createElement()方法的语法糖(简化语法)
JSX语法被@babel/preset-react插件编译为createElement()方法
React元素:是一个对象,用来描述你希望在屏幕上看到的内容

JSX语法

const element =(
	<h1 className="greeting">
		Hello JSX!
	</h1>
)

createElement()

const element = React.createElement(
	"h1",
	{
		className: "greeting"
	} ,
	'Hello JSX!'
);

React元素

// 注意:这是简化过的结构
const element = {
	type: 'h1' ,
	props: {
		className: 'greeting',
		children: 'Hello JSX!'
	}
}

组件更新机制

  • setState() 的两个作用:

    • 修改state
    • 更新组件UI

过程:父组件重新渲染时,也会重新渲染子组件,但只会渲染当前组件子树(当前组件以其所有子组件)

// 根组件
class App extends React.Component {
  state = {
    color: '#369'
  }

  getColor() {
    return Math.floor(Math.random() * 256)
  }

  changeBG = () => {
    this.setState(() => {
      return {
        color: `rgb(${this.getColor()}, ${this.getColor()}, ${this.getColor()})`
      }
    })
  }

  render() {
    console.log('根组件')
    return (
      <div className="app" style={{ backgroundColor: this.state.color }}>
        <button onClick={this.changeBG}>根组件 - 切换颜色状态</button>
        <div className="app-wrapper">
          <Parent1 />
          <Parent2 />
        </div>
      </div>
    )
  }
}

// ------------------------左侧---------------------------

class Parent1 extends React.Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState(state => ({ count: state.count + 1 }))
  }
  render() {
    console.log('左侧父组件')
    return (
      <div className="parent">
        <h2>
          左侧 - 父组件1
          <button onClick={this.handleClick}>点我({this.state.count}</button>
        </h2>
        <div className="parent-wrapper">
          <Child1 />
          <Child2 />
        </div>
      </div>
    )
  }
}

class Child1 extends React.Component {
  render() {
    console.log('左侧子组件 - 1')
    return <div className="child">子组件1-1</div>
  }
}
class Child2 extends React.Component {
  render() {
    console.log('左侧子组件 - 2')
    return <div className="child">子组件1-2</div>
  }
}

// ------------------------右侧---------------------------

class Parent2 extends React.Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState(state => ({ count: state.count + 1 }))
  }

  render() {
    console.log('右侧父组件')
    return (
      <div className="parent">
        <h2>
          右侧 - 父组件2
          <button onClick={this.handleClick}>点我({this.state.count}</button>
        </h2>
        <div className="parent-wrapper">
          <Child3 />
          <Child4 />
        </div>
      </div>
    )
  }
}

class Child3 extends React.Component {
  render() {
    console.log('右侧子组件 - 1')
    return <div className="child">子组件2-1</div>
  }
}
class Child4 extends React.Component {
  render() {
    console.log('右侧子组件 - 2')
    return <div className="child">子组件2-2 </div>
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

组件性能优化

减轻state

只存储跟组件渲染相关的数据(比如:count / 列表数据 / loading等)
注意:不用做渲染的数据不要放在state中
对于这种需要在多个方法中用到的数据,应该放到this中

class Hello extends Component {
	componentDidMount() {
		// timerId存储到this中,而不是state中
		this.timerId = setInterval(() => {}, 2000)
	}
	componentWillUnmount() {
		clearInterval(this.timerId)
	}
	render() {}
}

避免不必要的重新渲染

  • 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
  • 问题:子组件没有任何变化时也会重新渲染
  • 如果避免不必要的重新渲染?
    • 解决方式:使用钩子函数 shouldComponentUpdate(nextProps, nextState)
      • 在这个函数中,nextProps和nextState是最新的状态以及属性
  • 作用:这个函数有返回值,如果返回true,代表需要重新渲染,如果返回false,代表不需要重新渲染
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate -> render)
class Hello extends Component {
	shouldComponentUpdate() {
		// 根据条件,决定是否重新渲染组件
		return false
	}
	render() {}
}

随机数案例:利用nextState参数来判断当前组件是否需要更新

// 生成随机数
class App extends React.Component {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: Math.floor(Math.random() * 3)
      }
    })
  }

  // 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染
  shouldComponentUpdate(nextProps, nextState) {
    console.log('最新状态:', nextState, ', 当前状态:', this.state)

    return nextState.number !== this.state.number

    // if (nextState.number !== this.state.number) {
    //   return true
    // }
    // return false

    // if (nextState.number === this.state.number) {
    //   return false
    // }
    // return true
  }

  render() {
    console.log('render')
    return (
      <div>
        <h1>随机数:{this.state.number}</h1>
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

随机数案例:利用props参数来判断是否需要进行更新

class App extends React.Component {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: Math.floor(Math.random() * 3)
      }
    })
  }

  // 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染
  // shouldComponentUpdate(nextProps, nextState) {
  //   console.log('最新状态:', nextState, ', 当前状态:', this.state)
  //   return nextState.number !== this.state.number
  // }

  render() {
    // console.log('render')
    return (
      <div>
        <NumberBox number={this.state.number} />
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
  }
}

class NumberBox extends React.Component {
  shouldComponentUpdate(nextProps) {
    console.log('最新props:', nextProps, ', 当前props:', this.props)
    // 如果前后两次的number值相同,就返回false,不更新组件
    return nextProps.number !== this.props.number

    // if (nextProps.number === this.props.number) {
    //   return false
    // }
    // return true
  }
  render() {
    console.log('子组件中的render')
    return <h1>随机数:{this.props.number}</h1>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

纯组件

作用及使用

纯组件:PureComponent React.Component 功能相似
区别:PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较
原理:纯组件内部通过分别 对比 前后两次 propsstate 的值,来决定是否重新渲染组件

class Hello extends React.PureComponent {
	render() {
		return (
			<div>纯组件</div>
		)
	}
}

实现原理

说明:纯组件内部的对比是 shallow compare(浅层对比)
对于值类型来说:比较两个值是否相同(直接赋值即可,没有坑)

let number = 0
let newNumber = number
newNumber = 2
console.log(number === newNumber) // false
state = { number: 0 }
setState({
	number: Math.floor(Math.random() * 3)
})
// PureComponent内部对比:
最新的state.number === 上一次的state.number // false,重新渲染组件

说明:纯组件内部的对比是 shallow compare(浅层对比)
对于引用类型来说:只比较对象的引用(地址)是否相同

const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true
state = { obj: { number: 0 } }
// 错误做法
state.obj.number = 2
setState({ obj: state.obj })
// PureComponent内部比较:
最新的state.obj === 上一次的state.obj // true,不重新渲染组件

注意:state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据!(示例)

// 正确!创建新数据
const newObj = {...state.obj, number: 2}
setState({ obj: newObj })
// 正确!创建新数据
// 不要用数组的push / unshift 等直接修改当前数组的的方法
// 而应该用 concat 或 slice 等这些返回新数组的方法
this.setState({
	list: [...this.state.list, {新数据}]
})

虚拟DOM和Diff算法

React 更新视图的思想是:只要 state 变化就重新渲染视图
特点:思路非常清晰
问题:组件中只有一个 DOM 元素需要更新时,也得把整个组件的内容重新渲染到页面中?理想状态:部分更新,只更新变化的地方。

虚拟 DOM

本质上就是一个 JS 对象,用来描述你希望在屏幕上看到的内容(UI)
在这里插入图片描述

Diff算法

执行过程

  1. 初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。
  2. 根据虚拟 DOM 生成真正的 DOM,渲染到页面中。
  3. 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。
  4. 与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),得到需要更新的内容。
  5. 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。

在这里插入图片描述

/* 
  虚拟DOM 和 Diff算法
*/
class App extends React.PureComponent {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: Math.floor(Math.random() * 2)
      }
    })
  }
  // render方法调用并不意味着浏览器中的重新渲染!!!
  // render方法调用仅仅说明要进行diff
  render() {
    const el = (
      <div>
        <h1>随机数:</h1>
        <p>{this.state.number}</p>
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
    console.log(el)
    return el
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

React路由

React路由介绍

现代的前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。

  • 前端路由功能:让用户从一个视图(页面)导航到另一个视图(页面)
  • 前端路由是一套映射规则,在React中,是URL路径与组件的对应关系
  • 使用React路由简单来说,就是配置路径和组件

路由的基本使用

  • 安装: yarn add react-router-dom
    • 如果没有安装yarn工具的,需要先全局安装一下yarn:npm install -g yarn
  • 导入路由的三个核心组件: Router / Route / Link
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
  • 使用Router 组件包裹整个应用
<Router>
	<div className="App">
		// … 省略页面内容
	</div>
</Router>
  • 使用Link组件作为导航菜单(路由入口)
<Link to="/first">页面一</Link>
  • 使用Route组件配置路由规则和要展示的组件(路由出口)
const First = () => <p>页面一的页面内容</p>
<Router>
	<div className="App">
		<Link to="/first">页面一</Link>
		<Route path="/first" component={First}></Route>
	</div>
</Router>

常用组件说明

  • Router组件:包裹整个应用,一个React应用只需要使用一次
    • 两种常用的Router: HashRouterBrowserRouter
    • HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
    • 推荐 BrowserRouter:使用H5的history API实现(localhost3000/first)
/* 
  react-router-dom 的基本使用:
  1 安装: yarn add react-router-dom
*/
// 2 导入组件:
// import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import { HashRouter as Router, Route, Link } from 'react-router-dom'
const First = () => <p>页面一的内容</p>

// 3 使用Router组件包裹整个应用
const App = () => (
  <Router>
    <div>
      <h1>React路由基础</h1>
      {/* 4 指定路由入口 */}
      <Link to="/first">页面一</Link>

      {/* 5 指定路由出口 */}
      <Route path="/first" component={First} />
    </div>
  </Router>
)
ReactDOM.render(<App />, document.getElementById('root'))
  • Link组件:用于指定导航链接(a标签)
    • 最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
// to属性:浏览器地址栏中的pathname(location.pathname)
<Link to="/first">页面一</Link>
  • Route组件:指定路由展示组件相关信息
    • path属性:路由规则,这里需要跟Link组件里面to属性的值一致
    • component属性:展示的组件
    • Route写在哪,渲染出来的组件就在哪
<Route path="/first" component={First}></Route>

路由执行过程

  • 当我们点击Link组件的时候,修改了浏览器地址栏中的url
  • React路由监听地址栏url的变化
  • React路由内部遍历所有的Route组件,拿着Route里面path规则与pathname进行匹配
    在这里插入图片描述
  • 当路由规则(path)能够匹配地址栏中的pathname时,就展示该Route组件的内容
/* 
  路由的执行过程
*/
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
const First = () => <p>页面一的内容</p>
const Home = () => <h2>这是Home组件的内容</h2>
// 使用Router组件包裹整个应用
const App = () => (
  <Router>
    <div>
      <h1>React路由基础</h1>
      <div>
        {/* 指定路由出口 */}
        <Route path="/first" component={First} />
        <Route path="/home" component={Home} />
      </div>
      {/* 指定路由入口 */}
      <Link to="/first">页面一</Link>
      <br />
      <Link to="/home">首页</Link>
    </div>
  </Router>
)
ReactDOM.render(<App />, document.getElementById('root'))

编程式导航

  • 场景:点击登陆按钮,登陆成功后,通过代码跳转到后台首页,如何实现?
  • 编程式导航:通过JS代码来实现页面跳转
  • history是React路由提供的,用于获取浏览器历史记录的相关信息
  • push(path):跳转到某个页面,参数path表示要跳转的路径
  • go(n):前进或后退功能,参数n表示前进或后退页面数量
class Login extends Component {
	handleLogin = () => {
		// ...
		this.props.history.push('/home')
	}
	render() {...省略其他代码}
}

功能演示

/* 
  编程式导航
*/
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
class Login extends React.Component {
  handleLogin = () => {
    // 使用编程式导航实现路由跳转
    // ...省略其他功能代码
    this.props.history.push('/home')
  }
  render() {
    return (
      <div>
        <p>登录页面:</p>
        <button onClick={this.handleLogin}>登录</button>
      </div>
    )
  }
}
const Home = props => {
  const handleBack = () => {
    // go(-1) 表示返回上一个页面
    props.history.go(-1)
  }
  return (
    <div>
      <h2>我是后台首页</h2>
      <button onClick={handleBack}>返回登录页面按钮</button>
    </div>
  )
}
const App = () => (
  <Router>
    <div>
      <h1>编程式导航:</h1>
      <Link to="/login">去登录页面</Link>

      <Route path="/login" component={Login} />
      <Route path="/home" component={Home} />
    </div>
  </Router>
)
ReactDOM.render(<App />, document.getElementById('root'))

默认路由

  • 现在的路由都是通过点击导航菜单后展示的,如果进入页面的时候就主动触发路由呢
  • 默认路由:表示进入页面时就会匹配的路由
  • 默认路由:只需要把path设置为 /
<Route path="/" component={Home} />

匹配模式

模糊匹配模式

  • 当Link组件的to属性值为 ‘/login’ 时候,为什么默认路由也被匹配成功?
  • 默认情况下,React路由是模糊匹配模式
  • 模糊匹配规则:只要pathname以path开头就会匹配成功
<Link to="/login">登录页面</Link>
<Route path="/" component={Home} /> 匹配成功
  • path 代表Route组件的path属性
  • pathname 代表Link组件的to属性(也就是 location.pathname)
path能够匹配的pathname
/所有 pathname
/first/first 或 /first/a 或 /first/a/b/…
/* 
  模糊匹配模式
*/
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'

const Home = () => <p>进入页面的时候,你能看到我吗?</p>
const Login = () => <p>我是Login组件的内容</p>

const App = () => (
  <Router>
    <div>
      <h1>默认路由</h1>
      <Link to="/first/a/b/c">登录页面</Link>

      {/* 默认路由 */}
      <Route path="/" component={Home} />
      <Route path="/first" component={Login} />
    </div>
  </Router>
)

ReactDOM.render(<App />, document.getElementById('root'))

精准匹配

  • 默认路由认可情况下都会展示,如果避免这种问题?
  • 给Route组件添加exact属性,让其变为精准匹配模式
  • 精确匹配:只有当pathpathname完全匹配时才会展示改路由
// 此时,该组件只能匹配 pathname=“/” 这一种情况
<Route exact path="/" component=... />

推荐:给默认路由添加 exact 属性

/* 
  精准匹配模式
*/

import { BrowserRouter as Router, Route, Link } from 'react-router-dom'

const Home = () => <p>进入页面的时候,你能看到我吗?</p>
const Login = () => <p>我是Login组件的内容</p>

const App = () => (
  <Router>
    <div>
      <h1>默认路由</h1>
      <ul>
        <li>
          <Link to="/">首页</Link>
        </li>
        <li>
          <Link to="/login">登录页面</Link>
        </li>
      </ul>

      {/* 默认路由,添加 exact 属性,就会让当前路由变为精确匹配 */}
      <Route exact path="/" component={Home} />
      <Route path="/login" component={Login} />
    </div>
  </Router>
)

ReactDOM.render(<App />, document.getElementById('root'))

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

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

相关文章

代理Windows台式机支持Remote Desktop外网远程桌面连接, 随时玩转Stable Diffusion WebUI

title: 《树莓派4B家庭服务器搭建指南》第十八期&#xff1a;代理Windows台式机支持Remote Desktop外网远程桌面连接, 随时玩转Stable Diffusion WebUI zhaoolee在家中Windows台式机折腾Stable Diffusion WebUI , 为了出门在外也能访问Windows台式机的Stable Diffusion WebUI&…

dom4j解析 mybatis config XML文件

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

软件工程——第4章形式化说明技术(非重点)

本专栏是博主个人笔记&#xff0c;主要目的是利用碎片化的时间来记忆软工知识点&#xff0c;特此声明&#xff01; 文章目录 1.软件工程方法划分成哪三类&#xff1f;并各自举例 2.形式化方法的定义&#xff1f; 3.非形式化的方法的缺点&#xff1f; 4.形式化方法的优点&am…

elasticsearch snapshot快照指定多个索引并行备份——筑梦之路

Curl 命令方式对elasticsearch备份和恢复—— 筑梦之路_筑梦之路的博客-CSDN博客 之前也写过使用API请求的方式对ES数据进行快照方式备份&#xff0c;这里主要对之前的内容进行完善和补充。 版本兼容性 快照包含构成索引的磁盘上数据结构的副本。这意味着快照只能还原为可以读…

Kubernetes API Server源码学习(二):OpenAPI、API Resource的装载、HTTP Server具体是怎么跑起来的?

本文基于Kubernetes v1.22.4版本进行源码学习 6、OpenAPI 1&#xff09;、OpenAPI的作用 OpenAPI是由Swagger发展而来的一个规范&#xff0c;一种形式化描述Restful Service的语言&#xff0c;便于使用者理解和使用一个Service。通过OpenAPI规范可以描述一个服务&#xff1a;…

28离散Hopfield神经网络的联想记忆数字识别(附matlab)

1.简述 学习目标&#xff1a;利用离散Hopfield神经网络进行联想记忆数字识别 1982年&#xff0c;美国加州理工学院的J.Hopfield教授提出了一种单层反馈神经网络&#xff0c;称为Hopfield网络[1]。Hopfield网络是一种循环的神经网络&#xff0c;从输出到输入有反馈连接。Hopfiel…

Leetcode---350周赛

题目列表 6901. 总行驶距离 6890. 找出分区值 6893. 特别的排列 6447. 给墙壁刷油漆 一、总行驶距离 很显然&#xff0c;这题单纯就是一道数学应用题&#xff0c;我们要明白最关键的一点 &#xff1a;只有当mainTank>5并且additionalTank>0时&#xff0c;才能发生副油…

操作系统 - 操作系统结构

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

【Kubernetes资源篇】StatefulSet无状态服务管理入门实战详解

文章目录 一、StatefulSet理论知识1、StatefulSet Pod控制器特性2、什么是有状态服务和无状态服务&#xff1f;3、Deployment和StatefulSet区别 二、案例&#xff1a;StatefulSet资源实战演示1、创建WEB站点并验证StatefulSet特点2、StatefulSet滚动更新 三、总结 一、Stateful…

JavaFX第四篇 Button按钮和事件处理

JavaFX第四篇 Button按钮和事件处理 1. 代码2. 讲解3. 代码仓库 上一篇我们讲解了Hello World演示&#xff0c;主要用到Label标签的功能&#xff0c; 这里我们要介绍的是最常用的控件之一&#xff1a;按钮 在现在的软件开发过程中还没发现没有用到按钮的应用&#xff0c; 基本上…

【数据库原理与实践】第八章至第十章作业汇总(更新中)

填空题部分&#xff1a; Chp 8 安全性与完整性 part 1&#xff1a; 数据库的安全性是指保护数据库以防止不合法的使用所造成的&#xff08; 数据泄露、更改或破坏 &#xff09;。计算机系统有三类安全性问题&#xff0c;即&#xff08; 技术安全 &#xff09;、管理安全和…

ROS:计算图

目录 一、ROS计算图简介二、节点&#xff08;Node&#xff09;三、节点管理器&#xff08;Master&#xff09;四、消息&#xff08;Message&#xff09;五、话题&#xff08;Topic&#xff09;六、服务&#xff08;Service&#xff09;七、动作&#xff08;Action&#xff09;八…

LabVIEW开发燃油阀自动性能测试系统

LabVIEW开发燃油阀自动性能测试系统 燃油阀是航空燃油控制系统的核心部件。燃油阀的流量滞后直接影响控制精度、稳定性和可靠性&#xff0c;而燃油阀生产的性能测试是至关重要的步骤。但是&#xff0c;由于流动滞后是非线性的&#xff0c;因此很难控制和进行实时测试。随着厂家…

CSS3-浮动

&#xff08;了解&#xff09;标准流&#xff1a;又称文档流&#xff0c;是浏览器在渲染显示网页内容时默认采用的一套排版规则&#xff0c;规定了应该以何种方式排列元素 常见标准流排版规则&#xff1a; 1. 块级元素&#xff1a;从上往下&#xff0c;垂直布局&am…

Matplotlib---柱形图

1. 绘制简单的柱状图 bar函数用于绘制柱状图。下面是bar函数的语法格式&#xff1a; bar(x, height, width0.8, bottomNone, aligncenter, dataNone, **kwargs)参数解释&#xff1a; x&#xff1a;指定每个柱子的x轴坐标。height&#xff1a;指定每个柱子的高度。width&…

TypeScript ~ 掌握基本类型 ②

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; TypeScript ~ TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &…

vue的学习

title: VUE 一、Vue简介 1.1 简介 ::: tip Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式的js框架&#xff0c;发布于 2014 年 2 月。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0…

F. Gardening Friends(树的直径)

Problem - 1822F - Codeforces 两位朋友Alisa和Yuki在他们的花园里种了一棵有n个顶点的树。树是一个无向图&#xff0c;没有循环、回路或多重边。这棵树中的每条边都有一个长度为k。最初&#xff0c;顶点1是树的根。 Alisa和Yuki种植这棵树不仅仅是为了好玩&#xff0c;而是想…

基于SpringBoot的流浪动物救助平台

1.引言 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于流浪动物救助平台所牵扯的管理及数据保存都是非常多的&#xff0c;例如首页、个人中心、会员管理、志愿者管理、流浪动物信息管理、领养信息管理、取消领养信息管理、志愿团队活动管理、志愿者申请表管理、…

【性能设计篇】数据库拓展

前两篇文章介绍了分布式存储的机制&#xff0c;为保证数据的高性能以及可拓展&#xff0c;采用分片/分区机制。为保证数据的高可用性&#xff0c;采用复制/镜像机制存储数据。一份数据存储多份。而这两种方式在数据中&#xff0c;就是分片/分区机制。分库分表/读写分离。 读写…