React(六):Redux的使用、react-redux简化代码、redux模块化、RTK的使用
React(六)
- 一、Redux测试项目搭建
- 1.创建store仓库
- 2.创建reducer函数(纯函数)
- 3.constants.js保存action名字
- 4.修改store中的数据
- 5.动态生成action
- 二、React中如何使用redux
- 1.安装redux
- 2.创建store
- 3.组件中订阅store
- 4.派发action修改store数据
- 三、第三方库react-redux简化代码
- 1.省掉手动订阅的步骤
- 2.同步派发action
- 3.redux如何进行异步操作
- (1)在组件中派发异步action
- (2)在actionCreators中派发异步action
- 4.redux的devtools怎么打开
- 四、redux模块化
- 五、Redux ToolKit的使用
- 1.三个核心的API
- 2.用更简洁的代码创建store
- (1)configureStore创建大仓库store
- (2)createSlice创建小仓库
- (3)将小仓库添加到大仓库中
- 3.组件中使用和修改仓库的数据
- 4.RTK的异步请求写在哪里
一、Redux测试项目搭建
新建一个文件夹Redux,里面建个src文件夹。先看一下最终的项目结构:
src目录下开启终端执行:
npm init
npm add redux
src目录下启动:
node 文件名
我们一点一点来看这个redux的使用过程:
1.创建store仓库
由于我们是node,所以导包用的不是import(这里没学过node,先不管,反正是import那个意思)
在index.js
中:
const { createStore } = require('redux');
const {reducer} = require('./reducer');// 创建的store
const store = createStore(reducer);module.exports = store;
其中第一行是导入这个创建store的函数,然后reducer我们在另一个文件中引入:
2.创建reducer函数(纯函数)
纯函数就是在这个函数中不能对传进来的参数内部的东西(属性)进行修改。
第一行是引入的action
名字,先不管。
1、第一步是定义一个初始化数据。
2、第二步是创建reducer
函数,该函数有两个参数,第一个state
是目前保存的旧state数据,第二个参数action
是别的地方dispatch
传过来的需要更新的数据
3、render
函数里面主要是找到对应的action,然后修改数据并返回一个新的浅拷贝的state
对象。
4、如果没有对应的aciton
的话,就返回默认的初始state
const {CHANGENAME, CHANGEAGE} = require('./constants');//初始化数据
const initialState = {name: 'zzy',age: 18
}//定义reducer函数:纯函数
//参数1:store目前保存state
//参数2:本次需要更新的action(dispatch传过来的)
//返回值:作为store之后存储的state
function reducer(state = initialState, action) {// console.log('reducer', state, action)// reducer { name: 'zzy', age: 18 } { type: 'changeName', name: 'ht' }//1.第一种,if-else// if (action.type === 'changeName') {// return { ...state, name: action.name };// }// if(action.type === 'changeAge') {// let newState = {...state};// newState.age = action.age;// return newState;// }// return state;//2.第二种,switch-caseswitch(action.type) {case CHANGENAME:return { ...state, name: action.name };case CHANGEAGE:let newState = {...state};newState.age = action.age;return newState;default: return state}
}module.exports = {reducer};
这里呢有个没见过的写法,先学习一下:
const obj = {name: 'zzy', age: 18};
const copy = {...obj, name:'ht'};
console.log(copy);//{name: 'ht', age: 18}
上面的代码中,是浅拷贝一个obj,并把里面的name替换成ht
其实本质上是:
const obj = {name: 'zzy', age: 18};
const copy = {...obj};
copy.name = 'ht'
console.log(copy);//{name: 'ht', age: 18}
最后我们再来看第一行是什么,其实这里的action的名字,只不过我们把名字单独写到另一个js文件引入进来,避免一些名字写错的问题。
3.constants.js保存action名字
这里的名字是为了action自己写名字的时候不小心写错,需要在两个地方引用这个变量:
1、reducer函数中
2、dipatch的地方()
const CHANGENAME = 'changeName';
const CHANGEAGE = 'changeAge';
module.exports = {CHANGENAME,CHANGEAGE
}
4.修改store中的数据
上面这些搞定了,接下来我们来看看如何修改store中的数据:
某个js文件:
const store = require('./store');
console.log(store.getState());
//修改store中的数据
store.dispatch({type: 'changeName', name: 'ht'});
console.log(store.getState());
store.dispatch({type: 'changeAge', age: 30});
console.log(store.getState());
把store引入进来,然后dispatch
,传入一个对象,该对象有两个属性,type
是固定的,值是action的名字;name
是要修改的属性值。通过store.getState()
可以读取当前仓库state
的值。
但是这样的话你会发现每次改都要手动输出修改的值,这样很麻烦,所以我们可以通过store.subscribe
订阅数据的改变,这样的话store中数据一旦改变,就会执行回调,该回调中通过store.getState()
读取的是改变后的新值。
const store = require('./store');//订阅数据的改变
const unsubscribe = store.subscribe(() => {console.log('数据变化了,变成了:', store.getState());
})//修改store中的数据:必须action
store.dispatch({ type: 'changeName', name: 'ht' });
//一般在销毁钩子中取消订阅
unsubscribe();
store.dispatch({ type: 'changeAge', age: 30 });
当然啊,如果在框架中用的话,最好在销毁钩子中取消订阅,方法就是调用这个玩意儿。
5.动态生成action
如果有多个dispatch:
store.dispatch({ type: CHANGENAME, name: 'ht' });
store.dispatch({ type: CHANGEAGE, age: 30 });
store.dispatch({ type: CHANGEAGE, age: 40 });
store.dispatch({ type: CHANGEAGE, age: 50 });
store.dispatch({ type: CHANGEAGE, age: 60 });
store.dispatch({ type: CHANGEAGE, age: 70 });
那么每次dispatch都要传一个对象,然后写触发哪个action,要改什么,这真的是非常麻烦,所以最后一个关键文件就是:actionCreators.js
用来封装每个dispatch的值:
二、React中如何使用redux
1.安装redux
在目录下创建一个脚手架
create-react-app 项目名
安装redux,在脚手架目录下:
npm install redux 或 npm add redux
2.创建store
创建store文件夹,里面先来两个文件:index.js
和reducer.js
index.js
import {createStore} from 'redux';
import reducer from './reducer';let store = createStore(reducer);export default store;
reducer.js
let initialState = {counter: 1
}
function reducer(state = initialState, action) {switch(action.type) {default:return state;}
}export default reducer;
再创建两个空文件constants.js
和actionCreators.js
备用
3.组件中订阅store
1、组件中我们如果想要去用store中的数据,可以在state中直接通过store.getState().属性名
去读取一个初始值。但是如果store数据改变,这里不会跟着变。
2、如果想要store数据改变时组件能跟着变,那么我们需要在componentDidMount
钩子中去订阅store
,每次数据改变,就会执行回调,回调中我们调用setState
重新读取store,组件就能够实现和store数据的同步。
import React, { PureComponent } from 'react';
import store from './store';
export class App extends PureComponent {constructor() {super();this.state = {counter: store.getState().counter}}componentDidMount() {//订阅storestore.subscribe(() => {// console.log(store.getState());this.setState({counter: store.getState().counter})})}render() {let { counter } = this.state;return (<div><h2>{counter}</h2></div>)}
}export default App;
4.派发action修改store数据
比如我们定义一个按钮用来控制store
中的counter+1
,那么:
1、回到第一步的空文件constants.js
中,我们来用变量存储action
的名字
export const CHANGECOUNTER = 'changeCounter';
2、回到空文件actionCreators.js
中,我们定义函数来格式化action
对象
import {CHANGECOUNTER} from './constants'
export const change = (num) => {return {type: CHANGECOUNTER,num: num}
}
3、再回到组件中:
当我们点击的时候就派发action,action对象由上面定义好的函数来生成。
import React, { PureComponent } from 'react';
import store from './store';
import {change} from './store/actionCreators'
export class App extends PureComponent {constructor() {super();this.state = {counter: store.getState().counter}}componentDidMount() {//订阅storestore.subscribe(() => {// console.log(store.getState());this.setState({counter: store.getState().counter})})}change(num) {store.dispatch(change(num));}render() {let { counter } = this.state;return (<div><h1>App</h1><h2>{counter}</h2><button onClick={() => this.change(1)}>点击+1</button><Son /></div>)}
}export default App;
使用流程大概就是这样。
三、第三方库react-redux简化代码
这个库是专门用于简化在react中使用redux的。
安装:
npm install react-redux
1.省掉手动订阅的步骤
这里是react-redux的使用步骤
在src的index.js中引入Provider,包裹App组件并传入我们定义好的store,这样的话后代组件就可以访问store
定义一个子组件Son,引入connect
函数,该函数返回一个高阶组件,高阶组件本身是一个函数,调用该函数可以给Son注入东西。connect
的参数是一个回调mapStateToProps
,顾名思义就是把store
中的state
通过高阶组件注入到Son的props
中,具体代码如下:
import React, { PureComponent } from 'react';
import store from '../store';
import {connect} from 'react-redux';export class Son extends PureComponent {render() {let { Soncounter } = this.props;return (<div><h1>Son</h1><h2>{Soncounter}</h2></div>)}
}//调用connect返回一个高阶组件(函数)
//高阶组件中传入我们要注入store的组件
const mapStateToProps = (state) => {//返回一个对象,这样可以读到store中state的数据,并把这个数据// 通过connect()返回的高阶组件注入到Son的props中return {//注入给Son的props.SoncounterSoncounter: state.counter }
}
export default connect(mapStateToProps)(Son);
2.同步派发action
connect的第二个参数也是一个函数mapDispatchToProps
,参数是一个dispatch函数。
mapDispatchToProps
可以向组件中注入一些函数,这些函数是用来派发action的。
组件可以通过props接收函数并调用传参,把要修改的值传过去,然后通知仓库修改数据。
3.redux如何进行异步操作
先安装axios
npm install axios
(1)在组件中派发异步action
如果我们要在组件中异步请求数据,然后把数据保存到store中,按照react-redux
库的步骤来说是这样的:
1、constants.js
中定义action
的名字:
2、actionCreators.js
中定义创建action对象的函数:
3、reducer.js
中定义接收数据后对应的更改操作
4、去组件中的componentDidMount中发送网络请求并通知store更新数据。
(2)在actionCreators中派发异步action
如果发送请求的组件根本不用这个数据呢?发请求只是为了把数据存到store中,那这样写在挂载钩子里就不太好。所以我们可以把异步的过程放到redux中去处理。
这里我们用到一个中间件redux-thunk:
npm install redux-thunk
这个中间件可以让dispatch的参数是一个函数的调用
,那么我们可以在这个函数里进行异步操作,得到结果后就再执行dispatch传入action对象
。
使用步骤:
1、在store/index.js
中传入createStore的第二个参数,意思是使用中间件,参数传我们刚才安装的thunk。这样的话dispatch就可以派发一个函数了。
2、在actionCreators.js
中把之前的函数返回值从一个action对象换成一个函数,在函数中请求到数据后再次dispatch一个action对象:{type: xxx, newsList: red.data}
3、回到组件中,直接把dispatch改成一个函数的调用
ok,异步请求数据就是这么搞的。
4.redux的devtools怎么打开
安装直接去浏览器插件库里一搜就有。
在开发环境下如果想要打开,需要在store/index.js
中:
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;let store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));export default store;
当然这是由中间件thunk的情况下,其他情况请见github:
https://github.com/zalmoxisus/redux-devtools-extension
四、redux模块化
类似vuex中的namespace,不过比那个麻烦一些。
👇👇👇👇👇👇
除了把每部分的数据拆开,每个小仓库的index.js
只需要把reducer函数导出
import reducer from './reducer';
export default reducer;
然后在大仓库中,通过combineReducers
把小仓库的reducer
合并成一个大reducer
并给每个小仓库起个名字。
其他需要注意的就是路径的问题了。还有就是读取仓库数据的时候,需要加上小仓库的名字:
五、Redux ToolKit的使用
Redux ToolKit 中文官方文档
安装:npm install @reduxjs/toolkit
。
如果没安react-redux
也要带着一起安装:npm install react-redux @reduxjs/toolkit
1.三个核心的API
configureStore: 包装旧的createStore
以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer
,添加你提供的任何 Redux 中间件,redux-thunk默认包含
(这个东西就是让dispatch可以是一个函数的调用的中间件),并启用 Redux DevTools Extension
(通过某个地方写api可以直接设置devtool是否启用,不用我们再window.xxxxx了)。
createSlice: 接受reducer函数
的对象、切片名称
和初始状态值
,并自动生成切片reducer
,并带有相应的actions
。
createAsyncThunk: 接受一个动作类型字符串
和一个可以写异步的函数
,并生成一个包含pending/fulfilled/rejected
的对象,我们可以通过某种方式监听状态的改变。
2.用更简洁的代码创建store
(1)configureStore创建大仓库store
在store/index.js
中,我们先创建一个store
import {configureStore} from '@reduxjs/toolkit';const store = configureStore({reducer: {待填写......},devTools: true, //默认就是true
})export default store;
传入的配置对象中,有如下三个常见的参数:
-
reducer:将slice中的reducer可以组成一个对象传入此处;
-
middleware:可以使用参数,传入其他的中间件(自行了解);
-
devTools:是否配置devTools工具,默认为true;
(2)createSlice创建小仓库
一般我们会在store/modules
里创建小仓库:
比如login.js
:
import {createSlice} from '@reduxjs/toolkit';const loginSlice = createSlice({name:'login',initialState: {counter: 99},//相当于reducer函数reducers: {//相当于每个caseaddNumber(state, action) {//这里可以直接改不用浅拷贝,默认给你拷贝好了state.counter = action.payload;}}
})
//reducers里的方法会自动放在counterSlice的actions属性中,用于派发action
export const { addNumber } = loginSlice.actions;
//注意导出的是reducer,用于在index.js中对reducer进行组合
export default loginSlice.reducer
在createSlice
的配置对象中,一般有如下几个配置项:
-
name:用户标记slice的名词,在之后的redux-devtool中会显示对应的名词;
-
initialState:初始化值,第一次初始化数据时的值;
-
reducers:相当于之前的reducer函数,是对象类型,对象中可以添加很多的函数;
函数类似于redux原来reducer中的一个case语句
,该函数的参数如下:
参数一:
state
, 当前的state状态
参数二:传递的actions参数
, actions有两个属性,一个是自动生成的type
, 另一个是传递的参数放在payload中
;
createSlice返回值是一个对象,包含所有的actions
,通常我们会把每个action暴露出去;
(3)将小仓库添加到大仓库中
回到index.js
,把创建的login
的reducer
函数导出来,添加到大仓库中
import {configureStore} from '@reduxjs/toolkit';
//导入reducer
import loginReducer from './modules/login';const store = configureStore({reducer: {login: loginReducer,},devTools: true, //默认就是true
})export default store;
这样的话, redux就搭建完成了,接下来我们去组件里使用
3.组件中使用和修改仓库的数据
这里要用到react-redux这个库,前边已经安装过了,需要配置的地方和之前差不多:
点击查看如何建立redux和react的连接
这样我们来到logIn.jsx
组件中,就可以去使用仓库中的counter
:
使用方式还是没什么太大区别的嗷。修改的话也是一样,看上面那个图,其实也是通过connect
把dispatch
的方法注入props
,然后调用派发action
。
这个React ToolKit
在这里简化了哪些东西呢?我感觉是简化了我们自己生成action
对象的操作,还有用变量存储type
名字的操作,以前我们需要很繁琐的操作(先生成action对象再派发)。
但是现在!我们派发可以直达小仓库的这个函数(可以直接导入进来)
import {createSlice} from '@reduxjs/toolkit';const loginSlice = createSlice({name:'login',initialState: {counter: 99},//相当于reducer函数reducers: {//相当于每个caseaddNumber(state, action) {console.log(action);//{type: 'login/addNumber', payload: 1}//这里可以直接改不用浅拷贝,默认给你拷贝好了state.counter = action.payload;}}
})
//reducers里的方法会自动放在counterSlice的actions属性中,用于派发action
export const { addNumber } = loginSlice.actions;
//注意导出的是reducer,用于在index.js中对reducer进行组合
export default loginSlice.reducer
import { addNumber } from '../store/modules/login';
而且输出aciton会发现type
是自动生成了个东西login/addNumber
,不用再担心自己会写错名字了。传过来的数值1存到payload
中。
最关键的!我们可以直接通过state
参数去修改仓库数据,不用再向之前一样先浅拷贝再修改了(底层做了处理),确实方便了许多。
4.RTK的异步请求写在哪里
可以像以前一样,写在挂载钩子中,写法一样,但是还是那个意思,组件里发请求不太好。所以RTK给我们封装的话我们异步在redux中如何写?
像上面一样,创建另一个小仓库home
这里就要用到第三个API:createAsyncThunk
,调用它返回一个函数,我们把函数暴露出去,在组件中引入并在dispatch
中传入该函数的调用
,即可回来执行函数的第二个参数(发请求的回调)
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';
import axios from 'axios';//异步操作的处理
export const reqNewsList = createAsyncThunk('reqData', async () => {const result = await axios.get('https://ku.qingnian8.com/dataApi/news/newslist.php')return result.data;
})const homeSlice = createSlice({name: 'home',initialState: {newsList: ['无数据'],},reducers: {getNewsList(state, {payload}) {console.log(payload);state.newsList = payload;}},//监听异步的状态变化extraReducers: {[reqNewsList.pending](state, {payload}) {console.log('异步处于pending状态');},[reqNewsList.fulfilled](state, {payload}) {console.log(payload);state.newsList = payload;},[reqNewsList.rejected](state, {payload}) {console.log('异步处于rejected状态,有错误!');},}
})export const {getNewsList} = homeSlice.actions;
export default homeSlice.reducer;
异步请求到数据后,把数据返回出来,我们通过createSlice配置对象中另一个配置项extraReducers可以监听异步请求的变化:
- 如果请求中就调用pending
- 请求到了就调用fulfilled并把返回值用payload接收,我们就可以拿到异步请求的数据了;
- 如果有错误,就会执行rejected的回调。
组件:
搞定
相关文章:

