06-React组件 Redux React-Redux

React组件化(以Ant-Design为例)

组件化编程,只需要去安装好对应的组件,然后通过各式各样的组件引入,实现快速开发
我们这里学习的是 Ant-design (应该是这样),它有很多的组件供我们使用
在这里插入图片描述
引入各种组件,可以方便开发,省着自己去二次封装组件,同时也更好看了

在这里插入图片描述

快速起步

安装antd组件库

npm install antd

随便一个组件里面引入

import { Button } from "antd";class App extends React.Component {render() {<div><Flex gap="small" wrap="wrap"><Button type="primary">Primary Button</Button><Button>Default Button</Button><Button type="dashed">Dashed Button</Button><Button type="text">Text Button</Button><Button type="link">Link Button</Button></Flex></div>}
}
export default App;

运行一下
在这里插入图片描述

大概就是这么个东西,一定学会看官方文档,尤其这种组件类的,不看文档就啥也做不了
AntDesign官方文档

其他UI,但是是国外的
material-ui

大概就是这些东西,还有自定义主题的一些方法,因为每个版本的官方修改方式都不一样,所以建议现用现查

Redux

非必须学习的项目,但是如果项目里用了就得学

Redux简介

1.redux是一个专门用于做状态管理的JS库(不是react插件库,只是名字像)。
2.它可以用在react, angular, vue等项目中, 但基本与react配合使用。
3.作用: 集中式管理react应用中多个组件共享的状态。

也类似于VueX

什么时候需要用Redux

首先,Redux是在有很多很多组件的情况下,才有可能需要用到Redux的,如果是单个组件,就自然没有很复杂的传值需求了

Redux 适用于多交互、多数据源的场景。简单理解就是复杂

从组件角度去考虑的话,当我们有以下的应用场景时,我们可以尝试采用 Redux 来实现

  1. 某个组件的状态需要共享时
  2. 一个组件需要改变其他组件的状态时
  3. 一个组件需要改变全局的状态时

除此之外,还有很多情况都需要使用 Redux 来实现(还没有学 hook,或许还有更好的方法)

在这里插入图片描述

Redux工作流程

  • store

store 是 Redux 的核心,可以理解为是 Redux 的数据流向指挥,我们可以将任何我们想要存放的数据放在 store 中,在我们需要使用这些数据时,我们可以从中取出相应的数据。因此我们需要先创建一个 store ,在 Redux 中可以使用 createStore API 来创建一个 store

store是无法直接进行操作的,需要借助Redux进行操作
store是一个调度者,store是指挥者,不干活。需要任何操作,或者动作,都会去分发走,找到对应的担当来做。(如果越过了store,action直接去找reducers,就有点类似去餐厅点餐,不找前台点餐,直奔后厨要吃的😂)

  • action

actionstore 中唯一的数据来源,一般来说,我们会通过调用 store.dispatch 将 action 分发到 store

我们需要传递的 action 是一个对象,它必须要有一个 type 值(如果是初始化,就传@@init@@代表要初始化),data值在第一次传参的时候可以为undefined,然后由Reducers来初始化赋值。

  • reducers

在 Reducers 中,我们需要指定状态的操作类型(type),要做怎样的数据更新,因此这个类型是必要的。

因为Reducers中会有很多具体处理的Reducer,所以这里Reducers代表很多处理Reducer的集合。

reducer 会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state

另外,Reducer可以加工状态,加工的前提是有上一次的状态值,如果没有状态值就要初始化一个状态值。有了状态值之后就可以进行下一步的加工。

在这里插入图片描述

手写一个Redux精简版

这里只是一个简化,忽略了很多东西(比如Creators创建Action的过程),侧重于展示store,reducer之间的关系
store.js文件:
创建一个store的js,导出供外界使用

import { createStore } from "redux";
//引入为store服务的Reducer
import countReducer from "./count_reducer";
//手动创建一个store
const store = createStore(countReducer);//全局只暴露这一个store对象
export default store;

count_reducer.js文件:
我们创建专门计数的reducer(简单来说就是专门负责处理某件事的function),这里取名叫作count_reducer

所有的action通过dispatch传进去之后,类型,数据都托管于store这个中心,调用的组件只需要把 type,data传进去,剩下的只需要等着从store中获取结果即可!

/*作为一个reducer应该有如下功能Store传来(previousState,action),Reducer接收到参数做出判断,previousState判断是否是空,是空就得初始化。非空就按照action进行下一步操作action处理完之后,把处理好的newState(previousState处理之后的版本),返回给store,等待Store返回给React组件所以以上的这些操作,只能用function。所以Reducers里的Reducer本质就是函数动作完成后,将数据暂存给store,等待后续组件获取值即可
*/export default function countReducer(previousState, action) {// 从Action对象里传来的action对象,里面包含type和data,所以我们需要解构出来。等待后续处理const { type, data } = action;if (previousState === undefined) {// 如果之前的状态是空的,就初始化之后还给store中心// 或者可以这么写,给参数给默认值// function countReducer(previousState=0, action) {return 0;}// 判断传入功能类型switch (type) {case "add"://这里用原来的值+新传入的值,得到新值return previousState + data * 1;case "sub"://这里用原来的值-新传入的值,得到新值return previousState - data * 1;// 提一嘴,这里都是return,所以不用breakdefault:return previousState;}
}

Count.jsx文件:
调用方组件:

import React, { Component } from "react";
// 获取store,用于获取store中的状态
import store from "../redux/store";export default class Count extends Component {// 之前准备的state,存储值count 都不用存在了,因为已经托管给store了// 但是,自己组件内部的值其实是还需要放在state的,因为store只需要托管复杂共享的情况state = {// 这个实际上就不要了,因为保存在state中// countNumber: "",// 但是比如car这个属性,只有自己用,就没必要兜一圈放redux中了,放自己组件里就挺好car: "威朗Pro GS",};add = () => {const { value } = this.count;// store是分发中心,告诉reducer要干的事情,以及传入的数值// 我们只需要将 type(要做的事情),value(原始值) 告诉store 。让store去找对应的reducer操作即可// 对应的reducer拿到数值,做出判断以及相应动作处理数据,处理好之后return值在store中等待组件get// 但注意,仅仅调用dispatch是不够的,因为redux是第三方js。无法触发页面刷新// 所以需要检测Redux里状态改变时,就去调用render。这里用到了订阅subscribe// 你都不用自己再解构接收值,所有的函数处理,值存储,都在redux中做好了,只需要我们从store中get结果即可// 获取store的地方,因为被监听所以自动刷新了,触发renderstore.dispatch({ type: "add", data: value });store.subscribe(() => {// 只要Redux里状态改变时,就去调用render。这里用到了订阅subscribe。手动触发一下就行// 借助setState,传个空值进去就可以触发render重新渲染// 当然,我们也可以在index.js的根标签监听这个。监听整个App组件,利用diffing算法全部更新,避免性能下降this.setState({});});};sub = () => {const { value } = this.count;store.dispatch({ type: "sub", data: value });store.subscribe(() => {// 手动监听,触发页面重新渲染this.setState({});});};render() {console.log(store);return (<div>当前结果<h1>{store.getState()}</h1>选择内容<select ref={(c) => (this.count = c)}><option value={1}>1</option><option value={2}>2</option><option value={3}>3</option></select><button onClick={this.add}>+</button><button onClick={this.sub}>-</button></div>);}
}

