【React系列】Redux(一)管理状态

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)

在React的开发过程中,Redux对于我们是非常重要的。
但是对于很多人来说,初次接触redux会感觉redux的学习坡度非常陡峭,难度比较大。
在接下来,我会通过一个系列的方式循循渐进、层层深入的方式来讲解Redux:从redux的独立使用过程、到redux的结构划分、到redux结合react的过程、到redux中异步操作、到复杂数据的结构处理、到结合immutable的使用。
整个过程,都是在现有知识的基础之上层层递增、一步步掌握redux的使用!

一. 认识Redux

1.1. JavaScript纯函数

函数式编程中有一个概念叫纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念;

在React中,纯函数的概念非常重要,在接下来我们学习的Redux中也非常重要,所以我们有必须来回顾(如果你之前没有学过,那么你就是学习)一下纯函数。

纯函数的维基百科定义:

在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数:

  • 此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。

当然上面的定义会过于的晦涩,所以我简单总结一下:

  • 确定的输入,一定会产生确定的输出;
  • 函数在执行过程中,不能产生副作用;

那么我们来看几个函数是否是纯函数:

案例一:

function sum(num1, num2) {return num1 + num2;
}
  • 很明显,上面的函数是一个纯函数;
  • 它的输出是依赖我们的输入内容,并且中间没有产生任何副作用;

案例二:

let foo = 5;function add(num) {return foo + num;
}console.log(add(5));
foo = 10;
console.log(add(5));
  • add函数不是一个纯函数;
  • 函数依赖一个外部的变量,变量发生改变时,会影响:确定的输入,产生确定的输出;
  • 能否改进成纯函数呢?const foo = 5; 即可

案例三:

function printInfo(info) {console.log(info.name, info.age);info.name = "哈哈哈";
}
  • printInfo不是一个纯函数;
  • 虽然无论输入什么,最终输出都是undefined,但是它产生了副作用,修改了传入的对象;

当然纯函数还有很多的变种,但是我们只需要理解它的核心就可以了。

为什么纯函数在函数式编程中非常重要呢?

  • 因为你可以安心的写安心的用
  • 你在写的时候保证了函数的纯度,只是实现自己的业务逻辑即可,不需要关心传入的内容或者依赖其他的外部变量;
  • 你在用的时候,你确定你的输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出;

React 非常灵活,但它也有一个严格的规则:

  • 所有的 React 组件(无论是函数组件还是class声明的组件),都必须像纯函数一样,保护它们的props不被修改。

在之后学习redux中,reducer也被要求是一个纯函数。

1.2. 认识Redux

1.2.1. 为什么需要redux?

JavaScript开发的应用程序,已经变得越来越复杂了:

  • JavaScript需要管理的状态越来越多,越来越复杂;
  • 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些UI的状态,比如某些元素是否被选中,是否显示加载动效,当前分页;

管理不断变化的state是非常困难的:

  • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也有可能会引起状态的变化;
  • 当应用程序复杂时,state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪;

React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:

  • 无论是组件定义自己的state,还是组件之间的通信通过props进行传递;也包括通过Context进行数据之间的共享;
  • React主要负责帮助我们管理视图,state如何维护最终还是我们自己来决定;

在这里插入图片描述

Redux就是一个帮助我们管理State的容器:Redux是JavaScript的状态容器,提供了可预测的状态管理;

Redux除了和React一起使用之外,它也可以和其他界面库一起来使用(比如Vue),并且它非常小(包括依赖在内,只有2kb)

1.2.2. redux的核心理念

Redux的核心理念非常简单。

比如我们有一个朋友列表需要管理:

const initialState = {friends: [{ name: "why", age: 18 },{ name: "kobe", age: 40 },{ name: "lilei", age: 30 },]
};

如果我们没有定义统一的规范来操作这段数据,那么整个数据的变化就是无法跟踪的;

  • 比如页面的某处通过friends.push的方式增加了一条数据;
  • 比如另一个页面通过friends[0].age = 25修改了一条数据;
  • 整个应用程序错综复杂,当出现bug时,很难跟踪到底哪里发生的变化;

Redux要求我们通过action来更新数据:

  • 所有数据的变化,必须通过派发(dispatch)action来更新;
  • action是一个普通的JavaScript对象,用来描述这次更新的typecontent

比如下面就是几个更新friendsaction

