【React系列】Hook(二)高级使用
本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)
一. Hook高级使用
1.1. useReducer
很多人看到useReducer
的第一反应应该是redux
的某个替代品,其实并不是。
useReducer
仅仅是useState
的一种替代方案:
- 在某些场景下,如果
state
的处理逻辑比较复杂,我们可以通过useReducer
来对其进行拆分; - 或者这次修改的
state
需要依赖之前的state
时,也可以使用;
单独创建一个reducer/counter.js
文件:
export function counterReducer(state, action) {switch(action.type) {case "increment":return {...state, counter: state.counter + 1}case "decrement":return {...state, counter: state.counter - 1}default:return state;}
}
home.js
:
import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'export default function Home() {const [state, dispatch] = useReducer(counterReducer, {counter: 100});return (<div><h2>当前计数: {state.counter}</h2><button onClick={e => dispatch({type: "increment"})}>+1</button><button onClick={e => dispatch({type: "decrement"})}>-1</button></div>)
}
我们来看一下,如果我们创建另外一个profile.js
也使用这个reducer
函数,是否会进行数据的共享:
import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'export default function Profile() {const [state, dispatch] = useReducer(counterReducer, {counter: 0});return (<div><h2>当前计数: {state.counter}</h2><button onClick={e => dispatch({type: "increment"})}>+1</button><button onClick={e => dispatch({type: "decrement"})}>-1</button></div>)
}
数据是不会共享的,它们只是使用了相同的counterReducer
的函数而已。
所以,useReducer
只是useState
的一种替代品,并不能替代Redux
。
1.2. useCallback
useCallback
实际的目的是为了进行性能的优化。
如何进行性能的优化呢?
useCallback
会返回一个函数的memoized
(记忆的) 值;- 在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const memoizedCallback = useCallback(() => {doSomething(a, b);},[a, b]
);
我们来看下面一段很有趣的代码:
import React, { memo, useState, useCallback } from 'react'export default function CallbackHookDemo() {const [count, setCount] = useState(0);const increment1 = useCallback(function increment() {setCount(count + 1);}, []);const increment2 = function() {setCount(count + 1);}return (<div><h2>当前计数: {count}</h2><button onClick={increment1}>+1</button><button onClick={increment2}>+1</button></div>)
}
increment1
在每次函数组件重新渲染时,会返回相同的值;increment2
每次定义的都是不同的值;
问题:是否increment1
会比increment2
更加节省性能呢?
- 事实上,经过一些测试,并没有更加节省内存,因为
useCallback
中还是会传入一个函数作为参数; - 所以并不存在
increment2
每次创建新的函数,而increment1
不需要创建新的函数这种性能优化;
那么,为什么说useCallback
是为了进行性能优化呢?
我们来对上面的代码进行改进:
import React, { memo, useState, useCallback } from 'react';const CounterIncrement = memo((props) => {console.log("CounterIncrment被渲染:", props.name);return <button onClick={props.increment}>+1</button>
})export default function CallbackHookDemo() {const [count, setCount] = useState(0);const increment1 = useCallback(function increment() {setCount(count + 1);}, []);const increment2 = function() {setCount(count + 1);}return (<div><h2>当前计数: {count}</h2>{/* <button onClick={increment1}>+1</button><button onClick={increment2}>+1</button> */}<CounterIncrement increment={increment1} name="increment1"/><CounterIncrement increment={increment2} name="increment2"/></div>)
}
- 在上面的代码中,我们将回调函数传递给了子组件,在子组件中会进行调用;
- 在发生点击时,我们会发现接受
increment1
的子组件不会重新渲染,但是接受increment2
的子组件会重新渲染; - 所以
useCallback
最主要用于性能渲染的地方应该是和memo
结合起来,决定子组件是否需要重新渲染;
1.3. useMemo
useMemo
实际的目的也是为了进行性能的优化。
如何进行性能的优化呢?
useMemo
返回的也是一个memoized
(记忆的) 值;- 在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
我们来看一个案例:
import React, { useState, useMemo } from 'react';function calcNum(count) {let total = 0;for (let i = 0; i < count; i++) {total += i;}console.log("计算一遍");return total
}export default function MemoHookDemo() {const [count, setCount] = useState(10);const [isLogin, setIsLogin] = useState(true);const total = calcNum(count);return (<div><h2>数字和: {total}</h2><button onClick={e => setCount(count + 1)}>+1</button>{isLogin && <h2>Coderwhy</h2>}<button onClick={e => setIsLogin(!isLogin)}>切换</button></div>)
}
- 无论我们点击了是
+1
还是切换
案例都会重新计算一次; - 事实上,我们只是希望在
count
发生变化时重新计算;
这个时候,我们可以使用useMemo
来进行性能的优化:
import React, { useState, useMemo } from 'react';function calcNum(count) {let total = 0;for (let i = 0; i < count; i++) {total += i;}console.log("计算一遍");return total
}export default function MemoHookDemo() {const [count, setCount] = useState(10);const [isLogin, setIsLogin] = useState(true);const total = useMemo(() => {return calcNum(count);}, [count]);return (<div><h2>数字和: {total}</h2><button onClick={e => setCount(count + 1)}>+1</button>{isLogin && <h2>Coderwhy</h2>}<button onClick={e => setIsLogin(!isLogin)}>切换</button></div>)
}
当然,useMemo
也可以用于子组件的性能优化:
import React, { useState, useMemo, memo } from 'react';function calcNum(count) {let total = 0;for (let i = 0; i < count; i++) {total += i;}console.log("计算一遍");return total
}const ShowCounter = memo((props) => {console.log("重新渲染");return <h1>Counter: {props.total}</h1>
})const ShowInfo = memo((props) => {console.log("ShowInfo重新渲染");return <h1>信息: {props.info.name}</h1>
})export default function MemoHookDemo() {const [count, setCount] = useState(10);const [isLogin, setIsLogin] = useState(true);const total = useMemo(() => {return calcNum(count);}, [count]);const info = useMemo(() => {return {name: "why"}}, [])return (<div><h2>数字和: {total}</h2><ShowCounter total={total} /><ShowInfo info={info}/><button onClick={e => setCount(count + 1)}>+1</button>{isLogin && <h2>Coderwhy</h2>}<button onClick={e => setIsLogin(!isLogin)}>切换</button></div>)
}
ShowCounter
子组件依赖的是一个基本数据类型,所以在比较的时候只要值不变,那么就不会重新渲染;ShowInfo
接收的是一个对象,每次都会定义一个新的对象,所以我们需要通过useMemo
来对其进行优化;
1.4. useRef
useRef
返回一个ref
对象,返回的ref
对象在组件的整个生命周期保持不变。
最常用的ref
是两种用法:
- 用法一:引入DOM(或者组件,但是需要是
class
组件)元素; - 用法二:保存一个数据,这个对象在整个生命周期中可以保存不变;
用法一:引用DOM
import React, { useRef } from 'react';export default function RefHookDemo() {const inputRef = useRef();const titleRef = useRef();const handleOperating = () => {titleRef.current.innerHTML = "我是coderwhy";inputRef.current.focus();}return (<div><input type="text" ref={inputRef}/><h2 ref={titleRef}>默认内容</h2><button onClick={e => handleOperating()}>操作</button></div>)
}
用法二:使用ref
保存上一次的某一个值
useRef
可以想象成在ref
对象中保存了一个.current
的可变盒子;useRef
在组件重新渲染时,返回的依然是之前的ref
对象,但是current
是可以修改的;
import React, { useState, useEffect, useRef } from 'react';let preValue = 0;export default function RefHookDemo02() {const [count, setCount] = useState(0);const countRef = useRef(count);useEffect(() => {countRef.current = count;}, [count]);return (<div><h2>前一次的值: {countRef.current}</h2><h2>这一次的值: {count}</h2><button onClick={e => setCount(count + 1)}>+1</button></div>)
}
1.5. useImperativeHandle
useImperativeHandle
并不是特别好理解,我们一点点来学习。
我们先来回顾一下ref
和forwardRef
结合使用:
- 通过
forwardRef
可以将ref
转发到子组件; - 子组件拿到父组件中创建的
ref
,绑定到自己的某一个元素中;
import React, { useRef, forwardRef } from 'react';const HYInput = forwardRef(function (props, ref) {return <input type="text" ref={ref}/>
})export default function ForwardDemo() {const inputRef = useRef();return (<div><HYInput ref={inputRef}/><button onClick={e => inputRef.current.focus()}>聚焦</button></div>)
}
上面的做法本身没有什么问题,但是我们是将子组件的DOM直接暴露给了父组件:
- 直接暴露给父组件带来的问题是某些情况的不可控;
- 父组件可以拿到DOM后进行任意的操作;
但是,事实上在上面的案例中,我们只是希望父组件可以操作的focus
,其他并不希望它随意操作;
通过useImperativeHandle
可以只暴露固定的操作:
- 通过
useImperativeHandle
的Hook,将传入的ref
和useImperativeHandle
第二个参数返回的对象绑定到了一起; - 所以在父组件中,使用
inputRef.current
时,实际上使用的是返回的对象; - 比如我调用了
focus
函数,甚至可以调用printHello
函数;
import React, { useRef, forwardRef, useImperativeHandle } from 'react';const HYInput = forwardRef(function (props, ref) {// 创建组件内部的refconst inputRef = useRef();useImperativeHandle(ref, () => ({focus: () => {inputRef.current.focus();},printHello: () => {console.log("Hello World")}}))// 这里绑定的是组件内部的inputRefreturn <input type="text" ref={inputRef}/>
})export default function ImperativeHandleHookForwardDemo() {const inputRef = useRef();return (<div><HYInput ref={inputRef}/><button onClick={e => inputRef.current.focus()}>聚焦</button><button onClick={e => inputRef.current.printHello()}>Hello World</button></div>)
}
1.6. useLayoutEffect
useLayoutEffect
看起来和useEffect
非常的相似,事实上他们也只有一点区别而已:
useEffect
会在渲染的内容更新到DOM上之后执行,不会阻塞DOM的更新;useLayoutEffect
会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;
如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect
。
我们来看下面的一段代码:
import React, { useEffect, useState, useLayoutEffect } from 'react';export default function EffectHookDemo() {const [count, setCount] = useState(0);useEffect(() => {if (count === 0) {setCount(Math.random()*200)}}, [count]);return (<div><h2>当前数字: {count}</h2><button onClick={e => setCount(0)}>随机数</button></div>)
}
- 这段代码在开发中会发生闪烁的现象;
- 因为我们先将
count
设置为了0
,那么DOM会被更新,并且会执行一次useEffect
中的回调函数; - 在
useEffect
中我们发现count
为0
,又执行一次setCount
操作,那么DOM会再次被更新,并且useEffect
又会被执行一次;
事实上,我们上面的操作的目的是在count
被设置为0
时,随机另外一个数字:
- 如果我们使用
useLayoutEffect
,那么会等到useLayoutEffect
代码执行完毕后,再进行DOM的更新;
import React, { useEffect, useState, useLayoutEffect } from 'react';export default function EffectHookDemo() {const [count, setCount] = useState(0);useLayoutEffect(() => {if (count === 0) {setCount(Math.random()*200)}}, [count]);return (<div><h2>当前数字: {count}</h2><button onClick={e => setCount(0)}>随机数</button></div>)
}
二. 自定义Hook
2.1. 认识自定义hook
自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。
需求:所有的组件在创建和销毁时都进行打印
- 组件被创建:打印
组件被创建了
; - 组件被销毁:打印
组件被销毁了
;
export default function CustomHookDemo() {useEffect(() => {console.log("组件被创建了");return () => {console.log("组件被销毁了");}}, [])return (<div><h2>CustomHookDemo</h2></div>)
}
但是这样来做意味着所有的组件都需要有对应的逻辑:
function Home(props) {useEffect(() => {console.log("组件被创建了");return () => {console.log("组件被销毁了");}}, [])return <h2>Home</h2>
}function Profile(props) {useEffect(() => {console.log("组件被创建了");return () => {console.log("组件被销毁了");}}, [])return <h2>Profile</h2>
}
如何可以对它们的逻辑进行抽取呢?
- 我们可能希望抽取到一个函数中;
function loggingLife() {useEffect(() => {console.log("组件被创建了");return () => {console.log("组件被销毁了");}}, [])
}
但是,抽取到这里调用之后,代码是报错的:
- 原因是普通的函数中不能使用hook
那么,我们应该如何操作呢?
- 非常简单,函数以特殊的方式命名,以
use
开头即可;
function useLoggingLife() {useEffect(() => {console.log("组件被创建了");return () => {console.log("组件被销毁了");}}, [])
}
当然,自定义Hook可以有参数,也可以有返回值:
function useLoggingLife(name) {useEffect(() => {console.log(`${name}组件被创建了`);return () => {console.log(`${name}组件被销毁了`);}}, [])
}
2.2. 自定义Hook练习
我们通过一些案例来练习一下自定义Hook。
使用User
、Token
的Context
比如多个组件都需要使用User
和Token
的Context
:
import React, { useContext } from 'react'
import { UserContext, TokenContext } from '../App'export default function CustomHookContextDemo() {const user = useContext(UserContext);const token = useContext(TokenContext);console.log(user, token);return (<div><h2>CustomHookContextDemo</h2></div>)
}
- 这段代码我们在每次使用
user
和token
时都需要导入对应的Context
,并且需要使用两次useContext
;
我们可以抽取到一个自定义Hook中:
function useUserToken() {const user = useContext(UserContext);const token = useContext(TokenContext);return [user, token];
}
获取窗口滚动的位置
- 在开发中,某些场景我们可能总是希望获取创建滚动的位置:
import React, { useEffect, useState } from 'react'export default function CustomScrollPositionHook() {const [scrollPosition, setScrollPosition] = useState(0);useEffect(() => {const handleScroll = () => {setScrollPosition(window.scrollY);}document.addEventListener("scroll", handleScroll);return () => {document.removeEventListener("scroll", handleScroll);}}, [])return (<div style={{padding: "1000px 0"}}><h2 style={{position: "fixed", top: 0, left: 0}}>CustomScrollPositionHook: {scrollPosition}</h2></div>)
}
但是如果每一个组件都有对应这样的一个逻辑,那么就会存在很多的冗余代码:
function useScrollPosition() {const [scrollPosition, setScrollPosition] = useState(0);useEffect(() => {const handleScroll = () => {setScrollPosition(window.scrollY);}document.addEventListener("scroll", handleScroll);return () => {document.removeEventListener("scroll", handleScroll);}}, [])return scrollPosition;
}
数据存储的localStorage
在开发中,我们会有一些数据希望通过localStorage
进行存储(当然,你可以根据自己的情况选择sessionStorage
)
import React, { useState, useEffect } from 'react'export default function CustomDataStoreHook() {const [name, setName] = useState(() => {return JSON.parse(window.localStorage.getItem("name"))});useEffect(() => {window.localStorage.setItem("name", JSON.stringify(name));}, [name])return (<div><h2>CustomDataStoreHook: {name}</h2><button onClick={e => setName("coderwhy")}>设置name</button></div>)
}
如果每一个里面都有这样的逻辑,那么代码就会变得非常冗余:
function useLocalStorange(key) {const [data, setData] = useState(() => {return JSON.parse(window.localStorage.getItem(key))});useEffect(() => {window.localStorage.setItem(key, JSON.stringify(data));}, [data]);return [data, setData];
}
三. Hook原理分析
这里我主要分析一下useState的原理,因为本次教程是穿插讲解源码的,所以不会所有源码一一讲解。
3.1. useState代码位置
useState
还是从React中导入的,所以我们可以先查看:
点到useState
的源码中:
useState
本质上,是使用的dispatcher
的useState
;
dispatcher
来自另外的一个函数resolveDispatcher
:
运行的过程中,会赋值一个current
的值是一个Dispatcher
类型:
Dispatcher
来自于react-reconciler/src/ReactFiberHooks
Dispatch
类型的定义:
这里的Dispatch
仅仅是一个类型而已,我们赋值具体的值在不同的阶段是不同的:
- 在挂载阶段:
HooksDispatcherOnMount
- 在更新阶段:
HooksDispatcherOnUpdate
挂载哪一个取决于renderWithHook
函数:
3.2. HooksDispatcherOnMount
HooksDispatcherOnMount
对应的useState
是mountState
mountState
的源码:
绑定的dispatchAction
函数,事实上是将所有的action
放到了queue
的队列中:
3.3. HooksDispatcherOnUpdate
HooksDispatcherOnUpdate
对应的useState
是updateState
updateState
本质上会执行updateReducer
:
- 所有其实就更新阶段而言,
useState
本质上用的是updateReducer
updateReducer
的源码如下:
四. Redux Hooks
在之前的redux
开发中,为了让组件和redux
结合起来,我们使用了react-redux
中的connect
:
- 但是这种方式必须使用高阶函数结合返回的高阶组件;
- 并且必须编写:
mapStateToProps
和mapDispatchToProps
映射的函数;
在Redux7.1开始,提供了Hook的方式,我们再也不需要编写connect
以及对应的映射函数了
4.1. useSelector使用
useSelector
的作用是将state
映射到组件中:
- 参数一:将
state
映射到需要的数据中; - 参数二:可以进行比较来决定是否组件重新渲染;(后续讲解)
const result: any = useSelector(selector: Function, equalityFn?: Function)
现在,我可以改进一下之前的Profile
中使用redux的代码:
function Profile(props) {const {banners, recommends, counter} = useSelector(state => ({banners: state.homeInfo.banners,recommends: state.homeInfo.recommends}));console.log("Profile重新渲染");return (<div><h2>数字: {counter}</h2><h1>Banners</h1><ul>{banners.map((item, index) => {return <li key={item.acm}>{item.title}</li>})}</ul><h1>Recommends</h1><ul>{recommends.map((item, index) => {return <li key={item.acm}>{item.title}</li>})}</ul></div>)
}
但是这段代码会有一个问题:
- 当前我们的组件并不依赖
counter
,但是counter
发生改变时,依然会引起Profile
的重新渲染;
原因是什么呢?
useSelector
默认会比较我们返回的两个对象是否相等;- 如何比较呢?
const refEquality = (a, b) => a === b
; - 也就是我们必须返回两个完全相等的对象才可以不引起重新渲染;
这个时候,我们可以使用react-redux中给我们提供的 shallowEqual
:
const {banners, recommends, counter} = useSelector(state => ({banners: state.homeInfo.banners,recommends: state.homeInfo.recommends}), shallowEqual);
- 这段代码的作用是避免不必要的重新渲染;
当然,你也可以编写自己的比较函数,来决定是否重新渲染。
4.2. useDispatch
useDispatch
非常简单,就是直接获取dispatch
函数,之后在组件中直接使用即可:
const dispatch = useDispatch()
直接使用dispatch
:
<button onClick={e => dispatch(subAction(1))}>-1</button>
<button onClick={e => dispatch(subAction(5))}>-5</button>
我们还可以通过useStore
来获取当前的store
对象:
const store = useStore()
在组件中可以使用store
:
const store = useStore();
console.log(store.getState());
相关文章:

