带你看懂 Redux 的文档和例子。
例子:https://redux.js.org/introduction/examples
注意几个问题
const reducer = (state, action) => {
if (state === undefined) {
return {
n: 0
}
} else {
if (action.type === 'add') {
var newState = {
n: state.n + action.payload
}
return newState
} else {
return state
}
}
}
const store = createStore(reducer)
store.subscribe(() => {
render()
})
store.dispatch({
type: 'add',
payload: 1
})
<Provider store={store}>
<App />
</Provider>,
function mapStateToProps(state) {
return {
n: state.n
}
}
function mapDispatchToProps(dispatch) {
return {
add1: () => dispatch({
type: 'add',
payload: 1
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Redux 可以说是发布订阅模式的一种变种 -> Redux 搞了一些约定,具体做了哪些约定就是我们今天要讲的内容 -> 透过 Redux 文档提供的例子来认识这些阅读
💡:评价 Redux 官网?
看起来很全,但实际上并不适合新手观看的官网!
总之,Redux 是面向于 JS 熟练者的官网,即便是它的中文官网,也不如 Vue 官网好看!
还有,这文档一开始就说:

可以看到,此文档对新手而言是极其不好的,所以我们该如何看 Redux 文档呢?

这些示例一个个地去看完,就能知道 Redux 它产生的背景以及动机了……
总之,Redux 文档很高端,适合熟手,而新手第一步是去看示例!
方方在看 Redux 之前就已经它的背景了!所以可以看懂了,而如果假装自己是新人的话,就觉得这官网写得并不好!
Counter 是数数,Vanilla 是原生 JS,香草之意
💡:一个梗?
很多前端程序员只会用库或者框架,ta 们 搞不清楚底层原理,如他们上手前端就直接学 Vue 和 React 了,人家叫你用原生 JS,但是你们居然不会……那就宣称原生 JS 是个库或者框架,以此来嘲讽这些不会原生 JS 的人……
此「库」官网:Vanilla JS

如何下载这个库?

所以「为何叫 Counter Vanilla」呢? -> 表示没有用任何的库,直接就是「Redux+原生 JS」,而不是「React」结合「Redux」。
总之,如果你遇到不想用原生 JS 的同事,那你就安利他使用一个叫「VanillaJS」的库吧,并且告诉他体积小,功能又强大!
💡:例子?
Example:https://github.com/reduxjs/redux/tree/master/examples/counter-vanilla

环境搭建:只用 cdn 引入 redux 就好了,而 VanillaJS 就不需要引入了 😀
注意点:

如何配合 Redux 使用?
如果不配合:

关键点讲解:
createStore :

写在 onclick 属性里边的代码涉及到变量,那就是全局的变量:

React 是不加括号的 -> 只需要获取函数引用就行了
做法:
store.dispatch({type:'add',payload:1})dispatch这个 API,会触发store.subscribe(callback)的callback参数执行
方方在 2018 年 9 月时说,Redux 过一年就得死……既然会死,我 2021 年为何还要去学它了? -> 因为它的思想是不会死的,以后出现的东西,只是把 Redux 简化罢了,实际上代码还是一样的思路……总之,写法、API 变了都无所谓,重要是思路不变就行了! -> 这就是我学习它的原因!
可以看到,这个过程挺复杂的,如果不用 Redux 的话,不用两句话就搞定了,但如果你有这种想法的话,你是学不会 Redux,总之你得放弃这种想法才能学会 Redux -> 不要去问「还有更简单的实现」这种问题
💡:Angular 很少为旧概念发明新名词?
function add1() {
store.dispatch({
type: "add",
payload: 1
});
// 1 dispatch 一个 action
}
「dispatch 一个 action」的意思其实就是「触发一个事件」,但 Redux 就得说成是「派发一个动作」 -> 它们本质上差不多是一个意思!
我其实比较认同 Redux 的说法的,我们 click 一个页面,其实就是把
add这个动作交给 Reducer 去处理!
其实如果我们对发布订阅模式有一定了解的话,我们就不会被 ` store.dispatch` 这行代码所迷惑
➹:为什么前端讨论的都是各种 react,vue 源码解析,渲染机制等,没人讨论 angular? - Trotyl Yu 的回答 - 知乎
💡:Redux 的本质?
了解了上边这个例子,就了解了接下来要讲的例子 -> 接下来的例子都是这个例子的加强版!
💡:state 和 store 的区别?
store 是存储 state 的地方,要获取 state,你只能通过 store.getState() 来获取
如果你要更新 state,你只需要调用一个 action 就更新了! -> 触发事件,操作 action,生成新的 state,重新获取 state

store 就是管家,它是用来管钱的!钱你要花是可以的,你要花在哪儿,我帮你付钱就行了,付完钱之后我告诉你「还剩多少钱」 -> 钱不经过用钱人的手,钱只经过管家的手,那么这个钱就很干净了!
在 Flux 里边,改数据是交给 Store 做的!
为啥需要 payload ?
不要 payload :

我们不能这样写,因为 add 和 add2 没啥本质区别,无非就是一个 +1 ,一个 +2 罢了!
总之,我们都是在做加操作,只是加的量不同罢了!
因此我们应该这样写(需要payload):
function add1() {
store.dispatch({
type: "add",
payload: 1
});
// 1 dispatch 一个 action
}
function add2() {
store.dispatch({ type: "add", payload: 2 });
}
一个加的量是1,另一个加的量则是2。
这样一来,我们就不用写那么多的actionType了 -> 显得更简洁!
总之,payload就是告诉我们这个操作的量是多少!(操作的量甚至可以是个对象)
💡:如何区分什么时候加括号?什么时候不加括号?
<button id="add1IfOdd" onclick="add1IfOdd()">如果是单数就+1</button>
写 React 是不加括号的,因为我们要的是引用,而加括号那就意味着我们要的返回值……
写 HTML,add1IfOdd()则表示一段 JS 代码,只要触发了,就会执行这行代码!
function add1IfOdd() {
var oldState = store.getState();
if (oldState % 2 === 1) {
store.dispatch({ type: "add", payload: 1 });
}
}
这是对原生事件的监听函数
我们通常把异步操作放在这儿:
function add1Async() {
setTimeout(() => {
store.dispatch({ type: "add", payload: 1 });
}, 2000);
}
但为何不放在 Reducer 里边呢?

因为如果这样做,默认返回的就是undefined了! -> 在 JS 里边,函数不写返回值,默认返回的就是undefined
定时器里边那个返回值是箭头函数返回的,可不是stateChanger所返回的……
接下来的例子就是「React+Redux」 -> 代码会少很多!
例子:https://github.com/reduxjs/redux/tree/master/examples/counter

之前那个「原生 JS+Redux」就一个文件就讲清楚了,而现在「React+Redux」就得多个文件了…… -> 对于新人而言,一句话概括之「什么鬼?这些鬼文件都是什么?」 -> 总之,这需要我们为 React 搭建开发环境
使用:create-react-app
官网:https://github.com/facebook/create-react-app
yarn create react-app my-app
代码写法步骤(不要 cdn 引入了):
如果你是用 yarn 搭建的项目,那么如果你在安装 Redux 的时候用了 npm 的话,那你就得把项目的
node_modules给rm -rf了 -> 一个项目不能同时使用两种包管理工具 -> 为啥前端不把这两个工具合并了?我也不知道,方方推荐用 yarn,因为这比 npm 快!
💡:为啥官方例子是value,而不是state属性呢?

💡:事件与constructor同级?
不然,无法访问到!
💡:为什么要给App组件直接传函数参数?

官方例子也是这样干的:
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import Counter from './components/Counter'
import counter from './reducers'
const store = createStore(counter)
const rootEl = document.getElementById('root')
const render = () => ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
/>,
rootEl
)
render()
store.subscribe(render)
💡:关于undefined的报错?

💡:为传给store.subscribe函数传参数?
store.subscribe(render)store.subscribe(()=>{ render(store) })如果直接这样store.subscribe(render(store)),那么render(store)就会直接执行了,把返回值作为subscribe的参数值 -> 也许会报错!
相较于原生 JS 配合 Redux,React+Redux:
index.js专门处理数据的App.js咩有与store相关的代码,就是调用它爸爸的一些钩子……App都给更新了!
目前代码存在的问题:

那我们就可以这做:
<App value={store.getState()} store={store} />

于是,就有很多工程师想做这么一件事情,不想一层层地通知数据下去,如子组件通知爸爸,爸爸通知爷爷,爷爷通知曾爷爷……,最后通知到App去更新那个store…… -> 如果一个组件嵌套了很多组件,最底部的组件用到了store,意味着我们需要一层层地往下传store(我们不用考虑往上调的情况,但得考虑往下传这种单向数据流的情况):

于是就引入了「react-redux」
总之,如果你直接用「react+redux」的话,那么你就得考虑这个:

话说,为啥 React 最初不内置 Redux 呢?
如果内置了,就不会出现 Redux 和 React-Redux 了
当然,React 最终还是出了 Hooks,而 Hooks 就是为了干掉 Redux 的,不然,React 整个生态就像个智障一样 -> 每一个都不爽,合起来了?还是不爽……
安装:
yarn add react-redux
react-redux,可以让我们随时访问store,而又不出现混乱的问题!
文档:
此文档相较于之前的「Redux」文档,要好那么一点点……
React Redux is the official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.
这段话给的感觉就是「react-redux」是专门为「React」写的……之前的「Redux」是万金油……
React Redux is the official Redux UI binding library for React.
react-redux 是 Redux 与 React 之间的桥梁……

➹:理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux? - Wang Namelos 的回答 - 知乎
这个库就只有 4 个 API

💡:如何讲provider这个 API?
如果先解释provider,我们是听不懂的,但如果不解释它的话,直接干代码,我们又不知道它是干啥用的……
方方的做法:一般就是 CRM,直接干……
我们不知道为啥这样写 provider,但就是无脑复制这个代码:
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { App } from './App'
import createStore from './createReduxStore'
const store = createStore()
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Provider的唯一功能就是传入store对象,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了
做法:
render的时候搞个provider组件包裹住App组件 -> 不这样做,就不起效果App组件,搞个connect(柯里化传参)
actionconnect(mapStateToProps,mapDispatchToProps)(App)返回的结果
stateChanger里边的{n:0}是为了方便后边的App组件取值!
💡:简单理解connect这个 API?

connect(1)(2)
它的读法:connect(1)叫偏函数,因为它的参数不全,所以叫偏……(以偏概全)
按照官网的说法:connect这个函数的作用就是把 Redux store 与 React 组件连接(关联)起来
connect的返回值是容器组件,传给它的参数是给 UI 组件的数据……
➹:Redux 入门教程(三):React-Redux 的用法 - 阮一峰的网络日志
💡:为什么取名mapStateToProps、mapDispatchToProps?
因为:

connect可以把这个参数合并起来作为props传给 App:

💡:react-redux 解决了什么问题?
不用我们一层层地传store了 或者 一层层地调回调了!
💡:bindActionCreators这个 API?

React 的特点 -> 把所有东西都分成函数 -> 用一个东西把它们组合起来 -> 组合起来的这个函数就把传给它的那几个函数调来调去
connect去读取传给Provider组件的store -> 让你不用写dispatch等 -> 也不用写重新 render 了 -> DemoContext API 也能解决 Redux 配合 React 的问题——不用一层层地传数据
这三个例子其实是很重复的,从中我们也可以看出 Redux 社区总喜欢造一些概念,如:
state返回新的state)💡:关于官网那个 ToDo APP 例子?
结构过于复杂,当然,它的复杂程度也就只能写到这儿了,不能再写得更复杂了!

问题缘由:文档

简单来说:
同构是共同构建,首屏后端渲染,后面的是前端渲染。如果你用过 next.js 就明白了,只要你请求一个页面,那就是后端渲染的,但如果你是前端路由跳转渲染页面,那就直接是前端渲染了!
具体点来说:
前后端同构就是 SSR 的一种实现方式
同构的概念最早是 angular2 在 beat 版中提出来的 (2016 年), 那时叫 angular isomorphism,是一个类似于草案的策略, 是为了解决 angular 1.x 时代的 SPA 首屏慢及 SEO 问题而提出的。
同构的核心理念是「客户端」与「服务端」共用一套渲染代码, 客户端自然是 javascript 的, 服务端使用可以识别 javascript 的引擎。 比如:
其他的入 python/.net/go 都有自己各自的方案。
对比其它的:
再来说说 PHP/JSP/ASP 的 SSR, 类似这种的渲染统称为模版引擎渲染。是任何一门 WEB 开发技术栈必须支持的技术, 也是最早期动态网站的根本技术。
当然现在已经有更多功能更丰富的模版引擎了。 比如:
Java 体系下的
Nodejs 体系下的
其实, 相对于同构 SSR, 模版引擎渲染才是 WEB 的主流。同构 SSR 虽然有诸多好处, 但是上手难度还是挺大的, 需要一个横跨前后端的人来做统筹, 目前来说依然算小众。

此项目就是一个 React 同构实现的, 后台通过 RPC 接到 Java, 主要功能就是 SSR
从零开始从头到尾自己纯手搭建就算一个老手也要一周才能完成。利用各种脚手架工具会快很多, 但是可定制化程度较低, 可优化程度也较低。(如果有高手有更好的办法, 可以分享一下。)
最后再来说说提出了前后端同构概念的 angular。angular isomorphism 项目在 angular 2.0 正式发布之后改名叫: angular universal。 目前是前端知名的高开低走的项目, 并不算成功。
➹:前端同构应用和 SSR 有什么区别? - 叛逆的回答 - 知乎
➹:大前端进阶-同构应用 - SegmentFault 思否
➹:魅族官网基于 next.js 重构实践总结与分享 - 知乎
初始状况:

先执行线程 2:

文件就会被加载
但,如果线程 1 先执行呢?

那就会 log 一个错误,说明该文件不存在!
这两种情况的发生就是因为「竞态条件」!
许多 JavaScript 开发人员都遇到了这种竞态条件,即使是在单线程代码中也是如此。你甚至都不需要理解任何关于多线程的东西就可以知道为什么这是一个「race」。
当然,有些竞态条件在单线程代码中是不可能的,但是当您使用多个线程编程并且这些线程共享内存时,就会发生这种情况。
两个线程都在「竞赛」地访问/更改数据 -> 如果 A 第一名,那么皆大欢喜,但如果 B 第一名,那就大家都不好了! -> 总之,谁跑第一,取决于线程调度算法!
为了防止出现「race condition」,我们通常会在共享数据周围设置一个锁,以确保一次只有一个线程可以访问数据!
不加锁:
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
加锁:
// Obtain lock for x
if (x == 5) {
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
Redux 为啥不让组件自己改全局状态而是交给 Reducer?因为改了的话,其它组件都会发生不可预知的错误了……
➹:Avoiding race conditions in SharedArrayBuffers with Atomics - Mozilla Hacks - the Web developer blog
➹:multithreading - What is a race condition? - Stack Overflow
可以!
➹:除 Redux 外,目前还有哪些状态管理解决方案? - 知乎
概述:
取模运算 (mod) 和取余运算 (rem) 两个概念有重叠的部分,但又不完全一致;主要区别在于对负整数进行除法运算时操作不同。取模主要是用于计算机术语中;取余则更多是数学概念。
取模 vs 取余:
计算步骤:
假设有整数 a 和 b(可以是负整数),那么取模/取余运算可以分为两步运算:
c = a/b;r = a - (c*b);a mod b = a - b[a/b] ([a/b]表示整数商)例子:
取模
| 简述 | 商值 | 取模值 | |
|---|---|---|---|
| 5 mod 3 = 2 | 5/3 = 1.66 商取小原则 商=1 | 5 - 3 * 1 = 2 | 2 |
| -5 mod 3 = 1 | -5/3 = -1.66 商取小原则 商=-2 | -5 - (3 * -2) = 1 | 1 |
| 5 mod -3 = -1 | 5/-3 = -1.66 商取小原则 商=-2 | 5 - (-3 * -2) = -1 | -1 |
| -5 mod -3 = -2 | -5/-3 = 1.66 商取小原则 商=1 | -5 - (-3 * 1) = -2 | -2 |
取余
| 简述 | 商值 | 取余值 | |
|---|---|---|---|
| 5 rem 3 = 2 | 5/3 = 1.66 商靠 0 原则 商=1 | 5 - 3 * 1 = 2 | 2 |
| -5 rem 3 = -2 | -5/3 = -1.66 商靠 0 原则 商=-1 | -5 - (3 * -1) = - 2 | -2 |
| 5 rem -3 = 2 | 5/-3 = -1.66 商靠 0 原则 商=-1 | 5 - (-3 * -1) = 2 | 2 |
| -5 rem -3 = -2 | -5/-3 = 1.66 商靠 0 原则 商=1 | -5 - (-3 * 1) = - 2 | -2 |
java 中 % 是取余运算;Python 中 % 是取模运算
💡:模的理解
“模”是指一个计量系统的计数范围;如时钟,12 个整点为计算范围,则模为 12;计算机也是一个计量机器,模为 32 位或者 64 位;
32 位计算机正常理解 在模 范围内能表达的 有 [0, 2³²-1];那么负数该怎么表达呢,所以出现了补码;也就是 正数 + 负数 正好达到模的溢出阀值 2³²;所以在计算机中负数是用补码方式表达的原因;
关于补码的例子:在 12 模的时钟中;假设当前时针指向 10 点,而准确时间是 6 点,调整时间可有以下两种拨法
10-4=6 -> (10-4) mod 12 = 610+8=12+6=6 -> (10+8) mod 12 = 6在以 12 模的系统中,加 8 和减 4 效果是一样的;因此凡是减 4 运算,都可以用加 8 来代替。对“模”而言,8 和 4 互为补数。实际上以 12 模的系统中 11 和 1、10 和 2、9 和 3、7 和 5、6 和 6 都有这个特性;共同的特点是两者相加等于模
“取模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数(取模);任何有模的计量器,均可化为加减法运算
5 mod 3 = 2 例子中;模 为 3;2 为取模的值
💡:计算机中取模应用思想
取模的本质是:取模的值,必定会模的范围内;所以,计算机领域引用该特性,使元素路由算法不超出边界,并有规则存放。
首先确定模(范围);元素取模,使元素有规则的落入模的范围内容器中
如:hashMap、数据库分表、分布式节点路由算法等
➹:原码, 反码, 补码 详解 - ziqiu.zhang - 博客园