总结一下精简案例

(1).去除Count组件自身的状态
(2).src下建立:-redux-store.js-count_reducer.js(3).store.js:1).引入redux中的createStore函数,创建一个store2).createStore调用时要传入一个为其服务的reducer3).记得暴露store对象(4).count_reducer.js:1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态2).reducer有两个作用:初始化状态,加工状态3).reducer被第一次调用时,是store自动触发的,传递的preState是undefined,传递的action是:{type:'@@REDUX/INIT_a.2.b.4}( _a.2.b.4是随机数,为了避免和自己写的type名有重合)(5).在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

完整版Redux

上面的案例没有写action的创建,所以这里补齐就成为了完整版。
该文件专门为Count组件需要用的Reducer去生成action对象,封装好type,这样在调用组件传一个data进来就可以了

新增文件:1.count_action.js 专门用于创建action对象2.constant.js 放置容易写错的type值

store.js文件:
创建一个store的js,导出供外界使用。这个和上面的没区别,这里不赘述了

count_action.js文件:

/*该文件专门为Count需要用的Reducer去生成action对象,封装好type,这样在调用组件传一个data进来就可以了
*/
import { ADD } from "./constant.js";// 注意,这里有个坑,就是我希望在这里返回对象的箭头函数,最外侧不能是花括号
// (data) => { type: ADD, data };  如果是这样,会把 type: ADD, data 这部分识别为函数体,就没法返回对象了
// 所以我们用括号给括起来,就自动返回对象了
export const createAddAction = (data) => ({ type: ADD, data });
// 这两种写法等价
export function createIncrementAction(data) {// 要返回Action对象(返回type和data数据)return { type: ADD, data };
}

count_reducer.js文件:
本案例中,主要是引入常量来表示Type,避免了容易拼错的问题

import { ADD, SUB } from "./constant.js";export default function countReducer(previousState, action) {// 从Action对象里传来的action对象,里面包含type和data,所以我们需要解构出来。等待后续处理const { type, data } = action;if (previousState === undefined) {// 如果之前的状态是空的,就初始化之后还给store中心// 或者可以这么写,给参数给默认值// function countReducer(previousState=0, action) {return 0;}// 判断传入功能类型switch (type) {case ADD://这里用原来的值+新传入的值,得到新值return previousState + data * 1;case SUB://这里用原来的值-新传入的值,得到新值return previousState - data * 1;// 提一嘴,这里都是return,所以不用breakdefault:return previousState;}
}

Count.jsx文件:
调用方组件,这里主要强调变化的部分,省略了部分代码:

import React, { Component } from "react";
// 传入action函数,我们只需要传入data即可
import { createAddAction } from "../redux/count_action";
// 获取store,用于获取store中的状态
import store from "../redux/store";export default class Count extends Component {...省略add = () => {const { value } = this.count;// 引入了action后,就不需要我们手动定义type了,在action中已经做好定义了,我们只需要传入data即可// store.dispatch({ type: "add", data: value });// 引入,并且直接用count_action所封装好的函数来传入参数store.dispatch(createAddAction(value * 1));store.subscribe(() => {// 监听,store值变化重新renderthis.setState({});});};sub = () => {省略...};render() {return (省略...);}
}

异步action版

action有两种类型,根据返回值类型来区分

  • action的值为Object,则为同步action(store可以直接接收处理)
//返回一个对象
export const createSubAction = (data) => ({ type: SUB, data });
  • action的值为function,则为异步action(只有function才能开启异步任务,并且,store不可以直接接收处理异步action,需要通过中间件处理后才能接收
export const createAddAsync = (data,time) => {return ()=>{setTimeout(() => {...省略}, time);}
};

之前的异步add方法,其本质还是在组件里写的,可以看到,等待的异步过程没有在Redux里面写

addAsync = () => {const { value } = this.count;setTimeout(() => {store.dispatch(createAddAction(value * 1));}, 5000);
};

我们现在要把异步等待的操作,放在Redux的Action Creators里面,不放在组件里面等待了
调用方组件Count:

addAsync = () => {const { value } = this.count;//常规调用,看似没问题store.dispatch(createAddAsync(value, 5000));store.subscribe(() => {// 手动监听,触发页面重新渲染(也可以去监听App组件)this.setState({});});
};

action组件:

export const createAddAsync = (data, time) => {return () => {// 这里其实只套了一个定时操作setTimeout(() => {// 通过store调用已经定义好的增加action,省着我们再写了store.dispatch(createAddAction(data));}, time);};
};

但实际上这是有问题的,运行代码会报错

翻译过来就是,store不直接接收action的值为function(异步action)。想要接收必须去用一个中间件,让store允许接收函数
在这里插入图片描述
引入中间件:npm install redux-thunk需要我们安装一下
引入完成之后,就需要在store.js里面修改一下,用于支持异步action。做的这一切,只是为了让store可以接收异步action返回的函数
store.js

//引入store创建,以及中间件
import { createStore, applyMiddleware } from "redux";
//引入为store服务的Reducer
import countReducer from "./count_reducer";
// 引入thunk给applyMiddleware中间件用
import thunk from "redux-thunk";
//手动创建一个store,传入applyMiddleware(thunk) 
const store = createStore(countReducer, applyMiddleware(thunk));//全局只暴露这一个store对象
export default store;

此时我们再看修改后的组件:

调用方组件Count(没有变化):

addAsync = () => {const { value } = this.count;store.dispatch(createAddAsync(value, 5000));store.subscribe(() => {// 手动监听,触发页面重新渲染(也可以去监听App组件)this.setState({});});
};

action组件:

// 异步action,就是只action的值为函数,在异步action中,一般都会调用同步action,异步action不是必须要用的
export const createAddAsync = (data, time) => {return () => {// 这里其实只套了一个定时操作setTimeout(() => {// 调用已经定义好的增加actionstore.dispatch(createAddAction(data));console.log(data, time);}, time);};// 不用store调用dispatch也可以,因为dispatch会自动传进来一个,这两种完全等价// return (dispatch) => {//     // 这里其实只套了一个定时操作//     setTimeout(() => {//         // 调用已经定义好的增加action//         dispatch(createAddAction(data));//         console.log(data, time);//     }, time);// };
};

此时再测试,不再报错。

配置完毕之后的store
如果我们给store传入一个普通类型的Object action,store就会直接找Reducer去做处理
如果给store传入一个异步类型的Function action,这个函数store就会帮你调用

总结下来,虽然异步的action调用的时候返回值是函数,但是最后一般都会调用同步action,来完成数据的操作

React18版本的store监听刷新

之前给的store subscribe监听刷新,是React17版本的,React18版本的可以参考这个
ReactDOM.render is no longer supported in React 18.
改造之后的index.js

// 引入React核心库
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App标签
import App from "./App";
import store from "./redux/store";// React18版本监听并刷新页面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(() => {root.render(<App />);
});

总结异步action

 (1).明确:延迟的动作不想交给组件自身,想交给action(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。(3).具体编码:1).npm install redux-thunk,并配置在store中2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。3).异步任务有结果后,分发一个同步的action去真正操作数据。(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

React-Redux基础

安装:npm install react-redux
安装不上去就用这个:npm install react-redux --legacy-peer-deps
还不行就把package.json里面的这俩都删了,在安装React-Redux

"redux": "^4.2.1",
"redux-thunk": "^2.4.2",

引言

React-Redux是React专门出的Redux,属于官方出品
宗旨在于将UI与redux分隔开,所有的操作都要经由容器组件
在这里插入图片描述

引言-简化版连接UI组件与容器组件


前置知识

redux里的唯一的api:connect;

使用的时候,一般都是这么用connect()();

简单来说,得分开看connect()是一个返回一个函数的方法
connect()()是在connect()返回函数后,继续再次调用这个返回的函数
就有点类似下面这个,调用connect()(),最后会触发a里的输出OK
当然,a()也可以返回一个返回值

connect(){return a();
}a(){console.log("OK")
}

首先明确文件应该放在哪个包下面:
UI组件:components包下
容器组件:containers包下

且UI组件的里面,不能有任何关于Redux相关的API(store,actionCreator,dispatch… 这些API都不能引入了)。只能有UI组件以及UI组件动作相关的东西。在components里创建countTemplate.jsx
countTemplate.jsx

import React, { Component } from "react";export default class CountTemplate extends Component {// 纯UI组件+页面操作//加法increment = () => {// 这个是获取页面的选择值const { value } = this.selectNumber;};...//异步加incrementAsync = () => {const { value } = this.selectNumber;};render() {//console.log('UI组件接收到的props是',this.props);return (<div><h1>当前求和为:{"???"}</h1><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>);}
}

简化版里面,容器组件更像是一个桥梁,负责连接 UI组件与Redux 的交互。所以在Container里面,创建一个count.jsx

在这个桥梁(count.jsx)里,我们可以导入UI组件等待连接,(理论上应该导入Redux的Store,就完成了,但是不行,store必须从App里面通过props传进来 )

我在container容器里面引入了store仍然报找不到的错(这里是错误示范)

//引入Count的UI组件
import CountTemplate from "../components/CountTemplate";
// 引入store
import { store } from "../../redux/store";
// 引入connect的API
import { connect } from "reat-redux";// 导入connect,并且连接UI
export default connect()(CountTemplate);

我已经引进来store了,但是还是提示找不到store,这里不是因为我没有在connect里连接,而是不允许这种用法。只能在调用Container组件的组件里传值用props进去。
在这里插入图片描述

注意:react-redux不允许直接引入,只能从Container组件被调用的那一级组件里传进来
App组件(调用Container的组件)。这样就不报错了

import React from "react";
import Count from "./pages/Count";
import "./App.css";
import store from "./redux/store";
class App extends React.Component {render() {return (<div>{/* 只能用props传递store进入Container组件 */}<Count store={store}></Count></div>);}
}
export default App;

省略store.subscribe

首先就是之前的store.subscribe,之前我们在Redux里面,由于组件和Redux之间没有直接监听更新的手段。所以这里需要手动去监听渲染组件

// 引入React核心库
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App标签
import App from "./App";
import store from "./redux/store";// React18版本监听并刷新页面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(() => {root.render(<App />);
});

而在React-Redux中,connect就自动集成了监听并更新的功能,所以我们不必再手动监听。删掉即可,测试完美替换,不影响功能。

// 引入React核心库
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App标签
import App from "./App";// React18版本监听并刷新页面
const root = createRoot(document.getElementById("root"));
root.render(<App />);

React-Redux的基本使用

上面说到,Container想接受到store,只能通过props形式传递,但是Container本身,并不是标签形式的,所以就用不了props来传递数据。要想把Container接收到的props传给UI组件,就只能用connect的API,通过函数返回对象的形式来传递


首先看这个Container组件,是没有办法去写子组件传值的
也就是<CountTemplate key1={value1} ...>这种是没有机会写的
所以我们只能依靠connect传值

//引入Count的UI组件
import CountTemplate from "../../components/Count/CountTemplate";
// 引入connect的API
import { connect } from "react-redux";// 使用connect()()创建并暴露一个Count的容器组件
export default connect()(CountTemplate);

props是key-value的形式,所以我们要来模仿这种形式
这里就采用了对象的形式,来模拟这种key-value
{key:value}或者{key:()=>{函数体}} 也就是说,props的形式都可以模拟
修改一下Container的Count组件

//引入Count的UI组件
import CountTemplatefrom "../../components/Count/CountTemplate";
// 引入connect的API
import { connect } from "react-redux";function a() {// a函数返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value,这个value是状态return { key1: "value1" };
}function b() {// a函数返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value,这个value是函数return {key2: () => {console.log("OK");},};
}
// 使用connect建并暴露一个Count的容器组件,传a,b函数的返回值给UI组件
export default connect(a, b)(CountTemplate);

UI组件获取参数:
在UI组件打印一下:console.log("UI组件接收到的props是", this.props);

在这里插入图片描述

所以想获取就更简单了,直接this.props.key即可

注意,a传值的时候有点小坑,我们应该专注于状态的传递。所以优化一下

function a(state) {// 这里直接传state即可,这个state是从App传过来的// 所有的状态就直接用这个state给传过去了(注意是所有的state)// key1是自定义的,不设限return { key1: state };
}

同样,b里如果要调用dispatch函数,本应该import store

import store from "../../redux/store";
import { createAddAction } from "../../redux/count_action";function b() {// 返回值返回value为函数的对象// add是自定义的,不设限,仅代表当前返回值的函数return {add: (number) => store.dispatch(createAddAction(number)),};
}

但是由于Container里面不应该引入store,并且dispatch可以自动传入,所以就不用通过导入store来调用dispatch了。和上面的state一样,自动传入dispatch
改造后:

//这个是自定义的action函数,不是redux的函数
import { createAddAction } from "../../redux/count_action";function b(dispatch) {// 自动传入了dispatchreturn {// 不再需要用store调用dispatch  // 这种就可以简写了add: (number) => store.dispatch(createAddAction(number)),add: (number) => dispatch(createAddAction(number)),};
}

案例总结

demo结构,重点主要集中在Container上
在这里插入图片描述

这四个文件照之前的没有任何变化,所以不赘述了
在这里插入图片描述
UI组件 CountTemplate.jsx

import React, { Component } from "react";export default class Count extends Component {add = () => {// add操作const { value } = this.selectNumber;// 找到传入函数并传参this.props.add(value * 1);};addNotOdd = () => {// 奇数add操作const { value } = this.selectNumber;if (this.props.key1 % 2 !== 0) {this.props.add(value * 1);}};addAsync = () => {// 异步add操作const { value } = this.selectNumber;setTimeout(() => {this.props.add(value * 1);}, 500);};render() {return (<div><div>当前求和:{this.props.key1}</div><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><div><button onClick={this.add}>add</button><button onClick={this.sub}>sub</button><button onClick={this.addNotOdd}>addNotOdd</button><button onClick={this.addAsync}>addAsync</button></div></div>);}
}

Container组件 Countainer.jsx

import { connect } from "react-redux";
import CountTemplatefrom "../../components/count/CountTemplate";
import { add } from "../../redux/count_action";function a(store) {return { key1: store };
}function b(dispatch) {// 允许传递多个函数return {// add操作的函数add: (data) => dispatch(add(data)),// sub操作的函数sub: (data) => dispatch(sub(data)),};
}// 传入func a(负责state传递)  func b(负责函数动作传递)
// 最后桥梁连接CountTemplate组件
export default connect(a, b)(CountTemplate);

App.jsx

import React from "react";
import Container from "./pages/container/Container";
import store from "./redux/store";
import { BrowserRouter } from "react-router-dom";class App extends React.Component {render() {return (<BrowserRouter>{/* 在App向Container传递store(props形式) */}<Container store={store}></Container></BrowserRouter>);}
}
export default App;

index.js

// 引入React核心库
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App标签
import App from "./App";
import store from "./redux/store";// React18版本监听并刷新页面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(() => {root.render(<App />);
});

以上就可以完成计算组件

在这里插入图片描述

Container命名优化(引出规定的函数名)

传值的函数可以看到,实际上,所有的state和function的参数传递,通过一次就可以完全传递完。
所以react-redux里面就提供好了函数名专门传递state和function,就不需要我们再去单独定义了。更加规范了。

// 函数命名不规范
function a(store) {return { key1: store };
}
// 函数命名不规范
function b(dispatch) {// 允许传递多个函数return {// add操作的函数add: (data) => dispatch(add(data)),// sub操作的函数sub: (data) => dispatch(sub(data)),};
}// 使用connect建并暴露一个Count的容器组件,传a,b函数的返回值给UI组件
export default connect(a, b)(CountUI);

connect 方法是一个连接器,用于连接容器组件和 UI 组件,它第一次执行时,接收4个参数,这些参数都是可选的,它执行的执行的结果还是返回一个函数,第二次执行接收一个 UI 组件

connect方法第一次执行时(connect())的四个参数:mapStateToPropsmapDispatchToPropsmergePropsoptions
connect方法第二次执行时传入的UI组件connect()(UI组件)

这里先说传state和传方法的两个函数
mapStateToProps函数返回的是一个对象(对象value是state),mapStateToProps用于传递状态
mapDispatchToProps函数返回的是一个对象(对象value是function),mapDispatchToProps用于传递操作状态的方法

这只是官方推荐的API定义方式,后面还有更简写的方式来传递state和function

官方解释:mapStateToProps,mapDispatchToProps

具体在UI获取时候的key,还是需要看return的对象把key定义成什么,才能用this.props来获取key


同时之前的定时加操作是在UI组件里面做的,并没有放在`count_action.js`里面,所以把定时加操作搬进`count_action.js`

优化之后:
Container.jsx:

import { connect } from "react-redux";
import CountUI from "../../components/count/CountUI";
import { add, sub, addAsync } from "../../redux/count_action";// 传递state
function mapStateToProps(store) {return { key1: store };
}// 传递dispatch
function mapDispatchToProps(dispatch) {// 允许传递多个函数   通过dispatch通知Redux执行函数return {// add操作的函数add: (data) => dispatch(add(data)),// sub操作的函数sub: (data) => dispatch(sub(data)),// addAsync操作的函数,addAsync操作搬进action里addAsync: (data, time) => dispatch(addAsync(data, time)),};
}// 传入func mapStateToProps(负责state传递)  func mapDispatchToProps(负责函数动作传递)
// 最后传入CountTemplate组件完成连接
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

ConuntTemplate.jsx:

import React, { Component } from "react";export default class Count extends Component {...addAsync = () => {const { value } = this.selectNumber;this.props.addAsync(value * 1, 500);};render() {return (<div>...</div>);}
}

count_action.js:

/*
该文件专门为Count需要用的Reducer去生成action对象,封装好type,这样在调用组件传一个data进来就可以了
*/
import { ADD, SUB, ADD_ASYNC } from "./constant.js";export function add(data) {// 要返回Action对象(返回type和data数据)return { type: ADD, data };
}export function sub(data) {// 要返回Action对象(返回type和data数据)return { type: SUB, data };
}// 把这个定时操作集成在action里面 => addAsync
// setTimeout(() => {
//   this.props.add(value * 1);
// }, 500);
export function addAsync(data, time) {// dispatch为自动传入return (dispatch) => {setTimeout(() => {// 通过dispatch调用add,直接调用是不可以的dispatch(add(data));}, time);};
}

其他的文件没啥变化,不赘述了

写到这里其实 connect 已经比较完善了,但是你可以仔细想想 redux 的工作流程

image-20210909194900532

似乎少了点什么,我们在这里调用了函数,创建了 action 对象,但是好像 store 并没有执行 dispatch ,那是不是断了呢?执行不了呢?

其实这里 react-redux 已经帮我们做了优化,当调用 Action Creator 的时候,会立即发送 actionstore 而不用手动的 dispatch。后面马上会用到这个。

总结React-Redux的基本使用

(1).明确两个概念:1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数connect(mapStateToProps,mapDispatchToProps)(UI组件)-mapStateToProps:映射状态,返回值是一个对象(对象以k:v形式保存state)-mapDispatchToProps:映射操作状态的方法,返回值是一个对象(对象以k:v形式保存多个方法)
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象(只传递一个方法,如果多个方法就需要多个k:v对象)

React-Redux优化

之前的案例,很多功能都是杂糅到一起的,如果体量大起来会让人不知道该怎么办,所以这里在这个案例里面对之前的功能代码进行拆分操作,分文件处理。优化文件结构。

前置知识

例子1:

 function mapStateToProps(store) {return { key1: store };}

可以用箭头函数来写

const mapStateToProps = (store)=> {return { key1: store };}

如果箭头函数只有一句并且默认return只有一个对象,就可以省略return,直接箭头指一个括号,就代表return值了 ({ key1: store })
最后优化完

const mapStateToProps = (store)=> ({ key1: store })

例子2:

function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};
}