【React系列】Hook(二)高级使用
本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. Hook高级使用 1.1. useReducer 很多人看到useReducer的第一反应应该是redux的某个替代品,其实并不是…...
编程笔记 html5cssjs 018 HTML颜色
编程笔记 html5&css&js 018 HTML颜色 一、HTML 颜色二、HTML中设置颜色值三、颜色名称和颜色值 颜色是视觉中重要因素,尤其是处理人机界面中,更是要处理颜色设置和搭配。在网页中,提供了设置颜色的一些方案,需要我们认真学…...

C++_继承
介绍 继承的基本概念 1.共性和个性 (PS:有相同的属性 但是 又有自己的特点) 基类和子类 1. 基类(父类) 共性 2. 子类(派生类) 个性(特点) 继承语法 1.class 子类名:继承方式1 基类1,继承方式2 基类2{ 行为 };继承方式(PS:默认继承方式为:私有继承) 1.公有继承: public 2.保护…...

Java-IO流-15
文件操作 文件创建 package com.edu.file;import org.junit.jupiter.api.Test;import java.io.File; import java.io.IOException;public class Demo01 {public static void main(String[] args) {}Test//方式1public void create01(){String filePath "D:\\new1.txt&q…...

java中使用redis
1、redis数据类型 1.1、5种数据类型 redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:字符串 string、哈希 hash、列表 list、集合 set、有序集合 sorted set / zset。 字符串(string):普通字符…...
Mongodb的可重试读操作
目录 重试读操作 需要条件 启用重读 支持可重试读的操作 不支持可重试读的操作 行为 重试读操作 连接mongodb进行读操作时,遇到网络或数据库集群的主节点切换导致的数据读问题。mongodb驱动自动尝试重新执行读操作。 需要条件 数据库连接驱动支持mongodb4.2…...
2024年1月2日-1月7日(ue5底层渲染+ue arpg+项目需求)
按照月计划,按照每小时分四段进行,arpg一例ue5底层渲染0.1小时arpg一例项目需求的相关视频教程一段 周二: 18:30- 19:30(1小时)ue arpg (88-89) ue5底层渲染03A14(6:08)…...
MySQL中的视图和触发器
SQL 视图 1 ) 概述 在mysql中,视图是一个非真实存在的虚拟表其本质是,根据sql语句获取动态的数据集,并为其命名用户使用时只需使用名称,即可获取结果集,并可以将其当做表来使用 2 )用法示例 2.1 比较麻烦…...
uView-UI v2.x常见问题整理
为了更好的给大家提供 uView UI 的技术支持,uView UI 团队整理常见问题文档,大家可以阅读查找常见的问题解决办法。 uView 2.x 文档 https://www.uviewui.com uView 1.x 文档 https://v1.uviewui.com uView UI uni-app 主页 DCloud 插件市场 uVie…...

