问:你是如何进行react状态管理方案选择的?
前言:最近接触到一种新的(对我个人而言)状态管理方式,它没有采用现有的开源库,如redux、mobx等,也没有使用传统的useContext,而是用useState + useEffect写了一个发布订阅者模式进行状态管理,这一点对我来说感觉比较新奇,以前从没接触过这种写法,于是决定研究一下目前比较常用的状态管理方式。
ps:这里谈到的状态管理是指全局状态管理,局部的使用useState即可
状态管理方式
目前比较常用的状态管理方式有hooks、redux、mobx三种,下面我将详细介绍一下这三类的使用方法以及分析各自的优缺点,以供各位进行参考。
Hooks状态管理
用hooks进行状态管理主要有两种方式:
- useContext+useReducer
- useState+useEffect
useContext+useReducer
使用方法
1.创建store和reducer以及全局context
src/store/reducer.ts
import React from "react";
// 初始状态
export const state = {count: 0,name: "ry",
};// reducer 用于修改状态
export const reducer = (state, action) => {const { type, payload } = action;switch (type) {case "ModifyCount":return {...state,count: payload,};case "ModifyName":return {...state,name: payload,};default: {return state;}}
};export const GlobalContext = React.createContext(null);
2.根组件通过 Provider 注入 context
src/App.tsx
import React, { useReducer } from "react";
import './index.less'
import { state as initState, reducer, GlobalContext} from './store/reducer'
import Count from './components/Count'
import Name from './components/Name'export default function () {const [state, dispatch] = useReducer(reducer, initState);return (<div><GlobalContext.Provider value={{state, dispatch}}><Count /><Name /></GlobalContext.Provider></div>)
}
3.在组件中使用
src/components/Count/index.tsx
import { GlobalContext } from "@/store/reducer";
import React, { FC, useContext } from "react";const Count: FC = () => {const ctx = useContext(GlobalContext)return (<div><p>count:{ctx.state.count}</p><button onClick={() => ctx.dispatch({ type: "ModifyCount", payload: ctx.state.count+1 })}>+1</button></div>);
};export default Count;
参考 前端进阶面试题详细解答
src/components/Name/index.tsx
import { GlobalContext } from "@/store/reducer";
import React, { FC, useContext } from "react";const Name: FC = () => {const ctx = useContext(GlobalContext)console.log("NameRerendered")return (<div><p>name:{ctx.state.name}</p></div>);
};export default Name;
useState+useEffect
使用方法
1.创建state和reducer
src/global-states.ts
// 初始state
let globalState: GlobalStates = {count: 0,name: 'ry'
}// reducer
export const modifyGlobalStates = (operation: GlobalStatesModificationType, payload: any
) => {switch (operation) {case GlobalStatesModificationType.MODIFY_COUNT:globalState = Object.assign({}, globalState, { count: payload })breakcase GlobalStatesModificationType.MODIFY_NAME:globalState = Object.assign({}, globalState, { name: payload })break}broadcast()
}
src/global-states.type.ts
export interface GlobalStates {count: number;name: string;
}export enum GlobalStatesModificationType {MODIFY_COUNT,MODIFY_NAME
}
2.写一个发布订阅模式,让组件订阅globalState
src/global-states.ts
import { useState, useEffect } from 'react'
import {GlobalStates,GlobalStatesModificationType
} from './global-states.type'let listeners = []let globalState: GlobalStates = {count: 0,name: 'ry'
}
// 发布,所有订阅者收到消息,执行setState重新渲染
const broadcast = () => {listeners.forEach((listener) => {listener(globalState)})
}export const modifyGlobalStates = (operation: GlobalStatesModificationType, payload: any
) => {switch (operation) {case GlobalStatesModificationType.MODIFY_COUNT:globalState = Object.assign({}, globalState, { count: payload })breakcase GlobalStatesModificationType.MODIFY_NAME:globalState = Object.assign({}, globalState, { name: payload })break}// 状态改变即发布broadcast()
}// useEffect + useState实现发布订阅
export const useGlobalStates = () => {const [value, newListener] = useState(globalState)useEffect(() => {// newListener是新的订阅者listeners.push(newListener)// 组件卸载取消订阅return () => {listeners = listeners.filter((listener) => listener !== newListener)}})return value
}
3.组件中使用
src/App.tsx
import React from 'react'
import './index.less'
import Count from './components/Count'
import Name from './components/Name'export default function () {return (<div><Count /><Name /></div>)
}
src/components/Count/index.tsx
import React, { FC } from 'react'
import { useGlobalStates, modifyGlobalStates } from '@/store/global-states'
import { GlobalStatesModificationType } from '@/store/global-states.type'const Count: FC = () => {// 调用useGlobalStates()即订阅globalStates()const { count } = useGlobalStates()return (<div><p>count:{count}</p><buttononClick={() =>modifyGlobalStates( GlobalStatesModificationType.MODIFY_COUNT, count + 1 ) } > +1 </button></div>)
}export default Count
src/components/Name/index.tsx
import React, { FC } from 'react'
import { useGlobalStates } from '@/store/global-states'const Count: FC = () => {const { name } = useGlobalStates()console.log('NameRerendered')return (<div><p>name:{name}</p></div>)
}export default Count
优缺点分析
由于以上两种都是采用hooks进行状态管理,这里统一进行分析,
优点
- 代码比较简洁,如果你的项目比较简单,只有少部分状态需要提升到全局,大部分组件依旧通过本地状态来进行管理。这时,使用 hookst进行状态管理就挺不错的。杀鸡焉用牛刀。
缺点
- 两种hooks管理方式都有一个很明显的缺点,会产生大量的无效rerender,如上例中的Count和Name组件,当state.count改变后,Name组件也会rerender,尽管他没有使用到state.count。这在大型项目中无疑是效率比较低的。
Redux状态管理
使用方法:
1.引入redux
yarn add redux react-redux @types/react-redux redux-thunk
2.新建reducer
在src/store/reducers文件夹下新建addReducer.ts(可建立多个reducer)
import * as types from '../action.types'
import { AnyAction } from 'redux'// 定义参数接口
export interface AddState {count: numbername: string
}// 初始化state
let initialState: AddState = {count: 0,name: 'ry'
}// 返回一个reducer
export default (state: AddState = initialState, action: AnyAction): AddState => {switch (action.type) {case types.ADD:return { ...state, count: state.count + action.payload }default:return state}
}
在src/stores文件夹下新建action.types.ts
主要用于声明action类型
export const ADD = 'ADD'
export const DELETE = 'DELETE'
3.合并reducer
在src/store/reducers文件夹下新建index.ts
import { combineReducers, ReducersMapObject, AnyAction, Reducer } from 'redux'
import addReducer, { AddState } from './addReducer'// 如有多个reducer则合并reducers,模块化
export interface CombinedState {addReducer: AddState
}
const reducers: ReducersMapObject<CombinedState, AnyAction> = {addReducer
}
const reducer: Reducer<CombinedState, AnyAction> = combineReducers(reducers)export default reducer
3.创建store
在src/stores文件夹下新建index.ts
import {createStore,applyMiddleware,StoreEnhancer,StoreEnhancerStoreCreator,Store
} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducers'// 生成store增强器
const storeEnhancer: StoreEnhancer = applyMiddleware(thunk)
const storeEnhancerStoreCreator: StoreEnhancerStoreCreator = storeEnhancer(createStore)const store: Store = storeEnhancerStoreCreator(reducer)export default store
4.根组件通过 Provider 注入 store
src/index.tsx(用provider将App.tsx包起来)
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { Provider } from 'react-redux'
import store from './store'ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
)
5.在组件中使用
src/somponents/Count/index.tsx
import React, { FC } from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { AddState } from 'src/store/reducers/addReducer'
import { CombinedState } from 'src/store/reducers'
import * as types from '@/store/action.types'// 声明参数接口
interface Props {count: numberadd: (num: number) => void
}// ReturnType获取函数返回值类型,&交叉类型(用于多类型合并)
// type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>const Count: FC<Props> = (props) => {const { count, add } = propsreturn (<div><p>count: {count}</p><button onClick={() => add(5)}>addCount</button></div>)
}// 这里相当于自己手动做了映射,只有这里映射到的属性变化,组件才会rerender
const mapStateToProps = (state: CombinedState) => ({count: state.addReducer.count
})const mapDispatchToProps = (dispatch: Dispatch) => {return {add(num: number = 1) {// payload为参数dispatch({ type: types.ADD, payload: num })}}
}export default connect(mapStateToProps, mapDispatchToProps)(Count)
src/somponents/Name/index.tsx
import React, { FC } from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { AddState } from 'src/store/reducers/addReducer'
import { CombinedState } from 'src/store/reducers'
import * as types from '@/store/action.types'// 声明参数接口
interface Props {name: string
}const Name: FC<Props> = (props) => {const { name } = propsconsole.log('NameRerendered')return (<div><p>name: {name}</p></div>)
}// name变化组件才会rerender
const mapStateToProps = (state: CombinedState) => ({name: state.addReducer.name
})// addReducer内任意属性变化组件都会rerender
// const mapStateToProps = (state: CombinedState) => state.addReducerexport default connect(mapStateToProps)(Name)
优缺点分析
优点
- 组件会订阅store中具体的某个属性【mapStateToProps手动完成】,只要当属性变化时,组件才会rerender,渲染效率较高
- 流程规范,按照官方推荐的规范和结合团队风格打造一套属于自己的流程。
- 配套工具比较齐全redux-thunk支持异步,redux-devtools支持调试
- 可以自定义各种中间件
缺点
- state+action+reducer的方式不太好理解,不太直观
- 非常啰嗦,为了一个功能又要写reducer又要写action,还要写一个文件定义actionType,显得很麻烦
- 使用体感非常差,每个用到全局状态的组件都得写一个mapStateToProps和mapDispatchToProps,然后用connect包一层,我就简单用个状态而已,咋就这么复杂呢
- 当然还有一堆的引入文件,100行的代码用了redux可以变成120行,不过换个角度来说这也算增加了自己的代码量
- 好像除了复杂也没什么缺点了
Mobx状态管理
常规使用(mobx-react)
使用方法
1.引入mobx
yarn add mobx mobx-react -D
2.创建store
在/src/store目录下创建你要用到的store(在这里使用多个store进行演示)
例如:
store1.ts
import { observable, action, makeObservable } from 'mobx'class Store1 {constructor() {makeObservable(this) //mobx6.0之后必须要加上这一句}@observablecount = 0@observablename = 'ry'@actionaddCount = () => {this.count += 1}
}const store1 = new Store1()
export default store1
store2.ts
这里使用 makeAutoObservable代替了makeObservable,这样就不用对每个state和action进行修饰了(两个方法都可,自行选择)
import { makeAutoObservable } from 'mobx'class Store2 {constructor() {// mobx6.0之后必须要加上这一句makeAutoObservable(this)}time = 11111111110
}const store2 = new Store2()
export default store2
3.导出store
src/store/index.ts
import store1 from './store1'
import store2 from './store2'export const store = { store1, store2 }
4.根组件通过 Provider 注入 store
src/index.tsx(用provider将App.tsx包起来)
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './store'
import { Provider } from 'mobx-react'ReactDOM.render(<Provider {...store}><App /></Provider>,document.getElementById('root')
)
5.在组件中使用
src/somponents/Count/index.tsx
import React, { FC } from 'react'
import { observer, inject } from 'mobx-react'// 类组件用装饰器注入,方法如下
// @inject('store1')
// @observer
interface Props {store1?: any
}
const Count: FC<Props> = (props) => {const { count, addCount } = props.store1return (<div><p>count: {count}</p><button onClick={addCount}>addCount</button></div>)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default inject('store1')(observer(Count))
src/components/Name/index.tsx
import React, { FC } from 'react'
import { observer, inject } from 'mobx-react'interface Props {store1?: any
}const Name: FC<Props> = (props) => {const { name } = props.store1console.log('NameRerendered')return (<div><p>name: {name}</p></div>)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default inject('store1')(observer(Name))
优缺点分析:
优点:
- 组件会自动订阅store中具体的某个属性,无需手动订阅噢!【下文会简单介绍下原理】只有当订阅的属性变化时,组件才会rerender,渲染效率较高
- 一个store即写state,也写action,这种方式便于理解,并且代码量也会少一些
缺点:
- 当我们选择的技术栈是React+Typescript+Mobx时,这种使用方式有一个非常明显的缺点,引入的store必须要在props的type或interface定义过后才能使用(会增加不少代码量),而且还必须指定这个store为可选的,否则会报错(因为父组件其实没有传递这个prop给子组件),这样做还可能会致使对store取值时,提示可能为undefined,虽然能够用“!”排除undefined,可是这种作法并不优雅。
最佳实践(mobx+hooks)
使用方法
1.引入mobx
同上
2.创建store
同上
3.导出store(结合useContext)
src/store/index.ts
import React from 'react'
import store1 from './store1'
import store2 from './store2'// 导出store1
export const storeContext1 = React.createContext(store1)
export const useStore1 = () => React.useContext(storeContext1)// 导出store2
export const storeContext2 = React.createContext(store2)
export const useStore2 = () => React.useContext(storeContext2)
4.在组件中使用
无需使用Provider注入根组件
src/somponents/Count/index.tsx
import React, { FC } from 'react'
import { observer } from 'mobx-react'
import { useStore1 } from '@/store/'// 类组件可用装饰器,方法如下
// @observerconst Count: FC = () => {const { count, addCount } = useStore1()return (<div><p>count: {count}</p><button onClick={addCount}>addCount</button></div>)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default observer(Count)
src/components/Name/index.tsx
import React, { FC } from 'react'
import { observer } from 'mobx-react'
import { useStore1 } from '@/store/'const Name: FC = () => {const { name } = useStore1()console.log('NameRerendered')return (<div><p>name: {name}</p></div>)
}export default observer(Name)
优缺点分析:
优点:
- 学习成本少,基础知识非常简单,跟 Vue 一样的核心原理,响应式编程。
- 一个store即写state,也写action,这种方式便于理解
- 组件会自动订阅store中具体的某个属性,只要当属性变化时,组件才会rerender,渲染效率较高
- 成功避免了上一种使用方式的缺点,不用对使用的store进行interface或type声明!
- 内置异步action操作方式
- 代码量真的很少,使用很简单有没有,强烈推荐!
缺点:
- 过于自由:Mobx提供的约定及模版代码很少,这导致开发代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一,团队建议启用严格模式!
- 使用方式过于简单
Mobx自动订阅实现原理
基本概念
Observable //被观察者,状态
Observer //观察者,组件
Reaction //响应,是一类的特殊的 Derivation,可以注册响应函数,使之在条件满足时自动执行。
建立依赖
我们给组件包的一层observer实现了这个功能
export default observer(Name)
组件每次mount和update时都会执行一遍useObserver函数,useObserver函数中通过reaction.track进行依赖收集,将该组件加到该Observable变量的依赖中(bindDependencies)。
// fn = function () { return baseComponent(props, ref);
export function useObserver(fn, baseComponentName) {...var rendering;var exception;reaction.track(function () {try {rendering = fn();}catch (e) {exception = e;}});if (exception) {throw exception; // re-throw any exceptions caught during rendering}return rendering;
}
reaction.track()
_proto.track = function track(fn) {// 开始收集startBatch();var result = trackDerivedFunction(this, fn, undefined);// 结束收集endBatch();};
reaction.track里面的核心内容是trackDerivedFunction
function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {...let result// 执行回调f,触发了变量(即组件的参数)的 get,从而获取 dep【收集依赖】if (globalState.disableErrorBoundaries === true) {result = f.call(context)} else {try {result = f.call(context)} catch (e) {result = new CaughtException(e)}}globalState.trackingDerivation = prevTracking// 给 observable 绑定 derivationbindDependencies(derivation)...return result
}
触发依赖
Observable(被观察者,状态)修改后,会调用它的set方法,然后再依次执行该Observable之前收集的依赖函数,触发rerender。
组件更新
用组件更新来简单阐述总结一下:mobx的执行原理。
- observer这个装饰器(也可以是Hoc),对React组件的render方法进行track。
- 将render方法,加入到各个observable的依赖中。当observable发生变化,track方法就会执行。
- track中,还是先进行依赖收集,调用forceUpdate去更新组件,然后结束依赖收集。
每次都进行依赖收集的原因是,每次执行依赖可能会发生变化
总结
简单总结了一下目前较为常用的状态管理方式,我个人最喜欢的使用方式是Mobx+Hooks,简单轻量易上手。各位可以根据自己的需求选择适合自己项目的管理方式。
相关文章:
问:你是如何进行react状态管理方案选择的?
前言:最近接触到一种新的(对我个人而言)状态管理方式,它没有采用现有的开源库,如redux、mobx等,也没有使用传统的useContext,而是用useState useEffect写了一个发布订阅者模式进行状态管理&…...
【华为OD机试真题 java、python、jsNode】任务总执行时长【2022 Q4 100分】
代码请进行一定修改后使用,本代码保证100%通过率,本题提供了 java、python、JsNode三种代码 题目描述 任务编排服务负责对任务进行组合调度。参与编排的任务有两种类型,其中一种执行时长为taskA,另一种执行时长为taskB。任务一旦开始执行不能被打断,且任务可连续执行。服…...
react基础
react组件传参 父传子 父组件 < ChildA value{this.state.num}></ChildA> 子组件 {props.value}接收父组件传入参数 ChildA.defaultProps{vaue:1} defaultProps默认参数 子传父 props回调函数形式 父 setNum>v>this.setState({num:v}) v形参 < ChildA…...

【Spark分布式内存计算框架——Spark SQL】2. SparkSQL 概述(上)
第二章 SparkSQL 概述 Spark SQL允许开发人员直接处理RDD,同时可以查询在Hive上存储的外部数据。Spark SQL的一个重要特点就是能够统一处理关系表和RDD,使得开发人员可以轻松的使用SQL命令进行外部查询,同时进行更加复杂的数据分析。 2.1 前…...

Kubeadm搭建K8S
目录 一、部署步骤 1、实验环境 2、环境准备 3、所有节点安装Docker 4、 所有节点配置K8S源 5、所有节点安装kubeadm,kubelet和kubectl 6、部署 kubernetes Master 节点 7、token制作 8、k8s-node节点加入master节点 9、 master节点安装部署pod网络插件&a…...
【技术分享】搭建java项目引入外部依赖教程
文章目录引言如何在linux中编译运行java程序IDEA中新建一个简单的java工程项目并运行IDEA中如何引入外部依赖并运行maven引入log4j jar包手工引入log4j jar包如何使用命令行的方式添加外部依赖如何新建一个spring源码项目并为其添加依赖给定一个spring工程源码,如何…...

算法 ——世界 二
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。个人爱好: 编程,打篮球,计算机知识个人名言:海不辞水,故能成其大;山不辞石…...
数据治理CDGP选择题 4
5、根据DMBOK2,在实施数据治理时,要注重数据标准的建设,以下关于数据标准的描述,哪个选项是不正确的? (知识点: CDGP仿真题)A.数据标准必须得到有效沟通、监控,并被定期审查和更新;最重要的是,必须有强制手…...

动态规划之01背包问题和完全背包问题
01背包的问题描述:(内容参考代码随想录)有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。问题示例&#…...
MATLAB算法实战应用案例精讲-【图像处理】数字图像灰度化(附Java、python、matlab和opencv代码实现)
目录 前言 几个相关概念 1、RGB 2、ARGB 3、灰度化 4.图像点运算 5.线性点运算...
Linux(强大的yum命令)
yum 读 [jʌm] ,中文谐音: 样安ing。 yum( Yellow dog Updater, Modified)是一个在 Fedora 和 RedHat 以及 SUSE 中的 Shell 前端软件包管理器。 基于 RPM 包管理,能够从指定的服务器自动下载 RPM 包并且安装&#x…...
28.结语
文章目录28 Epilogue 结语28 Epilogue 结语 Don’t let it end like this. Tell them I said something. 不要让它就这样结束。 告诉他们我说了些什么 —Pancho Villa 您已到达旅程的尽头。 辛苦了 我们希望您能从本书中找到一些有价值的收获。 我们建议该列表应包括以下内…...
ICRS、GCRS、CIRS、TIRS和ITRS坐标系统简介
ICRS、GCRS、CIRS、TIRS和ITRS坐标系统简介1. 简介2. ICRS、GCRS、CIRS、TIRS和ITRS分别介绍2.1 ICRS详细说明2.2 GCRS详细说明2.3 CIRS详细说明2.4 TIRS详细说明2.5 ITRS 详细说明1. 简介 ICRS、GCRS、CIRS、TIRS和ITRS都是天文学中使用的坐标系统: ICRS (Intern…...

你是真的“C”——详解结构体知识点
你是真的“C”——详解结构体知识点😎前言🙌什么是结构体?🙌1. 结构体的声明🙌1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化2. 结构体成员的访问🙌3结构体传参&#x…...
2023新华为OD机试题 - 单词接龙(JavaScript) | 刷完必过
单词接龙 题目 单词接龙的规则是: 可用于接龙的单词,首字母必须要与前一个单词的尾字母相同; 当存在多个首字母相同的单词时,取长度最长的单词; 如果长度也相等,则取字典序最小的单词; 已经参与接龙的单词不能重复使用; 现给定一组全部由小写字母组成的单词数组, 并指…...
第一章 一般错误信息 - 错误代码 0 到 99
文章目录第一章 一般错误信息 - 错误代码 0 到 99一般错误信息错误代码 0 到 99第一章 一般错误信息 - 错误代码 0 到 99 一般错误信息 错误代码被报告为 ERROR #nnn。这些错误代码有时称为 %Status 错误代码。 可以使用 DisplayError() 和 Error() 方法确定指定错误代码的错…...

MyBatis 之一(概念、创建项目、操作模式、交互流程)
1. MyBatis 是什么MyBatis 是一款优秀的持久层框架MyBatis 也是一个 ORM (Object Relational Mapping)框架,即对象关系映射它支持自定义 SQL、存储过程以及高级映射MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作MyBatis…...

学习笔记:文件
因为有的数据,数据量极大。或者是你想把编译输出的内容存储起来,就可以使用文件 读文件中内容具体操作 来自C语言详解 FILE文件操作 - 知乎 (zhihu.com) 写入文件具体操作 同样来自 C语言详解 FILE文件操作 - 知乎 (zhihu.com) 当文件关闭时,…...
高考结束了以后应该做的事情(个人经历的总结)
高考结束了以后应该做的事情 在本指导中,我总结了我大学期间我认为做的没有后悔,最正确的事情。同时,根据我的经历和我踩过的坑总结出来了这一份入学指南。 这个指导有点偏向于工科生,已经尽量偏向于所有专业了。对于所有专业的同…...
蓝桥杯:k倍区间
[蓝桥杯 2017 省 B] k 倍区间给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。你能求出数列中总共有多少个 K 倍区间吗?输入格式第一行包含两个整…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...