首页
统计
关于
Search
1
Sealos3.0离线部署K8s集群
1,433 阅读
2
Spring Cloud OAuth2.0
982 阅读
3
类的加载
969 阅读
4
SpringBoot自动装配原理
848 阅读
5
集合不安全问题
702 阅读
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
登录
Search
标签搜索
Java
CSS
mysql
RabbitMQ
JavaScript
React
Redis
OpenCV
JVM
Mybatis-Plus
Netty
Camunda
多线程
CSS3
Python
Canvas
Spring Cloud
注解和反射
Activiti
工作流
蘇阿細
累计撰写
484
篇文章
累计收到
4
条评论
首页
栏目
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
页面
统计
关于
搜索到
484
篇与
的结果
2026-06-17
NIO基础 - ByteBuffer
2. ByteBuffer2.1 基本使用package com.sw.netty._01; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; @Slf4j public class ByteBufferTest { public static void main(String[] args) { try (InputStream is = ByteBufferTest.class.getClassLoader().getResourceAsStream("ByteBufferTest.txt")) { if (is == null) { throw new IllegalArgumentException("resource not found: ByteBufferTest.txt"); } try (ReadableByteChannel channel = Channels.newChannel(is)) { // 准备缓冲区 ByteBuffer bf = ByteBuffer.allocate(10); // 从 channel 读数据,写入 buffer int len = channel.read(bf); log.info("读取到的字节数:{}", len); // 切换为读模式 bf.flip(); while (bf.hasRemaining()) { byte b = bf.get(); log.info("读取到的字节:{}", (char) b); } // 切换为写模式 bf.clear(); } } catch (IOException e) { e.printStackTrace(); } } } 执行流程:向 buffer 写入数据, channel.read(buffer)调用 flip() 切换至读模式从 buffer 读数据,buffer.get()调用 clear() 或 compact() 切换至写模式重复步骤 1 - 42.2 结构buffer 包含的属性有:capacity、position、limit开始:写模式下,position 表示写入位置,limit 表示容量,写入4个字节调用 flip 后,position 切换为读取位置, limit切换为读取限制读取4个字节后的状态调用 clear 后调用 compact 方法,它的作用是把未读完的部分向前压缩,然后切换至写模式ByteBufferReadWriteTestpublic class ByteBufferReadWriteTest { public static void main(String[] args) { ByteBuffer bf = ByteBuffer.allocate(10); bf.put((byte) 0x16); debugAll(bf); bf.put(new byte[]{0x17, 0x18, 0x19}); debugAll(bf); bf.flip(); System.out.println(bf.get()); debugAll(bf); bf.compact(); debugAll(bf); bf.put(new byte[]{0x20, 0x21, 0x22}); debugAll(bf); // +--------+-------------------- all ------------------------+----------------+ // position: [1], limit: [10] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 16 00 00 00 00 00 00 00 00 00 |.......... | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [4], limit: [10] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 16 17 18 19 00 00 00 00 00 00 |.......... | // +--------+-------------------------------------------------+----------------+ // 22 // +--------+-------------------- all ------------------------+----------------+ // position: [1], limit: [4] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 16 17 18 19 00 00 00 00 00 00 |.......... | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [3], limit: [10] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 17 18 19 19 00 00 00 00 00 00 |.......... | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [6], limit: [10] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 17 18 19 20 21 22 00 00 00 00 |... !".... | // +--------+-------------------------------------------------+----------------+ } } 2.3 常见方法(1)分配空间// 使用堆内存,会受 gc 的影响 ByteBuffer.allocate(10); // 使用直接(物理)内存,读写效率高,但分配效率低 ByteBuffer.allocateDirect(10);(2)向 buffer 写入数据调用 channel 的 read 方法int len = channel.read(bf);调用 buffer 的 put 方法bf.put((byte) 0x16)(3)从 buffer 读取数据调用 channel 的 write 方法int len = channel.write(bf);调用 buffer 的 get 方法// get 方法会让 position 读指针向后走 // rewind 方法可以将 position 重新置为 0 // get(index) 方法获取指定索引下标的内容时,不会移动读指针 byte b = bf.get();ByteBufferReadTestpackage com.sw.netty._01; import java.nio.ByteBuffer; import static utils.ByteBufferUtil.debugAll; public class ByteBufferReadTest { public static void main(String[] args) { ByteBuffer bf = ByteBuffer.allocate(10); bf.put(new byte[]{'a', 'b', 'c', 'd'}); bf.flip(); // 读全部 bf.get(new byte[4]); debugAll(bf); // 从头重新开始读取一个字节 bf.rewind(); System.out.println((char) bf.get()); // +--------+-------------------- all ------------------------+----------------+ // position: [4], limit: [4] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... | // +--------+-------------------------------------------------+----------------+ // a // mark 标记 position 位置,reset 将 position 位置重置到 mark 标记的位置 System.out.println((char) bf.get()); // b bf.mark(); System.out.println((char) bf.get()); // c System.out.println((char) bf.get()); // d bf.reset(); System.out.println((char) bf.get()); // c // get(index) 不会改变读索引的位置 System.out.println((char) bf.get(3)); debugAll(bf); // d // +--------+-------------------- all ------------------------+----------------+ // position: [1], limit: [4] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... | // +--------+-------------------------------------------------+----------------+ } } (4)字符串与 ByteBuffer 互转package com.sw.netty._01; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import static utils.ByteBufferUtil.debugAll; public class ByteBuffer2StringTest { public static void main(String[] args) { ByteBuffer bf = ByteBuffer.allocate(10); // 字符串转 ByteBuffer // 1. 字符串 getBytes() bf.put("sxc".getBytes()); debugAll(bf); // +--------+-------------------- all ------------------------+----------------+ // position: [3], limit: [16] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 73 78 63 00 00 00 00 00 00 00 00 00 00 00 00 00 |sxc.............| // +--------+-------------------------------------------------+----------------+ // 2. Charset encode之后自动切换为读模式 ByteBuffer bf1 = StandardCharsets.UTF_8.encode("sxc"); debugAll(bf1); // +--------+-------------------- all ------------------------+----------------+ // position: [0], limit: [3] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 73 78 63 |sxc | // +--------+-------------------------------------------------+----------------+ // 3. wrap 同理方法2,自动切换为读模式 ByteBuffer bf2 = ByteBuffer.wrap("sxc".getBytes()); debugAll(bf2); // +--------+-------------------- all ------------------------+----------------+ // position: [0], limit: [3] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 73 78 63 |sxc | // +--------+-------------------------------------------------+----------------+ // ByteBuffer 转字符串 bf.flip(); System.out.println(StandardCharsets.UTF_8.decode(bf)); //sxc // Charset、wrap 方法生成的 ByteBuffer 对象不需要再手动显示切换为读模式 System.out.println(StandardCharsets.UTF_8.decode(bf1)); //sxc System.out.println(StandardCharsets.UTF_8.decode(bf2)); //sxc } } 2.4 Scattering Reads 分散读ScatteringReadsTestpackage com.sw.netty._01; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import static utils.ByteBufferUtil.debugAll; public class ScatteringReadsTest { public static void main(String[] args) { URL resource = ScatteringReadsTest.class.getClassLoader().getResource("ScatteringReadsTest.txt"); if (resource == null) { throw new IllegalArgumentException("resource not found: ScatteringReadsTest.txt"); } try (FileChannel channel = FileChannel.open(Paths.get(resource.toURI()))) { ByteBuffer bf1 = ByteBuffer.allocate(3); ByteBuffer bf2 = ByteBuffer.allocate(3); ByteBuffer bf3 = ByteBuffer.allocate(3); channel.read(new ByteBuffer[]{bf1, bf2, bf3}); bf1.flip(); bf2.flip(); bf3.flip(); debugAll(bf1); debugAll(bf2); debugAll(bf3); // +--------+-------------------- all ------------------------+----------------+ // position: [0], limit: [3] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 31 32 33 |123 | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [0], limit: [3] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 34 35 36 |456 | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [0], limit: [3] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 37 38 39 |789 | // +--------+-------------------------------------------------+----------------+ } catch (Exception e) { e.printStackTrace(); } } } 2.5 GatheringWrites 集中写GatheringWritesTestpackage com.sw.netty._01; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; public class GatheringWritesTest { public static void main(String[] args) { ByteBuffer bf1 = StandardCharsets.UTF_8.encode("sun"); ByteBuffer bf2 = StandardCharsets.UTF_8.encode("xiao"); ByteBuffer bf3 = StandardCharsets.UTF_8.encode("chuan"); try (FileChannel channel = new RandomAccessFile("GatheringWritesTest.txt", "rw").getChannel()) { channel.write(new ByteBuffer[]{bf1, bf2, bf3}); } catch (IOException e) { e.printStackTrace(); } } } 2.6 黏包、半包ByteBufferExamTestpackage com.sw.netty._01; import java.nio.ByteBuffer; import static utils.ByteBufferUtil.debugAll; public class ByteBufferExamTest { public static void main(String[] args) { /** * 例:通过网络发送给服务器的多条数据如下: * Yao Shui Ge,\n * Jin Se Wei Ye Na,\n * Zhi Bo Jian. * 由于各种原因,变成了如下的形式(黏包、半包) * Yao Shui Ge,\nJin S * e Wei Ye Na,\nZ * hi Bo Jian. * 现要求将黏包、半包的数据恢复为正确的按 \n 分隔的数据 */ ByteBuffer originBf = ByteBuffer.allocate(45); originBf.put("Yao Shui Ge,\nJin S".getBytes()); split(originBf); originBf.put("e Wei Ye Na,\nZ".getBytes()); split(originBf); originBf.put("hi Bo Jian.\n".getBytes()); split(originBf); } private static void split(ByteBuffer source) { source.flip(); for (int i = 0; i < source.limit(); i++) { if ('\n' == source.get(i)) { int length = i + 1 - source.position(); ByteBuffer target = ByteBuffer.allocate(length); // 从 source 读,向 target 写 for (int j = 0; j < length; j++) { target.put(source.get()); } debugAll(target); } } // 此处不使用 clear,需使用 compact 将剩余未读的部分向前移动 source.compact(); } // +--------+-------------------- all ------------------------+----------------+ // position: [13], limit: [13] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 59 61 6f 20 53 68 75 69 20 47 65 2c 0a |Yao Shui Ge,. | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [18], limit: [18] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 4a 69 6e 20 53 65 20 57 65 69 20 59 65 20 4e 61 |Jin Se Wei Ye Na| // |00000010| 2c 0a |,. | // +--------+-------------------------------------------------+----------------+ // +--------+-------------------- all ------------------------+----------------+ // position: [13], limit: [13] // +-------------------------------------------------+ // | 0 1 2 3 4 5 6 7 8 9 a b c d e f | // +--------+-------------------------------------------------+----------------+ // |00000000| 5a 68 69 20 42 6f 20 4a 69 61 6e 2e 0a |Zhi Bo Jian.. | // +--------+-------------------------------------------------+----------------+ }
2026年06月17日
36 阅读
0 评论
0 点赞
2026-06-17
NIO基础 - 三大组件
Netty参考 B站 it黑马 Netty 课程一、NIO 基础1. 三大组件1.1 Channel、BufferChannel 是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以从 buffer 将数据写入 channel,常见的 Channel 有:FileChannelDatagramChannelSocketChannelServerSocketChannelBuffer 用于缓冲读写数据,常见的 Buffer 有:ByteBufferMappedByteBufferDirectByteBufferHeapByteBufferShortBufferIntBufferLongBufferFloatBufferDoubleBufferCharBuffer1.2 SelectorSelector 的作用是配合一个线程来管理多个 Channel,获取不同 Channel 上发生的事件,这些 Channel 工作在非阻塞模式下,不会让线程一直工作在一个 Channel 上,适合连接数多,但数据量不大的场景
2026年06月17日
11 阅读
0 评论
0 点赞
2026-04-14
React - zustand
更轻量级的集中式状态管理工具1. 示例import { create } from 'zustand' const useStore = create((set) => { return { // 状态数据 count: 0, // 修改状态数据的方法 inc: () => { // 修改原数据 set((state) => ({ count: state.count + 1 })) // 直接修改 // set({ count: 100 }) } } }) function App() { const { count, inc } = useStore() return ( <div className="App"> <p>{count}</p> <button onClick={inc}>+ 1</button> </div> ) } export default App 2. 异步方法import { useEffect } from 'react' import { create } from 'zustand' const useStore = create((set) => { return { // 状态数据 count: 0, channelList: [], // 修改状态数据的方法 inc: () => { // 修改原数据 set((state) => ({ count: state.count + 1 })) // 直接修改 // set({ count: 100 }) }, getChannelList: async () => { const res = await fetch('http://geek.itheima.net/v1_0/channels') const resJson = await res.json() console.log(resJson) set({ channelList: resJson.data.channels }) } } }) function App() { const { count, inc, channelList, getChannelList } = useStore() useEffect(() => { getChannelList() }, [getChannelList]) return ( <div className="App"> <p>{count}</p> <button onClick={inc}>+ 1</button> <ul> {channelList.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> ) } export default App 3. 切片模式当单个 store 较大时,可以用切片模式进行模块的拆分组合import { useEffect } from 'react' import { create } from 'zustand' const createCountStore = (set) => { return { count: 0, inc: () => { set((state) => ({ count: state.count + 1 })) } } } const createChannelStore = (set) => { return { channelList: [], getChannelList: async () => { const res = await fetch('http://geek.itheima.net/v1_0/channels') const resJson = await res.json() console.log(resJson) set({ channelList: resJson.data.channels }) } } } const useStore = create((...a) => { return { ...createCountStore(...a), ...createChannelStore(...a) } }) function App() { const { count, inc, channelList, getChannelList } = useStore() useEffect(() => { getChannelList() }, [getChannelList]) return ( <div className="App"> <p>{count}</p> <button onClick={inc}>+ 1</button> <ul> {channelList.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> ) } export default App
2026年04月14日
33 阅读
0 评论
0 点赞
2026-04-13
React - 类组件
1. 概念通过 js 中的类来组织代码2. 实现方式(1)通过类属性 state 定义状态数据(2)通过 setState 方法修改状态数据(3)通过 render 函数返回要渲染的 UI3. 示例import { Component } from 'react' class Counter extends Component { state = { count: 0 } setCount = () => { this.setState({ count: this.state.count + 1 }) } render() { return <button onClick={this.setCount}>{this.state.count}</button> } } function App() { return ( <div className="App"> <Counter /> </div> ) } export default App 4. 生命周期函数示例(以 componentDidMount, componentWillUnmount 为例):import { Component, useState } from 'react' class Son extends Component { // 组件渲染完毕执行一次 componentDidMount() { console.log('组件渲染完毕') this.timer = setInterval(() => { console.log('执行定时器') }, 1000) } // 组件卸载之后执行(常用于清除副作用) componentWillUnmount() { console.log('子组件被卸载') clearInterval(this.timer) } render() { return <div>Son Component</div> } } function App() { const [show, setShow] = useState(true) return ( <div className="App"> {show && <Son />} <button onClick={() => setShow(false)}>unmount son component</button> </div> ) } export default App 5. 组件通信概念:与常规的组件通信思路一致(父传子、子传父、兄弟组件间通信)(1)父传子import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据' } render() { return ( <div> <p>Parent Component</p> <Son msg={this.state.msg} /> </div> ) } } class Son extends Component { render() { return ( <div> Son Component <p>{this.props.msg}</p> </div> ) } } function App() { return ( <div className="App"> <Parent /> </div> ) } export default App (2)子传父import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据' } getSonMsg = (sonMsg) => { console.log('子组件传递的数据:', sonMsg) } render() { return ( <div> <p>Parent Component</p> <Son msg={this.state.msg} getSonMsg={this.getSonMsg} /> </div> ) } } class Son extends Component { render() { return ( <div> Son Component <p>{this.props.msg}</p> <button onClick={() => this.props.getSonMsg('孙笑川')}> 传递数据给父组件 </button> </div> ) } } function App() { return ( <div className="App"> <Parent /> </div> ) } export default App (3)兄弟组件通信import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据', msg1: '' } setMsg1 = (val) => { this.setState({ msg1: val }) } getSonMsg = (sonMsg) => { console.log('子组件传递的数据:', sonMsg) } render() { return ( <div> <p>Parent Component</p> <hr /> <Son msg={this.state.msg} getSonMsg={this.getSonMsg} setMsg={this.setMsg1} /> <hr /> <Daughter msg={this.state.msg} brotherMsg={this.state.msg1} /> </div> ) } } class Son extends Component { render() { return ( <div> Son Component <p>{this.props.msg}</p> <button onClick={() => this.props.getSonMsg('孙笑川')}> 传递数据给父组件 </button> <br /> <button onClick={() => this.props.setMsg('药水哥')}> 传递数据给兄弟组件 </button> </div> ) } } class Daughter extends Component { render() { return ( <div> Daughter Component <p>父组件传递的数据:{this.props.msg}</p> <p>兄弟组件传递的数据:{this.props.brotherMsg}</p> </div> ) } } function App() { return ( <div className="App"> <Parent /> </div> ) } export default App
2026年04月13日
26 阅读
0 评论
0 点赞
2026-04-13
React - useImperativeHandle
作用:通过 ref 讲子组件的方法暴露给父组件import { forwardRef, useImperativeHandle, useRef } from 'react' const Son = forwardRef((props, ref) => { const inputRef = useRef(null) const inputHandle = () => { inputRef.current.focus() } useImperativeHandle(ref, () => { return { inputHandle } }) return <input type="text" ref={inputRef} /> }) function App() { const sonRef = useRef(null) const sonInputFocusHandle = () => { console.log(sonRef) sonRef.current.inputHandle() } return ( <div className="App"> <Son ref={sonRef} /> <button onClick={sonInputFocusHandle}>focus</button> </div> ) } export default App
2026年04月13日
29 阅读
0 评论
0 点赞
2026-04-13
React - forwardRef
作用:使用 ref 暴露组件给父节点import { forwardRef, useRef } from 'react' // function Son() { // return <input type="text" /> // } const Son = forwardRef((props, ref) => { return <input type="text" ref={ref} /> }) function App() { const sonRef = useRef(null) const sonInputFocus = () => { console.log(sonRef) sonRef.current.focus() } return ( <div className="App"> <Son ref={sonRef} /> <button onClick={sonInputFocus}>focus</button> </div> ) } export default App
2026年04月13日
18 阅读
0 评论
0 点赞
2026-04-13
React - useCallback
作用:在组件多次重新渲染时缓存函数import { memo, useCallback, useState } from 'react' const MemoInput = memo(function Input({ onChange }) { console.log('执行子组件重新渲染') return <input type="text" onChange={(e) => onChange(e.target.value)} /> }) function App() { const changeHandle = useCallback((val) => { console.log(val) }, []) const [count, setCount] = useState(0) return ( <div className="App"> <MemoInput onChange={changeHandle} /> <button onClick={() => setCount(count + 1)}>{count}</button> </div> ) } export default App
2026年04月13日
21 阅读
0 评论
0 点赞
2026-04-12
React - React.memo
作用:允许组件在 Props 没有改变的情况下跳过渲染注:React 组件渲染机制,只要父组件重新渲染,子组件也会重新渲染1. 示例import { memo, useState } from 'react' // function Son() { // console.log('执行子组件渲染') // return <div>Son Component</div> // } // props 发生变化时才重新渲染子组件 const MemoSon = memo(function Son() { console.log('执行子组件渲染') return <div>Son Component</div> }) function App() { const [count, setCount] = useState(0) return ( <div className="App"> count: {count} <button onClick={() => setCount(count + 1)}>count + 1</button> <MemoSon /> </div> ) } export default App 2. props 的比较机制在使用 memo 缓存组件后,React 会对每一个 prop 使用 Object.is 方法比较心值和旧值prop 是简单(基本)类型Object.is(1, 1) ---> true 无变化prop 是引用类型(对象/数组)Object.is([], []) ---> false 有变化(即:React 不关心内容,只关心引用是否发生变化)import { memo, useMemo, useState } from 'react' const MemoSon = memo(function Son({ count, list }) { console.log('执行子组件渲染') return <div>Son Component</div> }) function App() { const [count, setCount] = useState(0) const index = 100 const list = [1, 2, 3] // 保持引用稳定 const memoList = useMemo(() => { return [1, 2, 3] }, []) return ( <div className="App"> count: {count} <button onClick={() => setCount(count + 1)}>count + 1</button> {/* 子组件 prop 发生变化(count累加),执行重新渲染 */} {/* <MemoSon count={count} /> */} {/* 子组件 prop 无变化(传递的 count 是固定的),不执行重新渲染 */} {/* <MemoSon count={index} /> */} {/* count 累加变化时,父组件重新渲染,list 也会重新执行声明(即子组件 prop 参数的引用发生了变化,执行重新渲染) */} {/* <MemoSon list={list} /> */} {/* memo 保持引用稳定 */} <MemoSon list={memoList} /> </div> ) } export default App
2026年04月12日
38 阅读
0 评论
0 点赞
2026-04-12
React - useMemo
作用:在组件每次重新渲染的时候缓存计算的结果import { useMemo, useState } from 'react' function fib(n) { console.log('执行斐波那契计算函数') if (n < 3) { return 1 } return fib(n - 2) + fib(n - 1) } function App() { const [count1, setCount1] = useState(0) const [count2, setCount2] = useState(0) // 只有count1变化时,才重新执行斐波那契计算函数 // useMemo 在计算密集型场景使用 const result = useMemo(() => { return fib(count1) }, [count1]) console.log('组件执行重新渲染') return ( <div className="App"> count1: {count1} <button onClick={() => setCount1(count1 + 1)}>count1 + 1</button> count2: {count2} <button onClick={() => setCount2(count2 + 1)}>count2 + 1</button> result: {result} </div> ) } export default App
2026年04月12日
24 阅读
0 评论
0 点赞
2026-04-12
React - useReducer
作用:和 useState 类似,用于管理相对复杂的状态数据基础用法:定义 reducer 函数组件中调用 useReducer,并传入 reducer 函数和初始值通过 dispatch 函数分派一个 action 对象(通知 reducer 要返回哪个新状态并渲染 UI)import { useReducer } from 'react' function reducer(state, action) { switch (action.type) { case 'INC': return state + 1 case 'DEC': return state - 1 case 'SET': return action.payload default: return state } } function App() { const [state, dispatch] = useReducer(reducer, 0) return <div className="App">{state} <button onClick={() => {dispatch({type: 'INC'})}}>+</button> <button onClick={() => {dispatch({type: 'DEC'})}}>-</button> <button onClick={() => {dispatch({type: 'SET', payload: 100})}}>SET</button> </div> } export default App
2026年04月12日
25 阅读
0 评论
0 点赞
1
2
3
...
49