React(六):Redux的使用、react-redux简化代码、redux模块化、RTK的使用
React(六)一、Redux测试项目搭建1.创建store仓库2.创建reducer函数(纯函数)3.constants.js保存action名字4.修改store中的数据5.动态生成action二、React中如何使用redux1.安装redux2.创建store3.组件中订阅store4.派发action修改…...

静态库和动态库的打包与使用
静态库和动态库 静态库和动态库的打包 生成可执行程序时链接使用 运行可执行程序时加载使用 提前声明,笔者示例的文件有mian.c/child.c/child.h。OK,我们先了解一下,库文件是什么?它其实就是打包了一堆实现常用功能的代码文件. ⭐…...

h264编码之SPS解析
一、概念 SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。 二、定义 H.264标准协议中规定的SPS格式位于文档的7.3.2.1.1,如下图所示: 1、profile_idc 根据《T-REC-H.264-2…...

使用R语言包clusterProfiler做KEGG富集分析时出现的错误及解决方法
使用enrichKEGG做通路富集分析时,一直报错:显示No gene can be mapped....k <- enrichKEGG(gene gene, organism "hsa", pvalueCutoff 1, qvalueCutoff 1)但是之前用同样的基因做分析是能够成功地富集到通路,即便是网上的数据…...

框架——MyBatis的入门案例
框架概述1.1什么是框架框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交与的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义…...

