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 来实现
- 某个组件的状态需要共享时
- 一个组件需要改变其他组件的状态时
- 一个组件需要改变全局的状态时
除此之外,还有很多情况都需要使用 Redux 来实现(还没有学 hook,或许还有更好的方法)

Redux工作流程
- store
store 是 Redux 的核心,可以理解为是 Redux 的数据流向指挥,我们可以将任何我们想要存放的数据放在 store 中,在我们需要使用这些数据时,我们可以从中取出相应的数据。因此我们需要先创建一个 store ,在 Redux 中可以使用 createStore API 来创建一个 store
store是无法直接进行操作的,需要借助Redux进行操作
store是一个调度者,store是指挥者,不干活。需要任何操作,或者动作,都会去分发走,找到对应的担当来做。(如果越过了store,action直接去找reducers,就有点类似去餐厅点餐,不找前台点餐,直奔后厨要吃的😂)
- action
action 是 store 中唯一的数据来源,一般来说,我们会通过调用 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> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </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())的四个参数:mapStateToProps 、mapDispatchToProps 、mergeProps、options
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 的工作流程

似乎少了点什么,我们在这里调用了函数,创建了 action 对象,但是好像 store 并没有执行 dispatch ,那是不是断了呢?执行不了呢?
其实这里 react-redux 已经帮我们做了优化,当调用 Action Creator 的时候,会立即发送 action 给 store 而不用手动的 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 来实现会变得非常简单
因为 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
相关文章:
06-React组件 Redux React-Redux
React组件化(以Ant-Design为例) 组件化编程,只需要去安装好对应的组件,然后通过各式各样的组件引入,实现快速开发 我们这里学习的是 Ant-design (应该是这样),它有很多的组件供我们…...
Windows安装卸载MySQL
【官方】MySQL参考手册:介绍MySQL Server、SQL、InnoDB存储引擎、复制等。 Windows 卸载 MySQL 删除程序 【win x】 > p ,在安装的程序中卸载MySQL相关删除安装目录和数据文件夹 D:\ProgramFiles\MySQL C:\ProgramData\MySQL删除服务 【win r】 &…...
element-ui 重置resetFields()不生效
element-ui 重置resetFields()不生效 初始化数据 data() {return {dialogVisible: false,form: {name: ,age: ,sex: ,birth: ,addr: }}}弹窗关闭重置数据 handleClose() {// 弹窗关闭的时候清除数据this.$refs.form.resetFields()this.dialogVisible false }以上操作this.$…...
JVM学习笔记-如何在IDEA打印JVM的GC日志信息
若要在Idea上打印JVM相应GC日志,其实只需在Run/Debug Configurations上进行设置即可。 拿《深入Java虚拟机》书中的3-7代码例子来演示,如 1 public class JvmTest {2 private static final int _1MB1024*1024;3 public static void main(String…...
16ASM 汇编基础与Debug使用
目录 硬件运行机制 微机系统硬件组成 计算机系统组成 8086CPU组织结构 DoxBox安装 Debug使用 R命令 D命令 E命令 U命令 T命令 A命令 标志寄存器 常用机器指令 硬件运行机制 下面是一个电子器件二极管,正向加电则通,反向加电则不通 利用二…...
基于Java车辆管理系统(如高校、办公园区)
基于Java车辆管理系统(如高校、办公园区) 功能需求 1、车辆进出管理:对进出校园和园区的车辆进行登记、授权和监管,确保车辆进出安全,特别是对于贵重车辆或特殊车辆,可以进行特别监控。 2、停车管理&…...
Dubbo v Spring Cloud:两大技术栈如何选型?
提到微服务开源框架,不可不说的是 Dubbo 和 Spring Cloud,这两大框架应该是大家最熟悉的微服务解决方案,也是面试中的热点。本文就梳理下 Dubbo 和 Spring Cloud 的应用特性,以及两个组件的功能对比。 Dubbo 应用 Dubbo 是阿里开…...
ubuntu上搭建bazel编译环境,构建Android APP
背景是github上下载的工程,说明仅支持bazel编译,折腾了一天Android studio,失败。 不得不尝试单价bazel编译环境,并不复杂,过程记录如下 说明:ubuntu环境是20.04,pve虚拟机安装 1.安装jdk sudo…...
(第38天)RAC 修改 IP 信息
介绍 在生产中有时候会遇到机房搬迁网络变更、系统上线由测试环境切换为生产环境、系统层面双网卡绑定或者解绑等情况。Oracle RAC 环境下应该如何操作?首先,我们都知道 Oracle RAC 中的 IP 主要有:Public IP、Virtual IP、SCAN IP、Private IP 这几种,接下来分别讲解这几…...
利用法线贴图渲染逼真的3D老虎模型
在线工具推荐: 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时,有几种不同的风格…...
『 MySQL数据库 』聚合统计
文章目录 前言 🥑🥝 聚合函数🍓 COUNT( ) 查询数据数量🍓 SUM( ) 查询数据总和🍓 AVG( ) 查询数据平均值🍓 MAX( ) 查询数据最大值🍓 MIN( ) 查询数据最小值 🥝 数据分组GROUP BY子句…...
Redis - 事务隔离机制
Redis 的事务的本质是 一组命令的批处理 。这组命令在执行过程中会被顺序地、一次性 全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会被中断。 当事务中的命令出现语法错误时,整个事务在 exec 执行时会被取消。 如果事务中的…...
android项目实战之编辑器图片上传预览
现状分析 项目的需求用到编辑器,编辑器中又可能用到图片上传功能。 实现方案 1. 增加依赖库,可以参考前面的几篇文章,都有描述。 2. 核心代码实现 PictureSelector.create(GoodItemContentFragment.this) .openGallery(SelectMimeType.…...
微信小程序:上传图片到别的域名文件下
效果 wxml <!-- 上传照片 --> <view class"addbtn"><view classpic name"fault_photo" wx:for"{{imgs}}" wx:for-item"item" wx:key"*this"><image classweui-uploader_img src"{{item}}"…...
Linux----内核及发行版
1. Linux内核 Linux内核是操作系统内部操作和控制硬件设备的核心程序,它是由芬兰人林纳斯开发的。 内核效果图: 说明: 真正操作和控制硬件是由内核来完成的,操作系统是基于内核开发出来的。 2. Linux发行版 是Linux内核与各种常用软件的组合产品&am…...
设备制造行业CRM:提升客户满意度,驱动业务增长
设备制造行业客户需求多样化、服务链路长,企业在关注APS、EMS等工业软件之余还要以客户为中心,做好客户服务。设备制造行业CRM管理系统是企业管理客户关系的利器,设备制造行业CRM的作用有哪些?一文带您看懂。 设备制造行业需要解…...
JavaScript实现复制功能函数
function copyUrl() {var copyText document.getElementById("url");copyText.select();document.execCommand("copy"); }其中,copyUrl()函数用于复制,document.getElementById(“url”)用于获取链接的DOM元素,select()…...
JVM垃圾收集器
主要垃圾收集器如下,图中标出了它们的工作区域、垃圾收集算法,以及配合关系。 HotSpot虚拟机垃圾收集器 这些收集器里,面试的重点是两个——CMS和G1。 Serial 收集器 Serial(串行)收集器是最基本、历史最悠久的垃圾…...
LeetCode(58)随机链表的复制【链表】【中等】
目录 1.题目2.答案3.提交结果截图 链接: 随机链表的复制 1.题目 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节…...
JVM源码剖析之registerNatives方法
目录 版本信息: 写在前面: 源码论证: 总结: 版本信息: jdk版本:jdk8u40 写在前面: 在Java类库中很多类都有一个registerNatives的native方法,并且写在static静态代码块中进行初…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