按照上面的说法,反正只有一个return,这里就可以直接优化成

mapDispatchToProps=(dispatch)=>{add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),}

带入到connect里面,甚至不用写属性,直接把函数体丢进去即可

简写 mapStateToProps & mapDispatchToProps

对于connect来说,第一次调用connect(),传入什么名字的函数不重要,传入的位置很重要,connect只认位置上的传入的值。
简写完前后对比:

/*
// 传统写法function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};}
*/// 引入connect生成一个容器组件,连接好React-Redux和UI组件,并暴露
// UI组件中依旧是通过this.props.xxxxxxx读取和操作状态
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect(// 对于第一个参数位置来说,首次调用可以传四个参数[ mapStateToProps 、mapDispatchToProps 、mergeProps、options  ]//store没有React-Redux给你自动调用,所以这里要自己写(state) => ({ count: state }), // 等价于mapStateToProps //mapDispatchToProps的简写(key:函数名),甚至不需要标注参数列表//dispatch有React-Redux给你自动调用,所以这里不用写了{ add: add, sub: sub, addAsync: addAsync }//等价于mapDispatchToProps 
)(CountUI);

完整开发

目录结构:
在这里插入图片描述

首先就是把Contrainer和UI组件放在一起,合并成一个Container文件叫Count。虽然文件是在一起,但是功能和类都是分开的。只是文件放一起了,对外依旧暴露Container,UI这次彻底不用export了。

