【react全家桶】react-Hook (下)

news2024/11/14 23:47:23

本人大二学生一枚,热爱前端,欢迎来交流学习哦,一起来学习吧。
<专栏推荐>
🔥:js专栏

🔥:vue专栏

🔥:react专栏

文章目录

  • 15【react-Hook (下)】
    • 1.React.memo
      • 1.1 基本介绍
      • 1.2 问题的引出
      • 1.3 使用React.memo
      • 1.4 使用注意
      • 1.5 容易出错的情况
    • 2.useCallback
      • 2.1 基本介绍
      • 2.2 解决1.5遗留的问题
      • 2.3 第二个参数的使用
    • 3.useMemo
      • 3.1 问题的引出
      • 3.2 使用 useMemo 解决上面的问题
      • 3.3 第二个参数的使用
    • 4.React.forwardRef
    • 5.useImperativeHandle

15【react-Hook (下)】

1.React.memo

1.1 基本介绍

这是一个高阶组件,用来做性能优化的,这个本来应该是写在React高级指引中的,但是这个案例会和后面的useCallback联合起来,所以就写在这里了

  • React.memo() 是一个高阶组件,如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。
    • 它接收另一个组件作为参数,并且会返回一个包装过的新组件
    • 包装过的新组件就会具有缓存功能,这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。
    • 包装过后,只有组件的props发生变化,才会触发组件的重新的渲染,否则总是返回缓存中结果。如果函数组件被 React.memo 包裹,且其实现中拥有 useStateuseReduceruseContext 的 Hook,当 state 或 context 发生变化时,它仍会重新渲染。

1.2 问题的引出

React组件会在两种情况下发生重新渲染。第一种,当组件自身的state发生变化时。第二种,当组件的父组件重新渲染时。第一种情况下的重新渲染无可厚非,state都变了,组件自然应该重新进行渲染。但是第二种情况似乎并不是总那么的必要。

App.jsx

import React, { useState } from 'react'

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)

  const clickHandler = () => {
    setCount(prevState => prevState + 1)
  }

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
      <A />
    </div>
  )
}

function A() {
  console.log('A渲染')
  return <div>我是A组件</div>
}

在点击增加后,我们发现AppA都重新渲染了。

当APP组件重新渲染时,A组件也会重新渲染。A组件中没有state,甚至连props都没有设置。换言之,A组件无论如何渲染,每次渲染的结果都是相同的,虽然重渲染并不会应用到真实DOM上,但很显然这种渲染是完全没有必要的。

image-20221030172720453

为了减少像A组件这样组件的渲染,React为我们提供了一个方法React.memo()。该方法是一个高阶函数,可以用来根据组件的props对组件进行缓存,当一个组件的父组件发生重新渲染,而子组件的props没有发生变化时,它会直接将缓存中的组件渲染结果返回而不是再次触发子组件的重新渲染,这样一来就大大的降低了子组件重新渲染的次数。

1.3 使用React.memo

使用React.memo包裹A组件

这里只是为了演示方便,把所有组件写一个文件,就用这种方式包裹A组件,平时单文件组件的时候我们这样使用,export default React.memo(A)

import React, { useState } from 'react'

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)

  const clickHandler = () => {
    setCount(prevState => prevState + 1)
  }

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
      <A />
    </div>
  )
}

const A = React.memo(() => {
  console.log('A渲染')
  return <div>我是A组件</div>
})

修改后的代码中,并没有直接使用A组件,而是在A组件外层套了一层函数React.memo(),这样一来,返回的A组件就增加了缓存功能,只有当A组件的props属性发生变化时,才会触发组件的重新渲染。memo只会根据props判断是否需要重新渲染,和state和context无关,state或context发生变化时,组件依然会正常的进行重新渲染

在点击增加后,我们发现只有App重新渲染了。

image-20221030173239606

这时我们改下代码

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)

  const clickHandler = () => {
    setCount(prevState => prevState + 1)
  }
	
  // 增加
  const test = count % 4 === 0

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
  	  {/* 改动 */}
      <A test={test} />
    </div>
  )
}

const A = React.memo(props => {
  console.log('A渲染')
  return (
    <div>
      我是A组件
      {/* 增加 */}
      <p>{props.test && 'props.test 为 true'}</p>
    </div>
  )
})

这次加了个表达式的结果传给A组件,一开始是false,只有为true的时候,A组件才会重新渲染

这时界面是这样的

image-20221030174105525

点击3次后,表达式为true,A组件的props发生改变,所以重新渲染了。