const action1 = { type: "ADD_FRIEND", info: { name: "lucy", age: 20 } }
const action2 = { type: "INC_AGE", index: 0 }
const action3 = { type: "CHANGE_NAME", playload: { index: 0, newName: "coderwhy" } }
  • 强制使用action的好处是可以清晰的知道数据到底发生了什么样的变化,所有的数据变化都是可跟追、可预测的;
  • 当然,目前我们的action是固定的对象,真实应用中,我们会通过函数来定义,返回一个action

但是如何将stateaction联系在一起呢?答案就是reducer

  • reducer是一个纯函数;
  • reducer做的事情就是将传入的stateaction结合起来生成一个新的state
function reducer(state = initialState, action) {switch (action.type) {case "ADD_FRIEND":return { ...state, friends: [...state.friends, action.info] }case "INC_AGE":return {...state, friends: state.friends.map((item, index) => {if (index === action.index) {return { ...item, age: item.age + 1 }}return item;})}case "CHANGE_NAME":return {...state, friends: state.friends.map((item, index) => {if (index === action.index) {return { ...item, name: action.newName }}return item;})}default:return state;}
}

这个reducer有点像 Android 的 HandlerhandleMessage() 方法中根据不同的消息命令进行分发处理的逻辑。

1.2.3. redux的三大原则

1. 单一数据源

整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中:

  • Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;
  • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;

2. State是只读的

唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State

  • 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state
  • 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;

3. 使用纯函数来执行修改

通过 reducerstateactions联系在一起,并且返回一个新的State

  • 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;
  • 但是所有的reducer都应该是纯函数,不能产生任何的副作用;

二. redux的基本使用

2.1. redux使用过程

安装redux

npm install redux --save
# 或
yarn add redux

这里,我通过创建一个简单的js文件,我们先来简单学习一下redux

搭建项目结构

  1. 创建一个新的项目文件夹:learn-redux
# 执行初始化操作
yarn init# 安装redux
yarn add redux
  1. 创建src目录,并且创建index.js文件

  2. 修改package.json可以执行index.js

"scripts": {"start": "node src/index.js"
}

在这里插入图片描述

开始在index.js中编写代码

  1. 创建一个对象,作为我们要保存的状态:
const redux = require('redux');const initialState = {counter: 0
}
  1. 创建Store来存储这个state
  • 创建store时必须创建reducer
  • 我们可以通过 store.getState 来获取当前的state
// 创建reducer
const reducer = (state = initialState, action) => {return state;
}// 根据reducer创建store
const store = redux.createStore(reducer);
console.log(store.getState());
  1. 通过action来修改state
  • 通过dispatch来派发action
  • 通常action中都会有type属性,也可以携带其他的数据;
store.dispatch({type: "INCREMENT"
})store.dispath({type: "DECREMENT"
})store.dispatch({type: "ADD_NUMBER",number: 5
})
  1. 修改reducer中的处理代码
  • 这里一定要记住,reducer是一个纯函数,不需要直接修改state
  • 后面我会讲到直接修改state带来的问题;
const reducer = (state = initialState, action) => {switch (action.type) {case "INCREMENT":return {...state, counter: state.counter + 1};case "DECREMENT":return {...state, counter: state.counter - 1};case "ADD_NUMBER":return {...state, counter: state.counter + action.number}default: return state;}
}
  1. 可以在派发action之前,监听store的变化:
store.subscribe(() => {console.log(store.getState());
})

完成的案例代码如下:

const redux = require('redux');const initialState = {counter: 0
}// 创建reducer
const reducer = (state = initialState, action) => {switch (action.type) {case "INCREMENT":return {...state, counter: state.counter + 1};case "DECREMENT":return {...state, counter: state.counter - 1};case "ADD_NUMBER":return {...state, counter: state.counter + action.number}default: return state;}
}// 根据reducer创建store
const store = redux.createStore(reducer);store.subscribe(() => {console.log(store.getState());
})// 修改store中的state
store.dispatch({type: "INCREMENT"
})
// console.log(store.getState());store.dispatch({type: "DECREMENT"
})
// console.log(store.getState());store.dispatch({type: "ADD_NUMBER",number: 5
})
// console.log(store.getState());

2.2. redux结构划分

如果我们将所有的逻辑代码写到一起,那么当redux变得复杂时代码就难以维护。

接下来,我会对代码进行拆分,将store、reducer、action、constants拆分成一个个文件。

注意:node中对ES6模块化的支持