import { connect } from "react-redux";
import { add, sub, addAsync } from "../../redux/count_action";
import React, { Component } from "react";// 这次CountUI彻底不用暴露了,暴露的任务交给connect来做。connect生成一个对外的Container,同时包含了数据和UI组件
// export default class Count extends Component {
class CountUI extends Component {add = () => {const { value } = this.selectNumber;this.props.add(value * 1);};sub = () => {const { value } = this.selectNumber;if (this.props.key1 === 0 || this.props.key1 < value) {return;}this.props.sub(value * 1);};addNotOdd = () => {const { value } = this.selectNumber;if (this.props.key1 % 2 !== 0) {this.props.add(value * 1);}};addAsync = () => {const { value } = this.selectNumber;this.props.addAsync(value * 1, 500);};render() {console.log(this.props);return (<div><div>当前求和:{this.props.key1}</div><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><div><button onClick={this.add}>add</button><button onClick={this.sub}>sub</button><button onClick={this.addNotOdd}>addNotOdd</button><button onClick={this.addAsync}>addAsync</button></div></div>);}
}/*function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};}
*/// 引入connect生成一个容器组件,连接好React-Redux和UI组件,并暴露
// UI组件中依旧是通过this.props.xxxxxxx读取和操作状态
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect((state) => ({ key1: state }), // 状态{ add: add, sub: sub, addAsync: addAsync } // 方法(不再需要注明参数列表)
)(CountUI);