image-20221030173754653

1.4 使用注意

  1. 此方法仅作为**性能优化**的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 bug。
  2. 与 class 组件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。

1.5 容易出错的情况

先回到这个案例的初始代码,在这之上进行修改

我们把App组件clickHandler方法传递给A组件,让A组件也能够改变App组件state

import React, { useState } from 'react'

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)

  const clickHandler = () => {
    setCount(prevState => prevState + 1)
  }

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
      <A clickHandler={clickHandler} />
    </div>
  )
}

const A = React.memo(props => {
  console.log('A渲染')
  return (
    <div>
      我是A组件
      <button onClick={props.clickHandler}>A组件的增加</button>
    </div>
  )
})

点击A组件的增加,发现A组件也重新渲染了

image-20221030175830062

这是因为App组件重新渲染的时候,clickHandler也重新创建了,这时传递给子组件的clickHandler和上一次不一样,所以react.memo失效了。

这个问题可以用useCallback解决。

2.useCallback

2.1 基本介绍

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

useCallbackuseMemo设计的初衷是用来做性能优化的。在Class Component中考虑以下的场景:

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={() => this.handleClick()}>Click Me</Button>;
  }
}

传给 Button 的 onClick 方法每次都是重新创建的,这会导致每次 Foo render 的时候,Button 也跟着 render。优化方法有 2 种,箭头函数和 bind。下面以 bind 为例子:

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={this.handleClick}>Click Me</Button>;
  }
}

同样的,Function Component也有这个问题:

function Foo() {
  const [count, setCount] = useState(0);

  const handleClick() {
    console.log(`Click happened with dependency: ${count}`)
  }
  return <Button onClick={handleClick}>Click Me</Button>;
}

而 React 给出的方案是useCallback Hook。在依赖不变的情况下 (在我们的例子中是 count ),它会返回相同的引用,避免子组件进行无意义的重复渲染

2.2 解决1.5遗留的问题

/*
*   useCallback()
*		这个hook会缓存方法的引用
*       参数:
*           1. 回调函数
*           2. 依赖数组
*               - 当依赖数组中的变量发生变化时,回调函数才会重新创建
*               - 如果不指定依赖数组,回调函数每次都会重新创建
*               - 一定要将回调函数中使用到的所有变量都设置到依赖数组中
*                   除了(setState)
* */

我们将clickHandler方法改造一下

  const clickHandler = useCallback(() => {
    setCount(prevState => prevState + 1)
  }, [])

第二个参数一定要加,不然和平常写没有区别

依赖项[]的意思是只有第一次渲染时才会创建,之后都不会重新创建了

点击A组件的增加,发现只有App组件重新渲染了。因为clickHandler没有重新创建,传给子组件的没有变化,所以子组件这次没有重新渲染。

image-20221030180349406

完整代码

import React, { useState, useCallback } from 'react'

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)

  const clickHandler = useCallback(() => {
    setCount(prevState => prevState + 1)
  }, [])

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
      <A clickHandler={clickHandler} />
    </div>
  )
}

const A = React.memo(props => {
  console.log('A渲染')
  return (
    <div>
      我是A组件
      <button onClick={props.clickHandler}>A组件的增加</button>
    </div>
  )
})

2.3 第二个参数的使用

继续改造上面的代码

import React, { useState, useCallback } from 'react'

export default function App() {
  console.log('App渲染')

  const [count, setCount] = useState(1)
  // 增加
  const [num, setNum] = useState(1)

  const clickHandler = useCallback(() => {
    setCount(prevState => prevState + num)
  // 增加
    setNum(prevState => prevState + 1)
  }, [])

  return (
    <div>
      <h2>App -- {count}</h2>
      <button onClick={clickHandler}>增加</button>
      <A clickHandler={clickHandler} />
    </div>
  )
}

const A = React.memo(props => {
  console.log('A渲染')
  return (
    <div>
      我是A组件
      <button onClick={props.clickHandler}>A组件的增加</button>
    </div>
  )
})

增加了一个num,让每一次count的增加比上次多1,现在这样写是有问题的。

image-20221030181249832

点击了两次增加后,预期值应该是4,但是显示的是3,是为什么呢?

因为clickHandler只在初次渲染的时候创建,当时num的值是1,这个函数一直没有重新创建,内部用的num一直是1

这时我们可以加一个依赖项

const clickHandler = useCallback(() => {
    setCount(prevState => prevState + num)
    setNum(prevState => prevState + 1)
  }, [num])