MBTI职业性格测试 28题(免费版)
MBTI职业性格测试概述 MBTI是现在国际上最为流行的测试工具,利用MBTI职业性格测试,可以清楚地找到自己的性格特点以及兴趣爱好,方便于对职业进行规划、以及改善人际关系。其主要应用心理学常识对个性做出判断,提炼出动力、信息收…...

Springcloud 微服务实战笔记 Ribbon
使用 Configurationpublic class CustomConfiguration {BeanLoadBalanced // 开启负载均衡能力public RestTemplate restTemplate() {return new RestTemplate();}}可看到使用Ribbon,非常简单,只需将LoadBalanced注解加在RestTemplate的Bean上࿰…...

CSS基础笔记-04cascade-specificity-inheritance
CSS基础笔记系列 《CSS基础笔记-01CSS概述》《CSS基础笔记-02动画》CSS基础笔记-03选择器 前言 Cascading Style Sheets,关键就在于这个cascading,对于这个术语理解,感觉对于我这种CSS新手有点儿不太friendly。本文记录下我对这个术语的理…...

Spring应用的部署与管理
一、前言 部署是将开发好的应用发布到服务器上,使其能够被用户访问的关键步骤。Spring框架提供了灵活的部署选项,本文将介绍Spring应用的常见部署方式和一些建议,帮助开发者顺利将应用投放到生产环境。 二、传统部署方式:WAR包 传…...

B端产品经理学习-需求挖掘
B端产品需求挖掘 目录 识别和管理干系人 决策人和负责人需求挖掘 针对用户进行需求挖掘 用户访谈结果整理 B端产品的需求来源是非常复杂的,要考虑多个方面;如果你是一个通用性的产品,要考虑市场、自身优劣势、干系人。而定制型B端产品会…...

整数规划基本原理
1.1 定义 规划中的变量(部分或全部)限制为整数时,称为整数规划。若在线性规划模型中,变量限制为整数,则称为整数线性规划。目前所流行的求解整数规划的方法,往往只适用于整数线性规划。目前还没有一种方法…...

秋招复习之堆
目录 前言 堆 堆的常用操作 堆的实现(大根堆) 1. 堆的存储与表示 2. 访问堆顶元素 3. 元素入堆 4. 堆顶元素出堆 Top-k 问题 方法一:遍历选择 方法二:排序 方法三:堆 总结 前言 秋招复习之堆。 堆 「堆 heap…...

算法训练营Day36(贪心-重叠区间)
都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙! 还是属于那种,做过了也就会了,没做过就很难想出来。 不过大家把如下三题做了之后&#…...

如何利用Oracle官方网站不登录账号下载和安装非最新版本的JDK(版本自由选择)
一、JDK概述 JDK(Java Development Kit)是Java开发工具集,是针对Java编程语言的软件开发环境。它包含了Java编译器、JRE(Java运行时环境)以及其他一些用于开发、调试和测试Java应用程序的工具,是Java开发人…...

税法相关的基础知识
文章目录 税法原则1.税法基本原则2.税法适用原则 来和大家聊聊税法相关的基础知识 税法原则 1.税法基本原则 2.税法适用原则...
ListNode 2487. 从链表中移除节点,单调栈的应用
一、题目 1、题目描述 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 2、接口描述 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nu…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...