Provider组件使用

首先看个之前的store传递,很麻烦,需要手动传递,如果每个标签都传递,都需要使用 store 时,很麻烦的

<Count store={store}/>
{/* 示例 */}
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>

所以官方就提供了一个大组件Provider,自动可以寻找需要用store的标签,自动精准传递store进去

我们可以这么做:在 src 目录下的 index.js 文件中,引入 Provider ,直接用 Provider 标签包裹 App 组件,将 store 写在 Provider 中即可

import { Provider } from "react-redux";ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById("root")
);

这样我们在 App.jsx 文件中,组件无需手写指定 store ,即可使用 store 非常方便

总结React-Redux优化

1.容器组件和UI组件整合一个文件

2.若有多个容器组件,无需自己给每个容器组件传递store,给包裹一个<Provider store={store}>即可。整体就贡献同一个store了。

3.使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

4.mapDispatchToProps也可以简单的写成一个对象,因为react-redux可以自动dispatch

5.一个组件要和react-redux“打交道”要经过哪几步?

  • 1)定义好UI组件—不暴露
  • 2)引入connect生成一个容器组件,并暴露,写法如下:
connect(state => ({key:value}), //映射状态{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
  • 3)在UI组件中通过this.props.定义好的key名 读取和操作状态

React-Redux综合案例(多组件组合共享)

引言

在写完了基本的 Redux 案例之后,我们可以尝试一些更实战性的操作,比如我们可以试试多组件间的状态传递,相互之间的交互

react-redux-demo

如上动图所示,我们想要实现上面的案例,采用纯 React 来实现是比较困难的,我们需要很多层的数据交换才能实现,但是我们如果采用 Redux 来实现会变得非常简单

因为 Redux 打通了组件间的隔阂,完成了复杂组件的数据交互,我们可以自由的进行数据交换,所有存放在 store 中的数据都可以实现共享,那我们接下来看看如何实现的吧~

整合UI组件与容器组件

如果UI组件和容器组件都分开写,那么实际上文件数量就得成倍增长,为了优化就整合一下两个文件,把UI组件和容器组件 放在一起。实际开发中也是整合的。

首先,一个jsx文件里面可以定义多个组件,其实思想上还是容器组件往UI组件传props,只不过两个组件放在了同一个文件里,对外只暴露一个Container接口。

这里演示一下两个或者多个组件在同一个文件里
创建一个Test组件
在这里插入图片描述

