问:你是如何进行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 倍区间吗?输入格式第一行包含两个整…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...