Это не интерпретация исходного кода!!!Если хотите посмотреть анализ исходного кода,то думаю можно много погуглить.Конечно исходный код Redux тоже легко понять. Рекомендуется читать напрямую ~ Сам исходный код также прост и понятен,Добро пожаловать для просмотра исходного кода напрямую.
Автор: Чжао Вэйлун
Наконец-то пришло время обновиться, и вторая статья будет выгнана для всех перед фестивалем Цинмин.Я надеюсь, что вы также можете провести праздник, который может обогатить вас. В этот раз я поделюсь этим очень старым стеком фронтенд-технологий (по сравнению со скоростью фронтенд-разработки), говоря, что он стар, это не значит, что у него старая концепция дизайна, а то, что она уже имеет свой исторический отпечаток . Я до сих пор помню, что первый редукс-релиз был в июне 2015 года. На самом деле, он прошел через многое, и это то, что мы видим сейчас. . .
Давайте посмотрим, для чего эта библиотека, прежде чем копаться в истории.Пришло время внимательно прочитать эту мотивацию.Если вы еще не использовали Redux, то я предлагаю вам взглянутьДокументацияПервые четыре главы: мотивация, принцип, три принципа и сравнение с другими идеями~
Если вы уже использовали его, вы можете сразу перейти к художественному фильму, если вы не использовали его, вы можете послушать мои простые слова здесь~ Согласно концепции автора, из-за нашей все более сложной логики и кода интерфейса, и все больше и больше фреймворков и библиотек используют эту концепцию.
UI=f(data)
Включая популярность front-end spa, логике нашего все более сложного проекта будет все труднее управлять состоянием, то есть данными в формуле, и часто эти проблемы вызваны переменными данными (мутациями) и асинхронностью (асинхронность ) самих данных. , автор сравнивает смешанный эффект этих двух проблем с этимэкспериментMentos и Coke, я попробовал их в своем туалете. . Советую не пробовать. . . Концовка часто представляет собой взрывную ситуацию!!!Затем эта библиотека используется для регулирования этого состояния, чтобы им можно было управлять. (Некоторые люди часто говорят, что Redux — это модуль управления глобальным состоянием. Лично я считаю, что это неправда. Он предоставляет нам так называемые спецификации, чтобы можно было контролировать наше состояние. Он поддерживает уникальное состояние, и все данные находятся здесь. , но не обязательно, глобальный он или нет!)
После разговора о его собственной идее, давайте поговорим о его мысленном путешествии:
Прошлая жизнь:
От самого раннего релиза 0.2.0 до 4.0.0 мы можем наблюдать ментальное путешествие автора Dan gaearon (также одного из авторов открытого ПО, которым я лично восхищаюсь) Самый ранний Redux уже не тот, что сейчас (хотя я тоже с 3.1.0 начал пользоваться~)
Самая ранняя версия (я подумал, что реагирование защищает ваш собственный поток данных в одностороннем потоке, github также имеет различные библиотеки типа, которые вы основаны на этой концепции) redux, также реализуется на основе концепции потока. До 1.0.0. Сегодня содержание библиотеки React-redux, самоконтролируемых компонентов с высоким порядком, аналогично разъему, поставщику, выполните соединение Redux и реагируют. (Целью автора также очевидна, я надеюсь, что вы сможете использовать Redux в React React Reachless. В то время библиотека потока очень высоко в функциональном программировании, поскольку концепция поток, поскольку этот вид функционального программирования заимствовал, также думал об этом. Мы упоминали, что все потоки данных включают в себя контроль логики, это функциональная концепция функции PRUE, которая Также так же, как оригинальное намерение грамматики JSX. Но как только вы бросите такую теорию, вам нужна ваша аудитория, чтобы принять эту концепцию.
+--------+ +------------+ +-------+ +----+
| Action | +----------->+ Dispatcher +----------->+ Store +----------->+View|
+--------+ +-------+----+ +-------+ +-+--+
^ |
| |
| |
| |
+----------------------------------------+
Мы можем обнаружить, что поток данных является односторонним, что является основной концепцией Flux, и Redux следует этой концепции, но есть некоторые различия, в чем разница?
+----------+
| | +-----------+ sliceState,action +------------+
| Action |dispacth(action)| +--------------------------------> |
| +----------------> store | | Reducers |
| Creators | | <--------------------------------+ |
| | +-----+-----+ (state, action) => state +------------+
+-----^----+ |
| |
| |subscribe
| |
| |
| |
| +--------+ |
+--------------+ View <---+
+--------+
Из рисунка видно, что здесь нет Dispatcher, но есть еще один Reducer.Один момент, который я должен упомянуть здесь, заключается в том, что все архитектуры Flux поддерживают состояние в соответствии с концепцией так называемого Predictable, поэтому как добиться предсказуемости То есть, мы должен гарантировать, что наше состояние является неизменяемым (данные неизменны). Flux полагается на диспетчера для распространения и гарантии неизменности Entity, в то время как Redux полагается на концепцию чистой функции, чтобы гарантировать, что каждое состояние является моментальным снимком исходного состояния. Практика этой основной формулы (состояние, действие) => состояние, так что ваши редукторы на самом деле представляют собой любое количество таких функций, и вам нужно подумать о том, как разделить эти функции. И если это чистая функция, это также поможет нам повторно использовать функции и проводить модульное тестирование. Это концепция, которую Redux заимствует из концепции функционального программирования. Если вы знакомы с Elm, вы должны знать концепцию модели. Чтобы обновить модель и сопоставить ее с представлением, вам нужен модуль обновления для обновления вашей модели. Здесь Redux заимствует концепцию средства обновления. Разделяйте и повторно используйте редюсеры. Если вас также интересует Вяз, вы можете прочитатьздесь.
Фактически, концепция функционального программирования также проходит через исходный код, например, реализацию в нем компоновки и промежуточного программного обеспечения, вы можете обратиться к этимисходный код, интересно то, что даже если первоначальный автор не знаком с некоторыми концепциями функционального программирования, он также следует некоторым pr-мнениям в некоторых реализациях, таких как реализация compose:
Из самого раннего reduceRight to reduce можно обнаружить, что автор, перепробовавший три основные версии и несколько младших версий, до сих пор не понимает, что выполнение функций справа налево можно обойтись без reduceRight.Заинтересованные студенты могут попробовать.Когда я это увидел pr, меня тоже удивило, что у автора хватило навыков Лиспа или Хаскеля, чтобы иметь такую интуицию!!(На самом деле функциональное программирование действительно может тренировать режим логического мышления и вашу математическую осведомленность, но на самом деле не более того, оно не принесет существенного улучшение так называемой производительности и читабельности) Для полноты и развязки функций в предыдущей версии также были внесены коррективы в жесткую связанность React.Вышеупомянутые коммуникационные компоненты высокого порядка упоминаются отдельно и библиотека react-redux поддерживается отдельно, так что сам Redux более чисто. управляй этим.
эта жизнь:
Просмотрев предыдущую жизнь, давайте взглянем на сегодняшний Redux.После итераций, основанных на нескольких версиях и практике каждого, будь то из самой концепции или из лучших практик, в том числе некоторых Redux-основанных на Github.Инкапсуляция уже по умолчанию лучшие практики и спецификации использования, поэтому давайте посмотрим на сценарии и методы, используемые сегодня самим Redux. Из приведенной выше концепции мы можем видеть, что как разделить редьюсер и поддерживать это единственное неизменное состояние — это самая важная вещь, на которой мы должны сосредоточиться при использовании редукции. Давайте сосредоточимся на лучших практиках использования Redux в React: (В реальных сценариях приложений большинству из нас следует использовать метод разработки Redux+React. Если вы все еще новичок в Redux, вам следует прочитатьздесь). Для того, чтобы обсудить некий официальный характер, давайте посмотрим на официальные документы (буду делать замечания и уточнения в том, что я считаю более личным), Сосредоточьтесь на следующих трех аспектах:
- Управление и поддержание уникального состояния
- Как разделить редукторы
- Асинхронная обработка состояния
Зачем сначала говорить о редьюсерах? Потому что наше состояние на самом деле состоит из редюсеров. Как видно из рисунка выше, (состояние, действие) => состояние — это формула для вычисления состояния, и API createStore() также принимает ваш редьюсер. для генерации состояния. Давайте сначала посмотрим на самый внешний слой, который нам нужен для создания состояния инициализации для состояния:
// 官网说无非两种方式
// 最外层你有一个reducer:
function rootReducer(state = 0, action) { // 在你的createStore第二个参数没有的情况下,你是需要给state一个默认值
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
// 通过官方提供的combineReducer去生成这个rootReducer, 其实你观看源码的话这个方法return的还是一个 (state, action) => {}的函数
function a(state = 'zwl', action) { // 在你的createStore第二个参数没有的情况下,你是需要给state一个默认值
return state;
}
function b(state = 'zwt', action) { // 在你的createStore第二个参数没有的情况下,你是需要给state一个默认值
return state;
}
const rootReducer = combineReducers({ a, b })
Так как инициализация, мы видим, что вышеупомянутая формула редукции может инициализировать ваше состояние, а другой поток данных обратный. официальный документ Как упомянуть так называемое состояние обработки состояния, в документе будет упомянута спецификация обработки самого состояния из трех мест, а именно
- Вторая половина редукционной инфраструктуры
- нормализация состояния
- Обновление нормализованных данных
Конечно, я думаю, что автор уже ясно сказал очень четко. Конец статьи также дал много ссылок, но все еще необходимо подвести итоги государственной парадигмы этой спецификации.
// 首先先看下这里的 state 基本结构,当然文档中也没有限制你,鼓励你根据自己的业务形态去定制,但是却是有些比较好的实践方式
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
// 区分领域的数据, 并且可能会有两种非领域数据类型,一种是页面上一些ui状态比如一个 button 是否展示的 boolean 值,这时候你会发现所谓的 sliceState 可能就是一个 domainData 或者是它下面的一个更小的分支,这个是根据你的 reducer 拆分规则指定的, 但是你可以想象下如果你的 data 是单纬数据结构或者简单数据结构,它就会非常好做逻辑计算,比如你有[a, b, c]单纬数组就比[{},{},{}]要好删查改除!
{
domainData1: {},
domainData2: {},
appState1: {},
appState2: {},
ui: {
uiState1: {},
uiState2: {},
}
}
// 经过网络上一些经验包括笔者自己的经验,你的基本数据类型往往会遵循一个数据原则为了尽可能维护最小的单元的数据,数据共享的部分会放在一起维护,至于如何范式化这个 state 后面也会提到
{
domainData1: {},
domainData1ID: [],
domainData2: {},
domainData2ID: [],
entites:{ //这里存放你需要共享数据的部分,但是仅仅是实例, 这里的实例的引用往往放在外面, 遵循的原则是实例和引用分开并且如果实例里有
//引用domainData1里的东西那么其实引用的也是id,你会存一个引用的id进去
commonData1: {},
commonData2: {},
},
commonData1ID: [],
commonData2ID: [],
ui: {
uiState1: {},
uiState2: {},
}
}
Позвольте мне рассказать о проблеме нормализации состояния:
// 文档中列举了一个博客数据的例子(当然其实这个数据结构已经挺复杂的了)
const blogPosts = [
{
id: "post1",
author: {username: "user1", name: "User 1"},
body: "......",
comments: [
{
id: "comment1",
author: {username: "user2", name: "User 2"},
comment: ".....",
},
{
id: "comment2",
author: {username: "user3", name: "User 3"},
comment: ".....",
}
]
},
{
id: "post2",
author: {username : "user2", name : "User 2"},
body: "......",
comments: [
{
id: "comment3",
author: {username : "user3", name : "User 3"},
comment: ".....",
},
{
id: "comment4",
author: {username : "user1", name : "User 1"},
comment: ".....",
},
{
id: "comment5",
author: {username : "user3", name : "User 3"},
comment: ".....",
}
]
}
// 重复很多遍
]
// 其实这里我们可以想象一下,如果我们需要更新这个数据结构,假如说直接把这个数据挂在 state 上。 那就会出现这种情况的代码[...state, {...slice[comments], ...sliceUpdater}]或者嵌套更深的更新方式,首先我们知道无论是扩展运算符和Object.assign都是浅拷贝,我们往往需要对嵌套结构每一个层级都去更新,如果操作数据结构就更加不方便了我们需要根据每个层级找到相应嵌套比较深的数据结构然后进行操作。这也就是为什么我前面说我们尽量维持单维度的数据结构原因
// 文档中建议我们拍平数据后得到这样的数据结构
{
posts: {
byId: {
"post1": {
id: "post1",
author: "user1",
body: "......",
comments: ["comment1", "comment2"]
},
"post2": {
id: "post2",
author: "user2",
body: "......",
comments: ["comment3", "comment4", "comment5"]
}
}
allIds: ["post1", "post2"]
},
comments: {
byId: {
"comment1": {
id: "comment1",
author: "user2",
comment: ".....",
},
"comment2": {
id: "comment2",
author: "user3",
comment: ".....",
},
"comment3": {
id: "comment3",
author: "user3",
comment: ".....",
},
"comment4": {
id: "comment4",
author: "user1",
comment: ".....",
},
"comment5": {
id: "comment5",
author: "user3",
comment: ".....",
},
},
allIds: ["comment1", "comment2", "comment3", "commment4", "comment5"]
},
users: {
byId: {
"user1": {
username: "user1",
name: "User 1",
}
"user2": {
username: "user2",
name: "User 2",
}
"user3": {
username: "user3",
name: "User 3",
}
},
allIds: ["user1", "user2", "user3"] // 这里官方推荐把相应的id放在层级内。这个地方其实都可以也可以像我前面提到的放在users平级的地方,这个取决你的项目具体而定
}
}
// 可以发现不同数据之间都被打成平级的关系,不需要去处理深层嵌套结构的问题,在给定的ID里去删查改除都比较方便!这里更新的话也是不会波及到别的 domainComponent 比如我们只是更新 users 里的信息只需要去更新 users > byId > user 这部分去做浅复制,它不会像上面那种嵌套数据结构整体更新影响别的相应渲染组件也去更新,这里其实还有一个优化点我们后面会说,就是我们在选择这个 sliceState 的时候, 从选择的 selector 不做重复运算。
Здесь рекомендуется использовать плоский методNormalizrСамостоятельно написать не невозможно, но ситуаций будет много, и эта сторонняя библиотека все же может решить эту проблему лучше. Здесь снова у этого нормализатора есть метод денормализации, который вам удобен, чтобы вернуть нормализованную структуру данных обратно. Похоже ли это на процесс объединения таблиц в базе данных Paradigm? Если вы знакомы с дизайном нормализованной базы данных, вы можете подумать, что это немного похоже на концепцию нормализованной базы данных, но на самом деле нет строгого определения, чтобы следовать дизайну нормальной формы, самое главное, что вам нужно найти обычная структура формы, которая вам подходит. Здесь автор также дает некоторые ссылки в документе (конечно, вам не нужно сначала изучать концепцию базы данных), вы можете просто понять эти концепции, включая дизайн базы данных «многие ко многим»:
Так как ранее было сказано, что sliceState нужен селектор, выбираем соответствующий слайс из состояния (кстати, упоминается и селектор, не требующий повторных операций для упомянутой выше небольшой оптимизации, который здесь и будет использоваться.библиотека):
// 首先你的sliceState需要去state选择相应的分片大多时候你都是
const usersSelector = state.users
const commonsSelector = state.commons
// 但是你会发现有些值是通过两个selector计算而来的,我们就拿reselect官网的第一个例子来看下
import { createSelector } from 'reselect'
const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc + item.value, 0)
)
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
export const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax })
)
let exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.20 },
{ name: 'orange', value: 0.95 },
]
}
}
console.log(subtotalSelector(exampleState)) // 2.15
console.log(taxSelector(exampleState)) // 0.172
console.log(totalSelector(exampleState)) // { total: 2.322 }
// 这里使用reselect的作用是如果下次传入的shopItemsSelector,taxPercentSelector 并没有改变那么这个selector不会重新计算,这个大家有兴趣可以看下源码,本身源码也不多很容易看完!
Концепция, упомянутая выше и состояние Селектор может видеть, сколько самого состояния является только читаемой (только для чтения), не модифицируемым, при следующем мне, как разделить наш редуктор функций, его устава и как (чиновник сказал там после редуктора):
- reducer
- root reducer
- slice reducer
- case function
- higher-order reducer
Вы можете обратиться к конкретному определениюздесь, На мой взгляд, его не нужно так мелко делить, основная функция функции — помочь нам разделить логику и добиться эффекта повторного использования, поэтому разделение редьюсера — основная концепция. Конкретная логика разделения может относиться кздесь, я тут не прикалываюсь, случай с документом достаточно ясен. Это, наверное, лучший документ, который я видел. Скажу сразу, что автор старательный и старательный человек!!!
Здесь мы поговорим о том, как работать с редьюсерами в некоторых особых сценариях.Конечно, в документе все еще говорится вздесьКак поступить с редьюсерами, которым нужны межсегментные данные? С точки зрения непрофессионала, нам нужен редюсер sliceStateA для обработки данных в sliceStateB:
// 第一种方式
// (还记得我们开头说的 Initinalizing state 的方式吗? 下面两种方式就是利用这点)
function combinedReducer(state, action) { // 在 root 层去拿最外面的 state 把相应需要的 sliceState 传给相应需要的 reducer
switch(action.type) {
case "A_TYPICAL_ACTION": {
return {
a: sliceReducerA(state.a, action),
b: sliceReducerB(state.b, action)
};
}
case "SOME_SPECIAL_ACTION": {
return {
// 明确地把 state.b 作为额外参数进行传递
a: sliceReducerA(state.a, action, state.b),
b: sliceReducerB(state.b, action)
}
}
case "ANOTHER_SPECIAL_ACTION": {
return {
a: sliceReducerA(state.a, action),
// 明确地把全部的 state 作为额外参数进行传递
b: sliceReducerB(state.b, action, state)
}
}
default: return state;
}
}
// 第二种方式
const combinedReducer = combineReducers({
a: sliceReducerA,
b: sliceReducerB
});
function crossSliceReducer(state, action) {
switch(action.type) {
case "SOME_SPECIAL_ACTION": {
return {
// state.b是额外的参数
a: handleSpecialCaseForA(state.a, action, state.b),
b: sliceReducerB(state.b, action)
}
}
default: return state;
}
}
function rootReducer(state, action) {
const intermediateState = combinedReducer(state, action);
const finalState = crossSliceReducer(intermediateState, action);
return finalState;
}
// 这都是官方推荐的方法, 但是你会发现万变不离其中,都需要从根部 root 去拿 state 达到共享数据的方式,并且无论是 combineReducers 还是 function 的方式都是要 Initinalizing state 的
Напоследок кратко обсудим вопрос асинхронности.Прежде всего, в исходниках ранней версии Redux учитывалось асинхронное решение.Привычный нам redux-thunk это конечно то же самое, что и react-redux, который был выделен в отдельный проект, о его выделении упоминается только в документации. На самом деле на рынке существует множество асинхронных решений на базе Redux, Redux-thunk должно быть достаточно для решения разных сценариев, но все же есть очень сложные сценарии запросов, для решения которых могут потребоваться следующие две популярные библиотеки:
Для этих двух программ нет ничего хорошего или плохого, во-первых, они решают одну и ту же задачу, но две совершенно разные концепции.
первый вариант
Используйте генератор для решения асинхронных проблем и определите множество API для решения асинхронных проблем в различных сложных сценариях. Например: [позвонить, поставить, отменить,...] Методов много, об этой редукционной саге тезис таков:официальная документацияИли разнообразных туториалов в сети уже много, этой ерунды не буду.
Второй вариант
Метод rx.js используется для решения асинхронной проблемы, а библиотека rx.js в основном является реализацией реактивного программирования, которая принадлежитreactivexОдна из ветвей использует концепцию потоков для решения задач асинхронного программирования. Это очень большая тема, и у нас есть возможность открыть специальную тему для обсуждения этого rx.js. Хотя его изучение само по себе будет иметь больше API, чем редукционная сага, может быть куча концепций, которые раньше не затрагивались для понимания. Но ценность изучения самого rx.js определенно будет полезнее, чем redux-saga с точки зрения будущих возможностей.
Однако автор также решит эту проблему в соответствии с бизнесом и командой.Если учитывать стоимость обучения и стоимость разработки, сама по себе редукс-сага может быть более подходящей для крупномасштабных проектов и групп обслуживания из нескольких человек. Так что решать вам, какой способ больше подходит именно вам!
Наконец, давайте взглянем на некоторые официально рекомендуемые методы работы с каталогами проектов.здесьВы также можете увидеть более полный подход! Я рекомендую первый подход: определить действия, редукторы (с соответствующими селекторами в них), константы (actionTypes), компоненты, контейнеры, что я думаю, более понятно. Сказал так много уже зрелых лучших практик. Стоит ли представлять будущее?
Загробная жизнь:
На самом деле, я также упоминал в прошлой статье, что основная идея самого React должна быть совместима с односторонним потоком данных (из-за существования нового контекстного API!) Если вы не знакомы с этим контекстом, вы можете обратиться кReact blogЗдесь я просто воображаю, это только мое личное мнение и не может отражать какую-либо будущую тенденцию развития.
// 上一篇文章我们利用 context 去实现 react-redux 的时候我们利用 context 传递了 redux 本身的 store,具体的 provider 和 connect 可以参考上一篇文章
// 我们自己实现的 store应该是这样的。(全部凭自我意淫。。。可以看个思路)
export const makeStore = (store) => {
let subscriptions = []
const Context = createContext()
const getState = () => store.initialState ? store.initialState : {} // 拿到当前的 state
const subscribe = fn => {
subscriptions = [...subscriptions, fn]
}
// 这里把 Provider 和 Connect 拿进来,他们俩分别使保存这里 store 和把 mapStateProps 以及 actions 传递进去
class Privider... // 一个维护Context.Provider 负责传递 store,更改store
class Connect... // 一个负责消费的Context.Cousumer 传递给你的组件相应的state,和actions
// 这里我还没想好如何维护整体代码结构。。
}
Когда я собирался опубликовать статью, кто-то уже проделал такую работубиблиотека, то у меня может быть только волна Amway. Надеюсь, все увидят направление, а не полностью отвергнут Redux. Потому что все-таки мы не очень готовы его заменить, и я считаю, что если очень хочется его заменить, то в существующих проектах и новых проектах может быть много подводных камней, но как говорится, как же без этого продвигаться вперед. наступать на ямы? ?(Приглашаю всех наступить на ямы хахахаха!!!!)
Напишите последние слова:
Независимо от того, испытываем ли мы технологию или инновацию в технологическую эпоху. На самом деле самое главное часто это процесс.Если мы игнорируем процесс и заботимся только о результатах, то все кажется недоделанными блюдами - скучно, и тогда возвращаясь к самому Redux, максимум что он нам приносит, это своего рода Соглашение (Если вы следите за статьей, вы должны понять!) Как повысить удобочитаемость и снизить затраты на обслуживание в современных командных проектах, состоящих из нескольких человек, — это также тема, которую инженеры всегда обсуждали. Конечно, так называемые лучшие практики — это не что иное, как те, которые мы действительно используем для преобразования из серверной части или других отраслей или из других отраслей после того, как мы на самом деле их применяли. Так называемая важность аналогии!Наконец, я надеюсь, что читатели будут продолжать обращать внимание на мои личные обновления и обновления команды!!!Я надеюсь, что мы сможем двигаться вперед вместе на волне технологий.