import React, { Component } from "react";// 默认对外暴露的容器组件
export default class Test extends Component {render() {return <div>Test</div>;}
}// UI组件,不对外暴露,但是可以在自己文件里面用
class Test2 extends Component {render() {return <div>UI组件</div>;}
}
// 他俩之间的连接靠connect
// 这是个简单的connect,后面有详细的描述
export default connect((state) => ({对象}),{// action动作}
)(CountUI);

所以以后就可以只留一个Container文件夹放容器组件

UI组件只要想去拿Redux的状态,就直接找内部的connect,因为这个connect已经拿到了Redux的所有state
后面有详细的描述

重新安排Redux文件结构

如果有Person和Count的组件,每个组件对应的action和reducer这两个文件,都是成倍的增长,所以肯定不能这么一大堆罗列于此,就要把Redux文件夹分层
在这里插入图片描述
分层后的Redux,将action文件和reducer文件分开

就不用再叫 组件_action.js组件_reducer.js 的这种了,因为已经在action文件夹下面了,根据文件夹就能分辨出来
在这里插入图片描述

React-Redux结构 & 合并Reducer

React-Redux结构

Redux里用的存储结构:
如果是只存了一个值,比如数字这种,那就是不用key去取,直接就this.store即代表着当前对象。
在这里插入图片描述

如果是存了两个值以上,n个对象,那就是Redux作为一个大对象,里面存了n多个小对象,通过key来获取目标对象(key是合并reducer时定义的)
在这里插入图片描述

合并Reducer

之前的store中只注册了一个Reducer并导出,对于多个Reducer来说,只导出一个肯定是不行的,所以我们要将其他Reducer也注册到store里面,并且命名好key来帮助后续获取

这里提一个前置知识:combineReducers({对象})传入的对象,就是Redux保存的总对象


重要事情说三遍~
combineReducers({对象})传入的所有对象,就是Redux保存的总对象
combineReducers({对象})传入的所有对象,就是Redux保存的总对象
combineReducers({对象})传入的所有对象,就是Redux保存的总对象


合并后的store.js:

//引入store创建,以及中间件
import { combineReducers,createStore } from "redux";
//引入为store服务的Reducer
import countReducer from "../redux/reducer/count";
import personReducer from "../redux/reducer/person";// 之前的store只能注册一个reducer,也就是count的countReducer
// 没法注册person的reducer,所以我们需要用函数把两个函数都注册了
// const store = createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});
//全局只暴露这个Reducers,同样需要用createStore函数创建
export default createStore(allReducer);

Container通过key获取对应的状态
Count组件的connect

// 使用connect建并暴露一个Count的容器组件,传a,b函数的返回值给UI组件
/*(state) => ({ sumNumber: state.sum })对应的含义:(默认传入的全局Redux对象) => ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key })
*/
export default connect(// 这里还获取了Person组件的人数长度(通过Redux获取其他组件的值)// 这里可以传入多个 k-v ,用逗号分隔(state) => ({ sumNumber: state.sum, personLength: state.persons.length }),{// action动作和以前一样传递即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);

Person组件的connect

/*(state) => ({ personList: state.persons }),对应的含义:(默认传入的全局Redux对象) => ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key }),
*/
export default connect(// 这里还获取了Count组件的求和值(通过Redux获取其他组件的值)// 这里可以传入多个 k-v ,用逗号分隔(state) => ({ personList: state.persons, countSum: state.sum }),{// 这里绑定的action该咋传咋传addPerson: addPerson,}
)(PersonUI);

整个案例代码

先看结构:
在这里插入图片描述
容器组件Count.jsx:

// 引入connect的API
import { connect } from "react-redux";
import { add, sub, addAsync } from "../../redux/action/count";import React, { Component } from "react";class CountUI extends Component {add = () => {const { value } = this.count;this.props.add(value);};sub = () => {const { value } = this.count;this.props.sub(value);};addNotOdd = () => {if (this.props.sumNumber % 2 !== 0) {this.props.add(value);}};addAsync = () => {const { value } = this.count;this.props.addAsync(value, 500);};render() {console.log(this.props);return (<div><h1>当前结果{this.props.sumNumber},下方组件总人数为{this.props.personLength}</h1>选择内容<select ref={(c) => (this.count = c)}><option value={1}>1</option><option value={2}>2</option><option value={3}>3</option></select><button onClick={this.add}>+</button><button onClick={this.sub}>-</button><button onClick={this.addNotOdd}>当前奇数加</button><button onClick={this.addAsync}>非同步加</button></div>);}
}// 使用connect建并暴露一个Count的容器组件,传a,b函数的返回值给UI组件
/*(state) => ({ sumNumber: state.sum })对应的含义:(默认传入的全局Redux对象) => ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key })
*/
export default connect(// 这里还获取了Person组件的人数长度(通过Redux获取其他组件的值)// 这里可以传入多个 k-v ,用逗号分隔(state) => ({ sumNumber: state.sum, personLength: state.persons.length }),{// action动作和以前一样传递即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);

容器组件Person.jsx:

import React, { Component } from "react";
import { addPerson } from "../../redux/action/person";
import { nanoid } from "nanoid";
import { connect } from "react-redux";class PersonUI extends Component {addPerson = () => {const name = this.name.value;const age = this.age.value;if (name === "" || age === "") {return;}const newPeson = { id: nanoid(), name, age };this.props.addPerson(newPeson);};render() {return (<div><h1>我是person组件,上方组件求和为:{this.props.countSum}</h1><inputref={(name) => {this.name = name;}}></input><inputref={(age) => {this.age = age;}}></input><button onClick={this.addPerson}>add New Person</button><ul>{this.props.personList.map((p) => {return (<li key={p.id}>name: {p.name}---- age: {p.age}</li>);})}</ul></div>);}
}/*(state) => ({ personList: state.persons }),对应的含义:(默认传入的全局Redux对象) => ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key }),
*/
export default connect(// 这里还获取了Count组件的求和值(通过Redux获取其他组件的值)// 这里可以传入多个 k-v ,用逗号分隔(state) => ({ personList: state.persons, countSum: state.sum }),{// 这里绑定的action该咋传咋传addPerson: addPerson,}
)(PersonUI);

Action下的count.js:


import { ADD, SUB } from "../constant.js";
import store from "../store.js";export const add = (data) => ({ type: ADD, data });
export const sub = (data) => ({ type: SUB, data });// 异步action,就是只action的值为函数,在异步action中,一般都会调用同步action,异步action不是必须要用的
// 能在这里写函数,是因为在store做了redux-thunk的设置
export const addAsync = (data, time) => {return () => {// 这里其实只套了一个定时操作setTimeout(() => {// 调用已经定义好的增加actionstore.dispatch(add(data));}, time);};
};

Action下的person.js:

import { ADD_PERSON } from "../constant";export const addPerson = (data) => ({ type: ADD_PERSON, data });

reducer下的count.js:

import { ADD, SUB } from "../constant.js";export default function countReducer(previousState, action) {// 从Action对象里传来的action对象,里面包含type和data,所以我们需要解构出来。等待后续处理const { type, data } = action;if (previousState === undefined) {return 0;}// 判断传入功能类型switch (type) {case ADD://这里用原来的值+新传入的值,得到新值return previousState + data * 1;case SUB:if (previousState < data * 1) {return previousState;}//这里用原来的值-新传入的值,得到新值return previousState - data * 1;// 提一嘴,这里都是return,所以不用breakdefault:return previousState;}
}

reducer下的person.js:

import { ADD_PERSON } from "../constant.js";const initPersonList = [{ id: "123456", name: "Tom", age: "20" }];export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;switch (type) {case ADD_PERSON:// 把之前的展开(之前的数组是没展开的),把新来的加进去return [data, ...previousState];default:return previousState;}
}

常量constant.js

export const ADD = "add";
export const SUB = "sub";
export const ADD_PERSON = "addPerson";

store.js

//引入store创建,以及中间件
import { applyMiddleware, combineReducers, createStore } from "redux";
//引入为store服务的Reducer
import countReducer from "../redux/reducer/count";
import personReducer from "../redux/reducer/person";
import thunk from "redux-thunk";// 之前的store只能注册一个reducer,也就是count的countReducer
// 没法注册person的reducer,所以我们需要用函数把两个函数都注册了
// const store = createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});// 全局只暴露这个Reducers,同样需要用createStore函数创建
// 同时允许接收函数的applyMiddleware(thunk)也不能丢了
export default createStore(allReducer, applyMiddleware(thunk));

App.jsx

class App extends React.Component {render() {return (<div><Provider store={store}>{/* 用Provider组件的props传递store进入Container组件 */}// 被Provider组件包裹的子组件都能自动接收到store// 这俩都是容器组件<Count></Count><Person></Person></Provider></div>);}
}
export default App;

发现个小bug:
在这里插入图片描述
原因是没有弄redux-thunk做中间件支持,再详细可以看这个:redux-thunk

总结React-Redux综合案例

(1).定义一个Pserson组件,和Count组件通过redux共享数据。
(2).为Person组件编写:reducer、action,配置constant常量。
(3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!
(4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

纯函数概念

就是一个概念,没有编码

引言

1.一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)

2.必须遵守以下一些约束

  • 1)不得改写参数数据
  • 2)不会产生任何副作用,例如网络请求,输入和输出设备
  • 3)纯函数里面不能调用Date.now()或者Math.random()等不纯的方法

3.redux的reducer函数必须是一个纯函数

例子

纯函数要求输入和输出相同
比如:

// a是纯函数
function a(data) {return data;
}
// b不是纯函数
function b(data) {let c = 1;return c;
}

再拿一个reducer举个例子:
这里的 return [data, ...previousState];。因为地址引用发生了变化,所以就能触发页面更新。因为Redux是浅比较,不比较对象内容,发现return的引用地址变化了就自动更新了。

export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;switch (type) {case ADD_PERSON:// 把之前的展开(之前的数组是没展开的),把新来的加进去return [data, ...previousState];default:return previousState;}
}

如果是非纯函数,比如这样。这就无法生效,因为不是纯函数

export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;
switch (type) {case ADD_PERSON:var newArray = previousState;newArray.push(data);return newArray;default:return previousState;}
}

Redux-DevTools

顾名思义,开发者工具

需要插件+npm库的依赖

 npm install redux-devtools-extension --legacy-peer-deps

在这里插入图片描述

store需要导入redux-devtools-extension的依赖,并且在暴露store的connect函数的第二个参数位置传入对应的api
如果之前有applyMiddleware(thunk)的中间件操作,可以选择将中间件传入其api

...省略
// redux-devtools api
import { composeWithDevTools } from "redux-devtools-extension";// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});export default createStore(allReducer,// redux-devtools apicomposeWithDevTools(applyMiddleware(thunk))
);

再次启动项目,就可以在浏览器的插件里面看redux存进去的东西了

在这里插入图片描述

总结工具使用

(1).yarn add redux-devtools-extension
(2).store中进行配置import {composeWithDevTools} from 'redux-devtools-extension'const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

Reducers优化

之前store中,所有的Reducers都是积压在store中进行的合并,如果是大型项目将难以维护,所以我们一般把Reducers的合并,放在一个单独的js文件中去做,完成导出,导出完毕之后在store中引入即可
在这里插入图片描述
在reducer下专门新建一个index文件,完成汇总合并操作

/*该文件用于汇总所有的reducer为一个总的reducer
*/
import { combineReducers } from "redux";//引入为store服务的Reducer
import countReducer from "./count";
import personReducer from "./person";
// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});// 导出
export default allReducer;

精简之后的store文件,非常清爽

//引入store创建,以及中间件
import { applyMiddleware, createStore } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";// 引入reducers
import allReducer from "./reducer";export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))
);