目前我使用的node版本是v12.16.1,从node v13.2.0开始,node才对ES6模块化提供了支持:

node v13.2.0之前,需要进行如下操作:

  • package.json中添加属性:"type": "module"
  • 在执行命令中添加如下选项:node --experimental-modules src/index.js;

node v13.2.0之后,只需要进行如下操作:

  • package.json中添加属性:"type": "module"

注意:导入文件时,需要跟上.js后缀名;

redux结构进行划分

创建store/index.js文件:

import redux from 'redux';
import reducer from './reducer.js';const store = redux.createStore(reducer);export default store;

创建store/reducer.js文件:

import {ADD_NUMBER,SUB_NUMBER
} from './constants.js';const initialState = {counter: 0
}function reducer(state = initialState, action) {switch(action.type) {case ADD_NUMBER:return {...state, counter: state.counter + action.num};case SUB_NUMBER:return {...state, counter: state.counter - action.num};default:return state;} 
}export default reducer;

创建store/actionCreators.js文件:

import {ADD_NUMBER,SUB_NUMBER
} from './constants.js'const addAction = (count) => ({type: ADD_NUMBER,num: count
});const subAction = (count) => ({type: SUB_NUMBER,num: count
})export {addAction,subAction
}

创建store/constants.js文件:

const ADD_NUMBER = "ADD_NUMBER";
const SUB_NUMBER = "SUB_NUMER";export {ADD_NUMBER,SUB_NUMBER
}

2.3. Redux流程图

我们已经知道了redux的基本使用过程,那么我们就更加清晰来认识一下redux在实际开发中的流程:

  1. 全局通常只有一个Store,存储我们的State
  2. Component中在某些情况会派发Action(这些Action是我们提前定义好的);
  3. Reducer会接收到这些Action,并且在Reducer中会返回一个新的State,作为StoreState
  4. State发生更新之后会触发通知,告知订阅者数据发生了改变;
  5. 订阅者拿到最新的数据(在props中),更新到jsx中,界面发生改变;

在这里插入图片描述

在这里插入图片描述

三. react结合redux

3.1. redux融入react代码

目前redux在react中使用是最大的,所以我们需要将之前编写的redux代码,融入到react当中去。

这里我创建了两个组件:

  • Home组件:其中会展示当前的counter值,并且有一个+1和+5的按钮;
  • Profile组件:其中会展示当前的counter值,并且有一个-1和-5的按钮;
    在这里插入图片描述

home.js代码实现:

import React, { PureComponent } from 'react';import store from '../store';
import {addAction
} from '../store/actionCreators';export default class Home extends PureComponent {constructor(props) {super(props);this.state = {counter: store.getState().counter}}componentDidMount() {store.subscribe(() => {this.setState({counter: store.getState().counter})})}render() {return (<div><h1>Home</h1><h2>当前计数: {this.state.counter}</h2><button onClick={e => this.increment()}>+1</button><button onClick={e => this.addCounter()}>+5</button></div>)}increment() {store.dispatch(addAction(1));}addCounter() {store.dispatch(addAction(5));}
}

Profile.js代码实现:

import React, { PureComponent } from 'react';import store from '../store';
import {subAction
} from '../store/actionCreators';export default class Profile extends PureComponent {constructor(props) {super(props);this.state = {counter: store.getState().counter}}componentDidMount() {store.subscribe(() => {this.setState({counter: store.getState().counter})})}render() {return (<div><hr/><h1>Profile</h1><div><h2>当前计数: {this.state.counter}</h2><button onClick={e => this.decrement()}>-1</button><button onClick={e => this.subCounter()}>-5</button></div></div>)}decrement() {store.dispatch(subAction(1));}subCounter() {store.dispatch(subAction(5));}
}

上面的代码其实非常简单,核心代码主要是两个:

  • componentDidMount 中定义数据的变化,当数据发生变化时重新设置 counter;
  • 在发生点击事件时,调用storedispatch来派发对应的action

不要忘记取消订阅:

在这里插入图片描述

3.2. 自定义 connect 函数

上面的代码是否可以实现react组件和redux结合起来呢?

  • 当然是可以的,但是我们会发现每个使用的地方其实会有一些重复的代码:
  • 比如监听store数据改变的代码,都需要在 componentDidMount中完成;
  • 比如派发事件,我们都需要去先拿到 store, 在调用其 dispatch 等;

能否将这些公共的内容提取出来呢?