hadoop兼容性验证
前言 Hadoop是一个由Apache基金会所开发的分布式系统基础架构,主要解决海量数据的存储和海量数据的分析计算问题,广义上来说,Hadoop通常是指一个更广泛的概念–hadoop生态圈 Hadoop优缺点: 优点: 1、高可靠性&#x…...

运维提质增效,有哪些办法可以做
凡是代码,难免有 bug。 开发者们的日常,除了用一行行代码搭产品外,便是找出代码里的虫,俗称 debug。 随着移动互联网的快速发展,App 已经成为日常生活中不可或缺的一部分。但是在开发者/运维人员的眼里简直就是痛苦的…...
c++基础——结构体
结构体结构体(struct),可以看做是一系列称为成员元素的组合体。可以看做是自定义的数据类型。定义结构体struct abc {int x;int y; } e[array_length];const abc a; abc b, B[array_length], tmp; abc *c;上例中定义了一个名为 abc 的结构体&…...

applicationContext相关加载
spring refresh 概述 refresh是一个方法,spring中所有的ApplicationContext容器都需要通过refresh方法初始化; 处理步骤 其中refresh方法包含12个主要的处理步骤: 1、第1个步骤做前置准备 2、第2~6步骤创建BeanFactory(Appl…...

数据同步工具Sqoop
大数据Hadoop之——数据同步工具SqoopSqoop基本原理及常用方法 1 概述 Apache Sqoop(SQL-to-Hadoop)项目旨在协助RDBMS(Relational Database Management System:关系型数据库管理系统)与Hadoop之间进行高效的大数据交…...
Kafka 版本
kafka-2.11-2.1.1 : Kafka 1.0.0 后,Kafka 版本命名规则从 4 位到 3 位Kafka版本号是 2.1.1前 2 : 大版本号 (MajorVersion)中 1 : 小版本号或次版本号 (Minor Version)后 1 : 修订版本号 (Patch) Kafka 0.7 最早开源版本 : 只提供最基础的消息队列功…...

ElasticSearch 在Java中的各种实现
ES JavaAPI的相关体系: 词条查询 所谓词条查询,也就是ES不会对查询条件进行分词处理,只有当词条和查询字符串完全匹配时,才会被查询到。 等值查询-term 等值查询,即筛选出一个字段等于特定值的所有记录。 【SQL】 s…...

SpringBoot整合Knife4j
文章目录前言一、Knife4j是什么?二、使用步骤1.导入依赖2.编写配置文件3.编写controller和实体类4.测试总结前言 接上篇整合Swagger链接奉上http://t.csdn.cn/9mXSu 一、Knife4j是什么? 官方文档:https://doc.xiaominfo.com/ knife4j可以理解…...

MyISAM和InnoDB存储引擎的区别
目录前言存储引擎区别事务外键表单的存储数据查询效率数据更新效率如何选择前言 MyISAM和InnoDB是使用MySQL最常用的两种存储引擎,在5.5版本之前默认采用MyISAM存储引擎,从5.5开始采用InnoDB存储引擎。 存储引擎 存储引擎是:数据库管理系统…...
SpringMVC自定义处理多种日期格式的格式转换器
package cn.itcast.utils;import org.springframework.core.convert.converter.Converter;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;/*** 把字符串转换日期*/public class StringToDateConverter implements Converter<String…...

NYUv2生成边界GT(1)
看了cityscape和NYUv2生成边界GT的代码后,因为自己使用的是NYUv2数据集,所以需要对自己的数据集进行处理。CASENet生成边界GT所使用的代码是MATLAB,所以又重新看了一下MATLAB的代码,并进行修改,生成了自己的边界代码。…...

Spring基本概念与使用
文章目录一、Spring概念1.容器2.IoC3.DI4.Ioc与DI的关系二、Spring创建与使用1.Maven2.添加Spring框架支持注:国内的Maven源配置3.简单实例(1)创建一个Bean对象。(2)将Bean对象存储到Spring当中(3ÿ…...

安恒信息java实习面经
目录1.Java ME、EE、SE的区别,Java EE相对于SE多了哪些东西?2.jdk与jre的区别3.说一下java的一些命令,怎么运行一个jar包4.简单说一下java数据类型及使用场景5.Map跟Collection有几种实现?6.面向对象的特性7.重载和重写的区别8.重…...

第八章:枚举类与注解
第八章:枚举类与注解 8.1:枚举类的使用 类的对象只有有限个,确定的。我们称此类为枚举类。当需要定义一组常量是,强烈建议使用枚举类。如果枚举类中只有一个对象,则可以作为单例模式的实现方式。 如何定义枚举类 …...

Ceph介绍
分布式存储概述 常用的存储可以分为DAS、NAS和SAN三类 DAS:直接连接存储,是指通过SCSI接口或FC接口直接连接到一台计算机上,常见的就是服务器的硬盘NAS:网络附加存储,是指将存储设备通过标准的网络拓扑结构ÿ…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...