这样num变化了,这个函数也会重新创建。

image-20221030181534667

点击了两次增加后,count变成了预期值4。

3.useMemo

useMemo和useCallback十分相似,useCallback用来缓存函数对象,useMemo用来缓存函数的执行结果。在组件中,会有一些函数具有十分的复杂的逻辑,执行速度比较慢。闭了避免这些执行速度慢的函数返回执行,可以通过useMemo来缓存它们的执行结果,像是这样:

const result = useMemo(()=>{
    return 复杂逻辑函数();
},[依赖项])

useMemo中的函数会在依赖项发生变化时执行,注意!是执行,这点和useCallback不同,useCallback是创建。执行后返回执行结果,如果依赖项不发生变化,则一直会返回上次的结果,不会再执行函数。这样一来就避免复杂逻辑的重复执行。

3.1 问题的引出

App.jsx

import React, { useMemo, useState } from 'react'

const App = () => {
  const [count, setCount] = useState(1)

  let a = 123
  let b = 456

  function sum(a, b) {
    console.log('sum执行了')
    return a + b
  }
    
  return (
    <div>
      <h1>App</h1>
      <p>sum的结果:{sum(a, b)}</p>
      <h3>{count}</h3>
      <button onClick={() => setCount(prevState => prevState + 1)}>点我</button>
    </div>
  )
}

export default App

这是一个计数器案例,但是多添加了一个函数展示结果,这种情况这个函数只需要在一开始调用一次就够了,但是count的改变会导致重新渲染模板,这样sum函数也会反复执行。

image-20221107143139632

现在这个sum函数太简单了,体现不出性能上的问题,我们可以把sum中的逻辑改复杂一点。

import React, { useMemo, useState } from 'react'

const App = () => {
  const [count, setCount] = useState(1)

  let a = 123
  let b = 456

  function sum(a, b) {
    console.log('sum执行了')
    const begin = +new Date()
    while (true) {
      if (Date.now() - begin > 3000) break
    }
    return a + b
  }
  return (
    <div>
      <h1>App</h1>
      <p>sum的结果:{sum(a, b)}</p>
      <h3>{count}</h3>
      <button onClick={() => setCount(prevState => prevState + 1)}>点我</button>
    </div>
  )
}

export default App

增加了一个功能,让这个函数起码3秒才能执行完。

image-20221107143451560

这个时候因为sum函数要3秒才能执行完,导致下面数字显示也变慢了3秒。

3.2 使用 useMemo 解决上面的问题

App.jsx

改写模板中的sum方法的调用

<p>sum的结果:{useMemo(() => sum(a, b), [])}</p>

image-20221107143946116

第一次加载慢是不可避免的,但是这个钩子函数将sum函数的返回值缓存起来,这样我们模板重新渲染时就没有再去执行sum函数,而是直接使用上一次的返回值。

3.3 第二个参数的使用

继续改造上面的代码,把Sum单独抽离成一个组件

Sum.jsx

import React from 'react'

export default function Sum(props) {
  console.log('Sum执行了')
  return <span>{props.a + props.b}</span>
}

App.jsx

添加了一个功能可以变换a的值

import React, { useMemo, useState } from 'react'
import Sum from './Sum'

const App = () => {
  const [count, setCount] = useState(1)

  let a = 123
  let b = 456

  if (count % 2 === 0) a = a + 1

  const result = useMemo(() => <Sum a={a} b={b} />, [])

  return (
    <div>
      <h1>App</h1>
      <p>sum的结果:{result}</p>
      <h3>{count}</h3>
      <button onClick={() => setCount(prevState => prevState + 1)}>点我</button>
    </div>
  )
}

export default App

现在有一个问题,如果Sum组件接收的值变化了,网页上显示的还是原来的缓存值,这个时候就要利用第二个参数。

image-20221107145159066

App.jsx

const result = useMemo(() => <Sum a={a} b={b} />, [a])

这里的意思和以前是一样的,如果a的值变化了,将会重新计算。

image-20221107145403725

4.React.forwardRef

这是一个高阶组件,用来做性能优化的,这个本来应该是写在React高级指引中的,但是这个案例会和后面的useImperativeHandle联合起来,所以就写在这里了

React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:

  • 转发 refs 到 DOM 组件
  • 在高阶组件中转发 refs

React.forwardRef 接受渲染函数作为参数。React 将使用 propsref 作为参数来调用此函数。此函数应返回 React 节点。

import React, { useRef } from 'react'