总结:

(1).所有变量名字要规范,尽量触发对象的简写形式。
(2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

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

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

相关文章

VOL-vue 框架 文件上传控件关于大文件上传等待的修改

我的项目在测试voltable列表组件中对阿里云OSS做附件上传时&#xff0c;几十M的文件可能就会需要一段时间来上传&#xff0c;才能有OSS的状态和链接返回。 但是控件VolUpload.vue并没有去在这方面做任何交互体验上的控制&#xff0c;而且VolUpload.vue本身写的几个上传函数都是…

内测分发是什么?十年的前端开发者带你了解

内测分发是软件开发过程中的一个阶段&#xff0c;特别指软件还未完全完成或准备对外广泛发布前&#xff0c;向一定范围的用户群体提供该软件版本的测试机会&#xff0c;以便收集反馈和修复潜在的问题。在讲解内测分发之前&#xff0c;我们需要明确几个相关概念&#xff1a; 软件…

区块链媒体宣发:揭示优势与趋势,引领信息传播新时代

在数字化潮流中&#xff0c;区块链技术正以惊人的速度改变着传媒行业的格局。从区块链媒体宣发中获得的种种优势和未来的趋势&#xff0c;不仅为企业带来了新的推广途径&#xff0c;也在信息传播领域掀起了一场革命。本文将深入探讨区块链媒体宣发的优势以及未来的发展趋势。 1…

排序算法---选择排序

1.实现流程&#xff1a; 1. 把第一个没有排序过的元素设置为最小值&#xff1b; 2. 遍历每个没有排序过的元素&#xff1b; 3. 如果元素 < 现在的最小值&#xff1b; 4. 将此元素设置成为新的最小值&#xff1b; 5. 将最小值和第一个没有排序过的位置交换 选择排序执行流程…

初识Ceph --组件、存储类型、存储原理

目录 ceph组件存储类型块存储文件存储对象存储 存储过程 ceph Ceph&#xff08;分布式存储系统&#xff09;是一个开源的分布式存储系统&#xff0c;设计用于提供高性能、高可靠性和可扩展性的存储服务&#xff0c;可以避免单点故障&#xff0c;支持块存储、对象存储以及文件系…

【小白专用】Apache2.4+PHP8.3+MYSQL的配置

1.下载PHP和Apache 1、PHP下载 PHP For Windows: Binaries and sources Releases 注意&#xff1a; 1.使用Apache作为服务器的话&#xff0c;一定要下载Thread Safe的&#xff0c;否则没有php8apache2_4.dll这个文件&#xff0c; 如果使用IIS的请下载 NON Tread safe的 2.如果…

iOS按钮控件UIButton使用

1.在故事板中添加按钮控件,步聚如下: 同时按钮Shift+Commad+L在出现在控件库中选择Button并拖入View Controller Scene中 将控件与变量btnSelect关联 关联后空心变实心 如何关联?直接到属性窗口拖按钮变量到控件上,出现一条线,然后松开,这样就关联成功了 关联成功后属性窗口…

ISP IC/FPGA设计-第一部分-MT9V034摄像头分析(0)

MT9V034为CMOS图像传感器&#xff0c;有着极其优秀的图像成像性能&#xff0c;同时支持丰富的功能用于isp的开发&#xff1b;MT9V034 的HDR宽动态、10bit数据深度、RAW格式&#xff08;bayer阵列&#xff09;图像、dvp和lvds接口、60fps正是学习isp开发的理想传感器&#xff1b…

使用Git进行版本控制

参考&#xff1a;《Python编程从入门到实践》 前言1、安装、配置 Git1.1 在Linux系统中安装Git1.2 在OS X系统中安装Git1.3 在Windows系统中安装Git1.4 配置Git 2、创建项目3、忽略文件4、初始化仓库5、检查状态6、将文件加入到仓库中7、执行提交8、查看提交历史 前言 版本控制…

C语言 预处理 + 条件编译宏 + 井号运算符

预处理阶段任务 预处理指令 条件编译宏 条件编译宏的作用在于根据编译时的条件进行代码的选择性编译&#xff0c;从而实现不同环境、不同配置或不同功能的编译版本。 这可以用于实现调试模式和发布模式的切换&#xff0c;平台适配&#xff0c;以及选择性地编译不同的功能模块等…

Git merge 与 Git rebase 与 Git fetch

Git merge 与 Git rebase 看这个图就行了 git merge、git rebase 和 git fetch 是 Git 中的三个不同的命令&#xff0c;它们分别用于不同的目的。以下是它们的主要区别&#xff1a; git merge&#xff08;合并&#xff09;&#xff1a; 用途&#xff1a; 用于将一个分支的更改…

基于hadoop下的spark安装

目录 简介 安装准备 spark安装 配置文件配置 简介 Spark主要⽤于⼤数据的并⾏计算&#xff0c;⽽Hadoop在企业主要⽤于⼤数据的存储&#xff08;⽐如HDFS、Hive和HBase 等&#xff09;&#xff0c;以及资源调度&#xff08;Yarn&#xff09;。但是也有很多公司也在使⽤MR2进…

【Spring教程24】Spring框架实战:从零开始学习SpringMVC 之 SpringMVC入门案例代码示例

目录 1:创建Maven项目&#xff0c;并导入对应的jar包2:创建控制器类3:创建配置类4:创建Tomcat的Servlet容器配置类5:配置Tomcat环境6:启动运行项目7:浏览器访问8:知识点总结 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0…

【数学建模】《实战数学建模:例题与讲解》第八讲-回归分析(含Matlab代码)

【数学建模】《实战数学建模&#xff1a;例题与讲解》第八讲-回归分析&#xff08;含Matlab代码&#xff09; 回归分析基本概念经典多元线性回归&#xff08;MLR&#xff09;主成分回归&#xff08;PCR&#xff09;偏最小二乘回归&#xff08;PLS&#xff09;建模过程应用和优势…

docker-ubuntu中基于keepalived+niginx模拟主从热备完整过程

一、环境准备 &#x1f517;在Ubuntu中安装docker 二、主机 1、环境搭建 1.1 镜像拉取 docker pull ubuntu:16.041.2 创建网桥 docker network create -dbridge --subnet192.168.126.0/24 br11.3 启动容器 docker run -it --name ubuntu-1 --privileged -v /home/vac/l…

为 Compose MultiPlatform 添加 C/C++ 支持(2):在 jvm 平台使用 jni 实现桌面端与 C/C++ 互操作

前言 在上篇文章中我们已经介绍了实现 Compose MultiPlatform 对 C/C 互操作的基本思路。 并且先介绍了在 kotlin native 平台使用 cinterop 实现与 C/C 的互操作。 今天这篇文章将补充在 jvm 平台使用 jni。 在 Compose MultiPlatform 中&#xff0c;使用 jvm 平台的是 An…

如何一个例子玩明白GIT

一个例子玩明白GIT GIT的介绍和教程五花八门&#xff0c;但实际需要用的就是建仓、推送、拉取等操作&#xff0c;这儿咱可以通过一个例子熟悉这些操作&#xff0c;一次性搞定GIT的使用方法学习。下面这个例子的内容是内容是建立初始版本库&#xff0c;然后将数据复制到 "远…

轻量封装WebGPU渲染系统示例<45>- 材质组装流水线(MaterialPipeline)灯光、阴影、雾(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/MaterialPipelineFog.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下&#xff1a; export class MaterialPipelineFog {pr…

数组创建方法

数组的创建 1.let a[] 2.let anew Array(5) 3.let anew Array(1,2,3) 4.let a[1,2,3] 创建数组是空还是有值是以上四种写法。但是如果没给值的变量是undefined&#xff0c;再a[0]找不到这种变量的。所以当找某一个数需要已经是数组内存。不想给值可以给空数组。只要是数组…

MEMS制造的基本工艺介绍——晶圆键合

晶圆键合是一种晶圆级封装技术&#xff0c;用于制造微机电系统 (MEMS)、纳米机电系统 (NEMS)、微电子学和光电子学&#xff0c;确保机械稳定和气密密封。用于 MEMS/NEMS 的晶圆直径范围为 100 毫米至 200 毫米&#xff08;4 英寸至 8 英寸&#xff09;&#xff0c;用于生产微电…