我们来定义一个connect函数:

这个connect函数本身接受两个参数:

  • 参数一:里面存放 component 希望使用到的 State 属性;
  • 参数二:里面存放 component 希望使用到的 dispatch动作;

这个connect函数有一个返回值,是一个高阶组件:

  • constructor中的state中保存一下我们需要获取的状态;
  • componentDidMount中订阅store中数据的变化,并且执行 setState操作;
  • componentWillUnmount中需要取消订阅;
  • render函数中返回传入的WrappedComponent,并且将所有的状态映射到其props中;
  • 这个高阶组件接受一个组件作为参数,返回一个class组件;
  • 在这个class组件中,我们进行如下操作:
import React, { PureComponent } from "react";import store from '../store';export default function connect(mapStateToProps, mapDispatchToProps) {return function handleMapCpn(WrappedComponent) {return class extends PureComponent {constructor(props) {super(props);this.state = {storeState: mapStateToProps(store.getState())}}componentDidMount() {this.unsubscribe = store.subscribe(() => {this.setState({storeState: mapStateToProps(store.getState())})})}componentWillUnmount() {this.unsubscribe();}render() {return <WrappedComponent {...this.props} {...mapStateToProps(store.getState())}{...mapDispatchToProps(store.dispatch)}/>}}}
}

homeprops文件中,我们按照自己需要的state、dispatch来进行映射:

比如home.js中进行如下修改:

const mapStateToProps = state => {return {counter: state.counter}
}const mapDispatchToProps = dispatch => {return {addNumber: function(number) {dispatch(addAction(number));}}
}export default connect(mapStateToProps, mapDispatchToProps)(Home);
  • mapStateToProps:用于将state映射到一个对象中,对象中包含我们需要的属性;
  • mapDispatchToProps:用于将dispatch映射到对象中,对象中包含在组件中可能操作的函数;当调用该函数时,本质上其实是调用dispatch(对应的Action)

Profile中也是类似的操作。

有了connect函数,我们之后只需要关心从statedispatch中映射自己需要的状态和行为即可;

3.3. store的context处理

但是上面的connect函数有一个很大的缺陷:依赖导入的store

如果我们将其封装成一个独立的库,需要依赖用于创建的store,我们应该如何去获取呢?
难道让用户来修改我们的源码吗?不太现实;

正确的做法是我们提供一个ProviderProvider来自于我们创建的Context,让用户将store传入到value中即可;

创建一个context.js文件:

import { createContext } from 'react';export const StoreContext = createContext();

修改connect函数中class组件部分的代码:

  • 注意下面我们将class组件的名称明确的定义出来,并且给它的contextType进行了赋值;
  • 在组件内部用到store的地方,统一使用this.context代替(注意:constructor中直接使用第二个参数即可)
import React, { PureComponent } from "react";import { StoreContext } from './context';export default function connect(mapStateToProps, mapDispatchToProps) {return function handleMapCpn(WrappedComponent) {class ConnectCpn extends PureComponent {constructor(props, context) {super(props);this.state = {storeState: mapStateToProps(context.getState())}}componentDidMount() {this.unsubscribe = this.context.subscribe(() => {this.setState({storeState: mapStateToProps(this.context.getState())})})}componentWillUnmount() {this.unsubscribe();}render() {return <WrappedComponent {...this.props}{...mapStateToProps(this.context.getState())}{...mapDispatchToProps(this.context.dispatch)} />}}ConnectCpn.contextType = StoreContext;return ConnectCpn;}
}

在入口的index.js中,使用Provider并且提供store即可:

import { StoreContext } from './utils/context';
import store from './store';ReactDOM.render(<StoreContext.Provider value={store}><App /></StoreContext.Provider>,document.getElementById('root')
);

测试代码,依然可以正常运行。

四. react-redux使用

4.1. react-redux的使用

开始之前需要强调一下,redux和react没有直接的关系,你完全可以在React, Angular, Ember, jQuery, or vanilla JavaScript中使用Redux。

尽管这样说,redux依然是和React或者Deku的库结合的更好,因为他们是通过state函数来描述界面的状态,Redux可以发射状态的更新,让他们作出相应。

虽然我们之前已经实现了connectProvider这些帮助我们完成连接reduxreact的辅助工具,但是实际上redux官方帮助我们提供了 react-redux 的库,可以直接在项目中使用,并且实现的逻辑会更加的严谨和高效。