const Child = React.forwardRef((props, ref) => {
  return (
    <>
      <h2>这是Child组件</h2>
      <input type="text" ref={ref} />
    </>
  )
})

export default function App() {
  const childRef = useRef(null)
  console.log(childRef)
  return (
    <div>
      <h2>这是App组件</h2>
      <Child ref={childRef} />
    </div>
  )
}

在上述的示例中,React 会将 <Child ref={childRef}> 元素的 ref 作为第二个参数传递给 React.forwardRef 函数中的渲染函数。该渲染函数会将 ref 传递给 <input ref={ref}> 元素。

因此,当 React 附加了 ref 属性之后,ref.current 将直接指向 <input> DOM 元素实例。

image-20221107150428406

我们改造App组件

export default function App() {
  const childRef = useRef(null)

  childRef.current.value = 'App组件设置的'

  return (
    <div>
      <h2>这是App组件</h2>
      <Child ref={childRef} />
    </div>
  )
}

image-20221107150535398

我们可以直接在App组件操作Child组件的内容,但是这样并不好,我们希望Child组件的内容只由Child组件自己去操作,所以引出了useImperativeHandle

5.useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用:

App.jsx

import React, { useRef, useEffect, useImperativeHandle } from 'react'

const Child = React.forwardRef((props, ref) => {
  const inputRef = useRef(null)

  const changeInputValue = value => (inputRef.current.value = value)

  // useImperativeHandle 可以用来指定ref返回的值
  useImperativeHandle(ref, () => ({
    changeInputValue,
  }))

  return (
    <>
      <h2>这是Child组件</h2>
      <input type="text" ref={inputRef} />
    </>
  )
})

export default function App() {
  const childRef = useRef(null)

  useEffect(() => {
    console.log(childRef)
  }, [])

  return (
    <div>
      <h2>这是App组件</h2>
      <button onClick={() => childRef.current.changeInputValue('App组件修改的')}>点击改变</button>
      <Child ref={childRef} />
    </div>
  )
}

我们来看看childRef的输出是什么

image-20221107151926890

可以发现我们把子组件的changeInputValue暴露出去了。

image-20221107152122394

点击按钮发现也是可以正常使用的。

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

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

相关文章

进程控制(Linux)

进程控制 fork 在Linux中&#xff0c;fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 返回值&#xff1a; 在子进程中返回0&#xff0c;父进程中返回子进程的PID&#xff0c;子进程创建失败返回-1。 …

Spring - BeanFactory与ApplicationContext介绍

文章目录 Spring Bean一、BeanFactory 快速入门1.1 BeanFactory 开发步骤1.2 DI依赖注入 二、ApplicationContext快速入门2.1 入门2.2 BeanFactory 与 ApplicationContext关系2.3 BeanFactory 继承体系2.4 ApplicationContext 继承体系 Spring Bean 之前也了解过Spring Bean&a…

高斯过程回归 | Matlab实现高斯过程回归多输入单输出预测(Gaussian Process Regression)

文章目录 效果一览文章概述研究内容程序设计参考资料效果一览 文章概述 高斯过程回归 | Matlab实现高斯过程回归多输入单输出预测(Gaussian Process Regression) 研究内容 高斯过程回归(Gaussian Process Regression,GPR)是一种基于概率模型的非参数回归方法,可以用于

mybatisplus数据权限插件学习初探 动态表名更换插件

文章目录 学习链接 mybatisplus数据权限插件学习初探前言案例建表用户表订单表 环境准备UserUserMapperUserMapper.xmlOrdersOrdersMapperOrdersMapper.xml 配置UserTypeEnumUserContextHolderCustomizeDataPermissionHandlerMybatisPlusConfig 测试测试类bossdeptManagerclerk…

Zinx框架学习 - 消息封装

Zinx - V0.5 消息封装 之前我们使用Request来保存服务器的数据&#xff0c;很显然使用[]byte来接收数据&#xff0c;没有长度也没有消息类型&#xff0c;接下来就要针对这个消息进行封装 创建消息类型 定义一个基本的message包&#xff0c;会包含消息ID、数据、数据长度三个…

路径规划算法:基于探路者优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于探路者优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于探路者优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法…

涉及float和double

文章目录 涉及float和double的问题&#xff1a;它们的存储方式&#xff1a;有效位&#xff1f; 链式结构 涉及float和double的问题&#xff1a; 它们的存储方式&#xff1a; 它们会分成小数部分和指数部分分别存储。小数部分的有效位数越多&#xff0c;精度就越高&#xff0c;…