安装react-redux

yarn add react-redux

使用connect函数:

  • 将之前使用的connect函数,换成react-reduxconnect函数;
import React, { PureComponent } from 'react';
import { connect } from "react-redux";// import connect from '../utils/connect2';export default connect(mapStateToProps, mapDispatchToProps)(Home);

使用Provider

  • 将之前自己创建的ContextProvider,换成react-reduxProvider组件:
  • 注意:这里传入的是store属性,而不是value属性(待会儿可以在源码中查看);
import { Provider } from 'react-redux';import store from './store';ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);

2.2. react-redux的源码

这里我简单带着大家看一下react-redux的源码:

  • 但是第一因为这个教程不是讲源码为主的教程(穿插讲解部分源码),所以源码只会阅读核心的部分;
  • 另外我经常会说,整个社区在hooks出现后大量的库转向了hooks,所以在源码中会出现大量的hooks代码;
  • 因为目前还没有讲解hooks相关的API,所以某些hooks的作用在这里也不方便解释(可以学习完hooks之后再详细阅读);

首先,我们简单看一下Provider的源码:

在这里插入图片描述

  • 使用了一个useMemo来返回一个contextValue的对象;
  • 这里使用useMemo的原因是为了进行性能的优化;
  • 在依赖的store不改变的情况下,不会进行重新计算,返回一个新的对象;
  • 在下面的ContextProvider中就会将其赋值给value属性;

ReactReduxContext来自另外一个文件:

在这里插入图片描述

connect函数的依赖比较复杂:

  • 调用createConnect来返回一个connect函数:

在这里插入图片描述

createConnect函数的调用:

在这里插入图片描述

connect函数最终调用的是connectHOC

  • connectHOC其实是connectAdvanced的函数;
  • connectAdvanced函数最终返回的是wrapWithConnect函数;

在这里插入图片描述

wrapWithConnect函数:

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/230848.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年HCIE认证有什么用?华为HCIE好考吗?

随着信息技术的迅速发展&#xff0c;网络工程师的需求越来越高&#xff0c;而HCIE作为华为认证体系中的最高级别认证&#xff0c;备受从业者关注。本文将深入研究2024年HCIE认证的价值、考试难度以及报名费用等方面的信息。 2024年HCIE认证有什么用? 新的一年即将到来&#x…

jmeter关联依赖---三种

1.正则表达式提取器 2.xpath取样器 3.json提取器

听GPT 讲Rust源代码--compiler(11)

File: rust/compiler/rustc_mir_transform/src/simplify.rs 在Rust源代码中&#xff0c;rust/compiler/rustc_mir_transform/src/simplify.rs文件是Rust编译器中一系列进行MIR&#xff08;中间表示&#xff09;简化的转换的实现。MIR是Rust编译器中用于进行优化和代码生成的中间…

QT_02 窗口属性、信号槽机制

QT - 窗口属性、信号槽机制 1. 设置窗口属性 窗口设置 1,标题 2,大小 3,固定大小 4,设置图标在 widget.cpp 文件中&#xff1a; //设置窗口大小,此时窗口是可以拉大拉小的 //1参:宽度 //2参:高度 this->resize(800, 600); //设置窗口标题 this->setWindowTitle("…

2023 IoTDB Summit:清华大学软件学院长聘副教授龙明盛《IoTDB 新组件:内生机器学习》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

安全狗入选“2023年福建省信息技术应用创新解决方案”名单

近日&#xff0c;福建省数字福建建设领导小组办公室公布了2023年福建省信息技术应用创新解决方案入选项目名单。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借综合且具备突出创新水平的方案入选。 据悉&#xff0c;此次方案征集面向全省信创企业和用户单位&#xff0c;…

HarmonyOS4.0系统性深入开发14AbilityStage组件容器

AbilityStage组件容器 AbilityStage是一个Module级别的组件容器&#xff0c;应用的HAP在首次加载时会创建一个AbilityStage实例&#xff0c;可以对该Module进行初始化等操作。 AbilityStage与Module一一对应&#xff0c;即一个Module拥有一个AbilityStage。 DevEco Studio默…

勒索事件急剧增长,亚信安全发布《勒索家族和勒索事件监控报告》

近期(12.15-12.21)态势快速感知 近期全球共发生了247起攻击和勒索事件&#xff0c;勒索事件数量急剧增长。 近期需要重点关注的除了仍然流行的勒索家族lockbit3以外&#xff0c;还有本周top1勒索组织toufan。toufan是一个新兴勒索组织&#xff0c;本周共发起了108起勒索攻击&a…

电脑视频需要分屏怎么做

在当今数字时代&#xff0c;人们对于视频的需求越来越高。有时候&#xff0c;我们可能想在同一屏幕上同时播放多个视频&#xff0c;进行对比、观看、剪辑或者其他目的。那么&#xff0c;视频分屏应该怎么做呢&#xff1f; 在本篇文章中&#xff0c;我们将会详细的为你介绍视频分…

JS中模块的导入导出

背景 学习js过程中&#xff0c;发现导入导出有的是使用的export 导出&#xff0c;import导入&#xff0c;有的是使用exports或module.exports导出&#xff0c;使用require导入&#xff0c;不清楚使用场景和规则&#xff0c;比较混乱。 经过了解发现&#xff0c;NodeJS 中&…

JAVA基础学习笔记-day13-数据结构与集合源1

JAVA基础学习笔记-day13-数据结构与集合源1 1. 数据结构剖析1.1 研究对象一&#xff1a;数据间逻辑关系1.2 研究对象二&#xff1a;数据的存储结构&#xff08;或物理结构&#xff09;1.3 研究对象三&#xff1a;运算结构1.4 小结 2. 一维数组2.1 数组的特点 3. 链表3.1 链表的…

CSS免费在线字体格式转换器 CSS @font-face 生成器

今天竟意外发现的一款免费的“网页字体生成器”&#xff0c;功能强大又好用~ 工具地址&#xff1a;https://transfonter.org/ 根据你设置生成后的文件预览&#xff1a; 支持TTF、OTF、WOFF、WOFF2 或 SVG字体格式转换生成&#xff0c;每个文件最大15MB。转换完成以后还会生成一…

Spring Security 6.x 系列(14)—— 会话管理之源码分析

一、前言 在上篇 Spring Security 6.x 系列(13)—— 会话管理之会话概念及常用配置 Spring Security 6.x 系列(14)—— 会话管理之会话固定攻击防护及Session共享 中了清晰了协议和会话的概念、对 Spring Security 中的常用会话配置进行了说明,并了解会话固定攻击防护…

代表团坐车 - 华为OD统一考试

OD统一考试(B卷) 分值: 100分 题解: Java / Python / C++ 题目描述 某组织举行会议,来了多个代表团同时到达,接待处只有一辆汽车可以同时接待多个代表团,为了提高车辆利用率,请帮接待员计算可以坐满车的接待方案输出方案数量。 约束: 一个团只能上一辆车,并且代表团…

广播及代码实现

广播&#xff08;Broadcast&#xff09;是一种网络通信方式&#xff0c;它允许一台设备向网络中的所有其他设备发送消息。广播通常用于在网络上传递一些信息&#xff0c;让所有设备都能接收并处理。在广播中&#xff0c;通信的目标是整个网络而不是特定的单个设备。 向子网中…

[嵌入式AI从0开始到入土]9_yolov5在昇腾上推理

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第一章 昇腾Altas 200 DK上手 第二章 下载昇腾案例并运行 第三章…

力扣刷题-二叉树-构建树

106.从中序与后序遍历序列构造二叉树 根据一棵树的中序遍历与后序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如&#xff0c;给出 中序遍历 inorder [9,3,15,20,7] 后序遍历 postorder [9,15,7,20,3] 返回如下的二叉树&#xff1a; 思路 参考&#xff1a;…

AI:112-基于卷积神经网络的美食图片识别与菜谱推荐

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

python包chromadb安装失败总结

1&#xff0c;背景&#xff1a; 最近在学习langchain的课程&#xff0c;里面创建自己的知识库的Retrieval模块中&#xff0c;需要用到向量数据库。 所以按照官方的教程&#xff08;vectorstores&#xff09;&#xff0c;准备使用chroma的向量数据库。图片来源 2&#xff0c;问…

HCIA-Datacom题库(自己整理分类的)——其他网络协议【完】

&#xff08;一&#xff09;单选 下列属于链路状态协议的是? Direct static FTP OSPF 解析&#xff1a; FTP&#xff1a;文件传输协议 OSPF&#xff1a;链路状态路由协议 如下图所示的网络主机A通过Telnet登录到路由器A然后在远程的界面通过FTP获取路由器的配置文件&…