NLP超详细新手快速入门上手篇(1)常用函数

前言 自然语言处理(NLP)是机器学习的应用之一&#xff0c;用于分析、理解和生成自然语言&#xff0c;以便人类与计算机&#xff0c;人类与人类更好的交流。自然语言处理按照任务类型可以分为分类、匹配、翻译、结构化预测、与序贯决策过程这五类。 本篇参考自TensorFlow官方文…

MyBatis 查询数据库

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 MyBatis 是什么&#xff1f;第⼀个MyBatis查询创建数据库和表添加MyBatis框架支持设置 MyBatis 配置信息添加业务代码 查询操…

【VBA】实现批量生成二维码

系列文章 【C#】单号生成器&#xff08;编号规则、固定字符、流水号、产生业务单号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成器&#xff08;开始日期、结束日期&#xff09; 本文链接&#xff1a;h…

Nginx - ​一个高性能、灵活可靠的开源Web服务器

Nginx是什么&#xff1f; Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;第一个公开版本0.1…

idea使用native-image打包springboot项目

native-image简介 native-image 是一个用于将 Java 程序编译为本地可执行文件的工具。它是 GraalVM 的一部分&#xff0c;GraalVM 是一个高性能的通用虚拟机&#xff0c;支持多种语言。 使用步骤 下载GraalVM 安装 GraalVM&#xff1a;首先&#xff0c;你需要安装 GraalVM。…

20230603-周六随笔

周六闲来无事&#xff0c;给新电脑装下开发环境&#xff0c;记录一下遇到的问题 git下载代码报错 报错1&#xff1a;schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)解决方法&#xff1a;执行git config --system http.sslbackend openssl命令 报错2&#xff1a;SSL certifica…

【Java 8 新特性】获取对象列表中的某个属性组成的列表

文章目录 获取对象列表中的某个属性组成的列表1、用法示例2、详细案例 附录&#xff1a;Java 8 Stream 基本用法1、map2、filter3、forEach4、limit5、sorted6、并行&#xff08;parallel&#xff09;程序7、Collectors8、统计 获取对象列表中的某个属性组成的列表 1、用法示例…

高完整性系统工程(十一):Fault Tolerant Design

目录 1. INTRODUCTION TO FAULT TOLERANCE 1.2 Definitions 1.3 Two Kinds of Faults 1.4 Hardware vs Software Faults 1.4.1 Failure Curve for Hardware 1.4.2 Hardware and Software Failures 1.5 Causes of Failures 1.6 3 Ways to Class Failures 1.6.1 Tempora…

【LLM】大模型值得探索的十个研究方向

note 基础理论&#xff1a;大模型的基础理论是什么&#xff1f; 网络架构&#xff1a;Transformer是终极框架吗&#xff1f; 高效计算&#xff1a;如何使大模型更加高效&#xff1f; 高效适配&#xff1a;大模型如何适配到下游任务&#xff1f; 可控生成&#xff1a;如何实…

ChatGPT有关的模块知多少?

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 ChatGPT有关的模块知多少&#xff1f; &#x1f9ca;摘要&#x1f9ca;ChatGPT 开发库清单 &#x1f9ca;摘要 本文介绍了基于OpenAI ChatGPT 的API 开发的python 模块库。【原创&am…

STM32cubemx定时外部模式测量10M以上频率

STM32cubemx定时外部模式测量10M以上频率 本文讲解利用定时器的外部时钟功能&#xff0c;巧妙测量高频外部信号频率。范围可以到高达30M以上。 所需工具&#xff1a; 开发板:STM32F103RCT6STM32CubeMXIDE: Keil-MDK 文章目录 STM32cubemx定时外部模式测量10M以上频率原理讲解…

【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(Optional篇)

Guava相关的介绍 Guava工程包含了许多被Google的Java项目广泛依赖的核心库。我们希望通过本文档为Guava中最流行和最强大的功能提供更具可读性和解释性的说明。 本教程是中级教程&#xff0c;适合 Guava 中级开发者的进阶学习。 学习Guava前的准备工作 学习目标和计划&#xf…

Python学习笔记 - 探索集合Set

尊敬的各位&#xff0c;我是Mr.数据杨&#xff0c;非常高兴和你们一起走进Python的世界。今天&#xff0c;让我们一起探讨Python中神奇的“集合”。你们是否读过《三国演义》&#xff1f;你们应该记得那场赫赫有名的“桃园三结义”吧&#xff1f;那让我们以此为例&#xff0c;来…