❤ отметьте меня, если вам нравится концепт ^_^
Управление состоянием является общей темой в мире фронтенда.Разработка всех фронтенд фреймворков неотделима от итерации и замены управления состоянием.Для реагирования развитие всего управления состоянием также следует за изменениями и новыми фичами архитектура реакции.Как разработчик, который вырос с реакцией почти 5 лет, испытав рефлюкс, редукс, мобкс и другие производные решения редукции dva, зеркало, рематч и т. д., я думаю, что они не являются я хочу окончательную форму управления состоянием, поэтому, чтобы создать решение для управления состоянием, которое было бы наиболее элегантным, простым в использовании и наиболее эффективным для работы с React, я отправился в погоню за мечтой.
Зачем нужно государственное управление
Зачем нужно вводить управление состоянием во front-end reference?В принципе все пришли к единому мнению.Здесь я резюмирую его в 3 пункта:
- По мере увеличения масштаба приложения и усложнения функций детализация абстракции компонентов будет становиться все тоньше и тоньше, а слои будут становиться все глубже и глубже после объединения в представлении, что может быть удобно.Совместное использование состояния между компонентамистать насущной потребностью.
- Состояние тоже нужно разделить на модули, логика смены состояния на самом деле наша бизнес-логика.Разделение пользовательского интерфейса и бизнеса, что способствует повторному использованию логики, а также непрерывному обслуживанию и повторению.
- Если состоянием можно централизованно управлять и разумно распределять, это выгодно компоненту.Обновление по требованию, что уменьшает диапазон рендеринга и, таким образом, повышает производительность рендеринга.
Состояние существующих решений по управлению состоянием
redux
Решение для управления состоянием, которое следует неизменной идее реакции, будь то звездный рейтинг git или процветание сообщества, первая рекомендация должна бытьredux
Это первый брат управления состоянием в мире реагирования, и ограничение использует уникальный путь.reducer
Чистые функции используются для изменения данных хранилища, чтобы поток состояний всего приложения был понятным и отслеживаемым.
mbox
Следите за отзывчивым поздним шоуmbox
, придумалcomputed
,reaction
концепция, ее официальный лозунгВсе, что может быть получено из состояния приложения, должно быть получено, превратив исходный простой объект json в наблюдаемый, мы можем напрямую изменить состояние,mbox
Будет автоматически запускать обновления рендеринга пользовательского интерфейса из-за его адаптивной концепции иvue
очень близко, вreact
соответствоватьmobx-react
После использования многие шутилиmobx
это воляreact
становится классомvue
Схема управления состоянием опыта разработки.
Конечно, потому чтоmbox
Это очень удобно для работы с данными, и это не соответствует требованию четкого и отслеживаемого пути потока состояния в крупномасштабных приложениях.Чтобы ограничить поведение пользователя при обновлении,mobx-state-tree
,в общем,mobx
Станьте отзывчивым представителем.
разное
Остальные решения по управлению состоянием в основном делятся на три категории.
Класс не устраиваетredux
Код избыточен и многословен, а интерфейс недостаточно дружелюбен.redux
Сделайте два пакета сверху, которые обычно представляют собой иностранные пакеты, такие какrematch
, внутренний какdva
,mirror
д., я называю ихredux
Производные семейные произведения или интерпретацииredux
Исходный код, интегрируйте свои собственные идеи и переделайте библиотеку, такую какfinal-state
,retalk
,hydux
и т. д., я называю их классамиredux
работай.
Одна категория — это программы, которые выбирают адаптивный путь, иmobx
Например, захват обычных объектов состояния в наблюдаемые объекты, такие какdob
, я называю их классамиmobx
работай.
Остальное - использоватьreact context api
или последнийhook
Функции, ориентированные на легкие, простые в использовании и несложные решения, такие какunstated-next
,reactn
,smox
,react-model
Ждать.
Мое идеальное решение
Все вышеперечисленные родственные решения могут в определенной степени удовлетворить наши потребности, но для программиста-Водолея, стремящегося к совершенству, я думаю, что они все-таки не мои идеальные решения.Они либо маленькие и красивые, либо большие и законченные. все еще недостаточно силен и недостаточно дружелюбен, поэтому я решил запустить самостоятельно разработанное решение для управления состоянием.
Я знаю, что маленькие и красивые, полные и сильные находятся в конфликте друг с другом.Я могу принять определенное количество больших, и от 10кб до 20кб после gzip-это диапазон, который я принимаю.Достигаются следующие цели, тем самым отражая различия и преимущества существующей структуры государственного управления.
- Когда его используют новички, им не нужно понимать новый API функций, и они не знают о существовании управления состоянием.
- Это позволяет ветеранам использовать недавно предоставленные API-интерфейсы функций в сочетании с их существующими знаниями об управлении состоянием, восстанавливать лучшие практики, признанные различными сообществами, и в то же время продолжать исследовать и совершенствовать, чтобы использовать больше преимуществ, связанных с управлением состоянием.
- существует
react
имеютhook
После этой функции как компоненты класса, так и компоненты функции могут пользоваться одним и тем же мышлением и согласованным управлением состоянием доступа к API, не создавая ощущения разделения. - На основе соблюдения вышеуказанных 3 пунктов пользователи могут использовать более упорядоченную и более интуитивную организацию для написания кода, и в то же время они также могут получить огромные преимущества в повышении производительности.
Для достижения вышеуказанных целей проектconcent
, который определяется какПредсказуемое, нулевое, прогрессивное, высокопроизводительное решение для расширенного управления состоянием, с нетерпением ожидая полировки его в реальную и настоящую структуру, которая заставит пользователей чувствовать себя красивыми, всеобъемлющими и мощными.
Говоря человеческим языком, он достаточно прост для понимания, достаточно элегантен для кодирования, достаточно надежен для инженерной архитектуры и достаточно хорош для использования... ^_^
предсказуемый
react
основывается наpull based
Чтобы сделать структуру пользовательского интерфейса для обнаружения изменений, для пользователя вам нужно явно вызватьsetState
позволитьreact
Ощущается изменение состояния, поэтомуconcent
Следуйте классическому принципу неизменности, реагируя на предсказуемость, и не используйте перехватывающие объекты для преобразования в наблюдаемые объекты для обнаружения изменений состояния (или снова становитесь классом).mobx
......), ни глобальный при использованииpub&sub
режим для управления обновлениями связанных представлений, а также для настройки различныхreselect
,redux-saga
Подождите, пока промежуточное программное обеспечение решит проблемы с кешем вычислений, асинхронным действием и т. д. (Если это так, не будет ли это точкой невозврата для ковшового колеса семейства Redux...)
Tucao: Обширная гранулярность подписки Redux - это все больше и больше компонентов, а состояние все более и более сложное, часто из-за того, что компоненты подписываются на ненужные данные и вызывают избыточные обновления, а различные рукописные карты XXXToYYY раздражают. это не может повредить...
Нулевое вторжение
Как упоминалось выше, ожидается, что новички смогут воспользоваться преимуществами, которые дает управление состоянием, только организовав код в соответствии с идеей реакции, поэтому это должно быть возможно только вsetState
На самом деле, чтобы поднять шумиху вокруг вышесказанного, мы можем поставитьsetState
В качестве важной записи для выдачи инструкций по рендерингу (помимо этого естьforceUpdate
).
Внимательно посмотрите на картинку выше, если вы обнаружите какие-то неточные описания, давайте взглянем на официальнуюsetState
Описание сигнатуры функции:
setState<K extends keyof S>(
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
callback?: () => void
): void;
Из описания подписи видно, чтоsetState
является частичным состоянием (состоянием фрагмента), на самом деле мы вызываемsetState
Это тоже часто делается, и кто его изменит, тот пройдет соответствующийstateKey
и значение.
реагировать автоматически преобразует частичное состояниесливатьсяв исходный объект всего состояния, чтобы перезаписать его соответствующее старое значение, а затем запустить соответствующее обновление представления.
Так что я просто могу позволитьsetState
Отправляя состояние себе, я также могу отправить его в хранилище и распространить на другие соответствующие экземпляры для достижения своей цели.
Очевидно, нам нужно угнатьsetState
, чтобы внедрить свою собственную логику, а затем вызвать原生setState
.
//伪代码实现
class Foo extends Component{
constructor(props, context){
this.state = { ... };
this.reactSetState = this.setState.bind(this);
this.setState = (partialState, callback){
//commit partialState to store .....
this.reactSetState(partialState, callback);
}
}
}
Конечно, как поставщик фреймворка, он точно не позволит пользователямconstructor
Для завершения этой дополнительной логики внедрения разработаны два ключевых интерфейса.run
а такжеregister
,run
Отвечает за загрузку конфигурации модуля,register
Отвечает за регистрацию компонентов для установки их собственных модулей, а зарегистрированные компонентыsetState
Он был улучшен, и его состояние отправки может не только инициировать обновления рендеринга, но также напрямую отправляться в хранилище и распространяться среди других экземпляров этого модуля.
Хотя хранилище представляет собой единое дерево состояний, фактическая бизнес-логика состоит из множества модулей, поэтому я использую ключ первого уровня хранилища в качестве имени модуля (аналогично пространству имен), что порождает концепцию модуля.
//concent代码示意
import { run, register } from 'concent';
run({
foo:{//foo模块定义
state:{
name: 'concent',
}
}
})
@register('foo')
class Foo extends Component {
changeName = ()=> {
this.setState({ name: e.currentTarget.value });//修改name
}
render(){
const { name } = this.state;//读取name
return <input value={name} onChange={this.changeName} />
}
}
Теперь давайте взглянем на приведенный выше код, за исключением того, что не показано наFoo
Состояние объявлено в компоненте, и другие места, кажется, дают вам ощущение: разве это не стандартный способ написания компонентов React? concent снижает стоимость управления состоянием доступа практически до незначительного уровня.
Конечно, вам также разрешено объявлять другие немодульные состояния в компоненте, чтобы они были эквивалентны частному состоянию, еслиsetState
Зафиксированное состояние содержит как модульные, так и немодульные состояния, и состояние модуля будет рассматриваться какsharedState
Извлеченный и переданный другим экземплярам, privName передается только самому себе.
@register('foo')
class Foo extends Component {
state = { privName: 'i am private, not from store' };
fooMethod = ()=>{
//name会被当做sharedState分发到其他实例,privName仅提交给自己
this.setState({name: 'newName', privName: 'vewPrivName' });
}
render(){
const { name, privName } = this.state;//读取name, privName
}
}
Учитывая, что это составное состояние будет иметь определенную неоднозначность при стыковке с ts, концентрат позволяет отображать полное состояние объявления.Когда ваше состояние содержит stateKey с тем же именем, что и симуляция, к которой оно принадлежит, вдо первого рендераЭти значения stateKey будут перезаписаны соответствующими значениями в состоянии модуля.
@register('foo')
class Foo extends Component {
// name对应的值在首次渲染前被替换为模块状态里name对应的值
state = { name:'', privName: 'i am private, not from store' };
render(){
// name: 'concent', privName: 'i am private, not from store'
const { name, privName } = this.state;
}
}
В таком шаблоне вы можете создать несколько экземпляровFoo
, любой экземпляр изменяетсяname
значение, другие экземпляры будут обновлены, и вам не нужно обертывать корневой компонент верхнего уровня, напримерProvider
вспомогательный тег для внедрения в контекст магазина.
Был в состоянии достичь этого эффекта, спасибоconcent
Основной принцип работытег зависимости,коллекция цитат,государственное распределениеОни будут упомянуты в следующем описании.
прогрессивный
способный пройти какsetState
Как вход для управления состоянием доступа и для того, чтобы отличить общее состояние от частного состояния, это значительно повышает удобство нашей работы с данными модуля, но достаточно ли этого и достаточно ли хорошо?
Более детальный контроль над потреблением данных
Гранулярность потребления компонентом состояния модуля не всегда очень грубая и напрямую соответствует модулю, то есть компонент, принадлежащий модулю fooCompA
может потреблять только модули в foof1
,f2
,f3
Значения, соответствующие трем полям, принадлежащим компоненту модуля fooCompB
может потреблять только другие модули в foof4
,f5
,f6
Значения, соответствующие трем полям, мы уж точно не ожидаемCompA
Экземпляры только модифицированныхf2
,f3
был запущенCompB
Рендеринг экземпляра.
В большинстве случаев мы ожидаем, что компоненты и модули будут поддерживать отношения один к одному, то есть компонент использует только данные, предоставленные определенным модулем, но на самом деле есть компонент, который использует данные из нескольких модулей.
Таким образом, дляregister
интерфейс, нам нужно передать больше информации, чтобы удовлетворитьПотребности в более детальном потреблении данных.
- пройти через
module
Отметьте, к какому конкретному модулю относится компонент
Это необязательный параметр, если вы его не укажете, пусть он относится к встроенному
?default
модуль (пустой модуль), сmodule
, вы можете позволить концентрату внедрить состояние модуля в состояние экземпляра после создания экземпляра его компонента.state
вверх.
- пройти через
connect
отметить другие подключенные модули
Это необязательный параметр для использования пользователями.
connect
Параметры, чтобы пометить другие подключенные модули, установить наблюдаемый диапазон stateKey в других модулях.
- пройти через
ccClassKey
Установить текущее имя класса компонента
Это необязательная опция, после настройки удобно
react dom tree
Просмотрите именованный узел компонента concent вверху, если он не установлен, concent будет автоматически изменяться в соответствии с егоmodule
а такжеconnect
Значение параметра рассчитывается как 1. В это время один и тот же модуль регистрируется и помечается одним и тем жеconnect
Различные реагирующие компоненты параметров находятся вreact dom tree
Все, что вы видите выше, это одно и то же имя тега.
Отметьте компонент с этими ключевыми параметрами, предоставленными вышеуказанным регистром, и завершитеconcent
Очень важная часть основного принципа работы:тег зависимости, поэтому, когда эти компоненты создаются как потребители данных, они уже содержат достаточно информации, чтобы потреблять требуемые данные с большей степенью детализации.
отstore
Отношения между классами и модулями с точки зрения
примерstate
Так как контейнер данных уже содержал состояние модуля, которому он принадлежит, то при использованииconnect
Как следует вводить эти данные, когда компонент подключен к нескольким другим модулям? Вслед за этим вопросом давайте вспомним то, что мы упоминали выше, экземпляр, вызывающийsetState
Представленный статус будетconcent
Который принадлежит к состоянию модуля добычи, как оноsharedState
Точное распределение на другие экземпляры.
Точное распределение может быть достигнуто, поскольку при создании экземпляров этих зарегистрированных компонентовconcent
создаст для него контекст экземпляраctx
, экземпляр соответствует уникальномуctx
,Потомconcent
Этиctx
Ссылки тщательно хранятся в глобальном контекстеccContext
in (одноэлементный объект, inrun
при создании), поэтому процесс создания экземпляра компонента завершен.concent
Очень важная часть основного принципа работы:коллекция цитат, разумеется, после уничтожения экземпляра соответствующийctx
также будет удален.
имеютctx
объект,concent
Естественно реализовать на нем различные функции.Для компонентов, упомянутых выше, которые соединяют несколько модулей, данные модуля будут вводиться вctx.connectedState
Затем получите соответствующие данные через конкретное имя модуля.
Мы можем легко создавать компоненты, которые потребляют данные из нескольких модулей в коде, и контролировать степень детализации потребления в соответствии с stateKey.
//concent代码示意
import { run, register, getState } from 'concent';
run({
foo:{//foo模块定义
state:{
name: 'concent',
age: 19,
info: { addr: 'bj', mail: 'xxxx@qq.com' },
}
},
bar: { ... },
baz: { ... },
})
//显示的设定ccClassKey名称,方便查看引用池时知道来自哪个类
@register({module:'foo' }, 'Foo2')
class Foo2 extends Component { ... }
// 属于foo模块,同时也连接bar、baz两个模块
@register({
module:'foo',
connect: ['bar', 'baz']
}, 'Foo2')
class Foo2 extends Component {
render(){
//获取到bar,baz两个模块的数据
const { bar, baz } = this.ctx.connectedState;
// 读取了barKey1,barKey1,当前实例数据的数据依赖是['barKey1', 'barKey2']
// 即仅当模块里的'barKey1', 'barKey2'发生变化时,才会触发当前实例渲染
const { barKey1, barKey2 } = bar;
}
}
Как упоминалось выше, точное распределение может быть достигнуто, поскольку состояние всех экземпляров является прокси-объектом, который помогает зависимостям данных, требуемым всеми этими экземплярами во время выполнения, для дальнейшего достижения точных обновлений.
Разделение пользовательского интерфейса и бизнеса
Как упоминалось в начале статьи «Зачем нам нужно управление состоянием», логика изменения состояния на самом деле является нашей бизнес-логикой.Разделение пользовательского интерфейса и бизнеса, что способствует повторному использованию логики, а также непрерывному обслуживанию и повторению.
поэтому мы используемsetState
Переплетение бизнес-логики, бизнес-кода и кода рендеринга неизбежно приведет к тому, что наши компоненты станут все более и более раздутыми, и это не способствует повторному использованию логики, ноВо многих случаях разделение функциональных границ и создание моделей данных для модулей вначале четко не определены, но постепенно ускоряются повторяющимися абстракциями в непрерывном итеративном процессе..
такconcent
Разрешить существование такого разнообразия режимов разработки.Вы можете планировать редьюсер магазина по модулям и функциям сверху вниз, а затем постепенно кодировать для реализации связанных компонентов, или вы можете разрабатывать и итерировать снизу вверх, когда требования или функции непонятны.Когда редьюсер не абстрагирован,просто пишем дело в компонентах,а потом извлекаем их по одному.Вместо форсирования централизованного хранилища модулей конфигурации можно свободно децентрализовать хранилище модулей конфигурации,а потом легко в соответствии с планом последующей итерации.Настроить конфигурацию магазина.
Добавьте определение редуктора
import { run } from 'concent';
run({
counter: {//定义counter模块
state: { count: 1 },//state定义,必需
reducer: {//reducer函数定义,可选
inc(payload, moduleState) {
return { count: moduleState.count + 1 }
},
dec(payload, moduleState) {
return { count: moduleState.count - 1 }
}
},
},
})
Изменить состояние через диспетчеризацию
import { register } from 'concent';
//注册成为Concent Class组件,指定其属于counter模块
@register('counter')
class CounterComp extends Component {
render() {
//ctx是concent为所有组件注入的上下文对象,携带为react组件提供的各种新特性api
return (
<div>
count: {this.state.count}
<button onClick={() => this.ctx.dispatch('inc')}>inc</button>
<button onClick={() => this.ctx.dispatch('dec')}>dec</button>
</div>
);
}
}
Поскольку модуль concent имеет такие параметры, как watch, вычисленный и init в дополнение к состоянию, редуктору, и поддерживает вас, чтобы определить его по мере необходимости.
Так ли это глобальное потреблениеbusiness model
, либо компонент или страница поддерживает себяcomponent model
а такжеpage model
, рекомендуется дополнительно записать модель в виде папки, определить состояние, редуктор, вычислить, наблюдать, инициализировать внутри, а затем экспортировать и синтезировать их вместе, чтобы сформировать полное определение модели.
src
├─ ...
└─ page
│ ├─ login
│ │ ├─ model //写为文件夹
│ │ │ ├─ state.js
│ │ │ ├─ reducer.js
│ │ │ ├─ computed.js
│ │ │ ├─ watch.js
│ │ │ ├─ init.js
│ │ │ └─ index.js
│ │ └─ Login.js
│ └─ product ...
│
└─ component
└─ ConfirmDialog
├─ model
└─ index.js
Это не только показывает, что их соответствующие обязанности четко определены, но и предотвращает расширение кода в огромный объект модели.В то же время, после независимого определения редьюсера, внутренние функции могут быть диспетчеризированы друг другу при их вызове.непосредственно на основе цитатвместо струн.
// code in models/foo/reducer.js
export function changeName(name) {
return { name };
}
export async function changeNameAsync(name) {
await api.track(name);
return { name };
}
export async function changeNameCompose(name, moduleState, actionCtx) {
await actionCtx.setState({ loading: true });
await actionCtx.dispatch(changeNameAsync, name);//基于函数引用调用
return { loading: false };
}
высокая производительность
Существующие решения по управлению состоянием в направлении повышения производительности основаны на уменьшении диапазона рендеринга, так что визуализируется только область рендеринга, что может значительно улучшить производительность реагирующего приложения, и в то же время избегайте искусственно написанногоshouldComponentUpdate
функция.
Затем сравнитеredux
, потому что он поддерживает управление детализацией потребления на уровне ключа, и вы знаете, какие экземпляры обновлять с момента отправки статуса, поэтому он может дать вам достаточные гарантии производительности, особенно для сценариев с большим количеством компонентов и сложными моделями данных.cocent
Это должно придать вам достаточно уверенности, чтобы спокойно справиться с этим, давайте посмотрим на сравнениеmbox
,concent
Какие еще сценарии были изучены.
renderKey, более точное управление диапазоном рендеринга
Контекст экземпляра для каждого компонентаctx
имеет соответствующий ему уникальный индекс, называемыйccUniqueKey
, каждый компонент передается, если он не отображается при создании экземпляраrenderKey
переписать, т.renderKey
Значение по умолчаниюccUniqueKey
, когда мы сталкиваемся с тем, что stateKey модуля является списком или картой, каждый дочерний элемент в представлении, созданный путем его обхода, вызывает один и тот жеreducer
, через идентификатор для достижения цели изменения только своих собственных данных, но они имеют общийstateKey
, так что нужно соблюдать этоstateKey
Другие дочерние элементы также запускаются для избыточного рендеринга, и ожидаемый результат таков: любой, кто изменяет свои данные, только инициирует рендеринг.
Например, список хранилища представляет собой длинный список, каждый элемент будет отображаться в ItemView, и каждый ItemView будет обращаться к одной и той же функции редюсера для изменения своих собственных данных, но мы ожидаем, что он будет отображаться только после модификации, поэтому достигатьБолее точное управление диапазоном рендеринга.
на основеrenderKey
механизм,concent
Вы можете легко сделать это, когда отметите запись о государственной отправкеrenderKey
час,concent
попадет прямо в этоrenderKey
Соответствующий экземпляр для запуска обновления рендеринга.
Будь то
setState
,dispatch
,ещеinvoke
, оба поддерживают входящиеrenderKey
.
Ключ, поставляемый с компонентом реакции, используется для diff v-dom-tree, concentrenderKey
Он используется для управления диапазоном позиционирования экземпляра. Между ними есть существенные различия. Ниже приведен пример кода,Образец онлайн-кода указывает мне проверить
// store的一个子模块描述
{
book: {
state: {
list: [
{ name: 'xx', age: 19 },
{ name: 'xx', age: 19 }
],
bookId_book_: { ... },//map from bookId to book
},
reducer: {
changeName(payload, moduleState) {
const { id, name } = payload;
const bookId_book_ = moduleState.bookId_book_;
const book = bookId_book_[id];
book.name = name;//change name
//只是修改了一本书的数据
return { bookId_book_ };
}
}
}
}
@register('book')
class ItemView extends Component {
changeName = (e)=>{
this.props.dispatch('changeName', e.currentTarget.value);
}
changeNameFast = (e)=>{
// 每一个cc实例拥有一个ccUniqueKey
const ccUniqueKey = this.ctx.ccUniqueKey;
// 当我修改名称时,真的只需要刷新我自己
this.props.dispatch('changeName', e.currentTarget.value, ccUniqueKey);
}
render() {
const book = this.state.bookId_book_[this.props.id];
//尽管我消费是subModuleFoo的bookId_book_数据,可是通过id来让我只消费的是list下的一个子项
//替换changeName 为 changeNameFast达到我们的目的
return <input value={ book.name } onChange = { changeName } />
}
}
@register('book')
class BookItemContainer extends Component {
render() {
const books = this.state.list;
return (
<div>
{/** 遍历生成ItemView */}
{books.map((v, idx) => <ItemView key={v.id} id={v.id} />)}
</div >
)
}
}
Поскольку concent использует стратегию обратного наследования для переноса основных компонентов класса по умолчанию, в дополнение к уменьшению диапазона рендеринга и времени рендеринга он также будет иметь меньше уровней DOM.
lazyDispatch, более точный контроль времени рендеринга
существуетconcent
внутри,reducer
функция иsetState
Точно так же рекомендуется возвращать то, что изменено, и формат написания разнообразный.
- Может быть обычной чистой функцией
- возможно
generator
генераторная функция - возможно
async & await
функция Вы можете вернуть частичное состояние, вы можете вызвать другие функции-редьюсеры, а затем вернуть частичное состояние, или вы можете ничего не возвращать, просто объедините другие функции-редьюсеры для вызова. В сравненииredux
илиredux
В схеме семейства много хлопот всегда синтезировать новое состояние, а чистые функции и функции побочных эффектов уже не трактуются по-разному. functions , просто объявите ее как обычную функцию, если вам нужна функция побочного эффекта, объявите ее как асинхронную функцию, простую и понятную, в соответствии с мышлением при чтении.
На основе этого механизма гранулярность наших функций-редьюсеров очень тонкая и атомарная, каждая из которых отвечает за независимое обновление значения определенного ключа и нескольких ключей, чтобы их можно было более гибко комбинировать для достижения цели высокой производительности. повторное использование, чтобы структура кода была элегантной и уменьшала обязанности каждой функции редуктора.
//reducer fns
export async function updateAge(id){
// ....
return {age: 100};
}
export async function trackUpdate(id){
// ....
return {trackResult: {}};
}
export async function fetchStatData(id){
// ....
return {statData: {}};
}
// compose other reducer fns
export async function complexUpdate(id, moduleState, actionCtx) {
await actionCtx.dispatch(updateAge, id);
await actionCtx.dispatch(trackUpdate, id);
await actionCtx.dispatch(fetchStatData, id);
}
Хотя структура кода стала более элегантной, а обязанности каждой функции-редюсера меньше, на самом деле каждая функция-редуктор фактически инициирует обновление.
Исходный триггер функции редуктора начинается с контекста экземпляра ctx.dispatch или глобального контекста cc.dispatch (или cc.reducer), вызывает функцию редуктора модуля, а затем запускает другие функции редуктора внутри своей функции редуктора. факт, ацепочка вызовов, каждая функция-редуктор в цепочке, которая возвращает значение состояния, вызовет обновление рендеринга.Если в цепочке много функций-редукторов, будет много избыточных обновлений того же представления, что и обычно.
Исходный код, запускающий редюсер
// in your view
<button onClick={()=> ctx.dispatch('complexUpdate', 2)}>复杂的更新</button>
Процесс обновления выглядит следующим образом
Для этой цепочки вызовов предусмотрена ленивая функция, чтобы пользователь мог удовлетворительно разделить степень детализации состояния обновления функции редуктора на мелкие детали, а также убедиться, что количество отрисовок сведено к минимуму.
увидеть эту функцию,
mbox
Думал ли пользовательtransaction
Концепция , да, вы правильно понимаете, в определенной степени они служат той же цели, но их проще и элегантнее использовать в контексте.
Теперь вам просто нужно сделать небольшую модификацию источника триггера, используяlazyDispatch
заменятьdispatch
Все, код в редьюсере не нужно корректировать, Concent задержит время, когда все функции редьюсера в цепочке вызовов функций редуктора вызовут обновление ui, и только новая часть возвращаемого ими состояния будет объединена модуль и временно сохраняется Конечный источник Когда вызов функции завершается, он отправляется в хранилище один раз и запускает рендеринг соответствующего экземпляра.
// in your view
<button onClick={()=> ctx.lazyDispatch('complexUpdate', 2)}>复杂的更新</button>
Теперь новый процесс обновления выглядит следующим образом
Конечно, lazyScope также можно настроить, и нет необходимости запускать ленивую функцию в исходной функции.
// in your view
const a= <button onClick={()=> ctx.dispatch('complexUpdateWithLoading', 2)}>复杂的更新</button>
// in your reducer
export async function complexUpdateWithLoading(id, moduleState, actionCtx) {
//这里会实时的触发更新
await actionCtx.setState({ loading: true });
//从这里开始启用lazy特性,complexUpdate函数结束前,其内部的调用链都不会触发更新
await actionCtx.lazyDispatch(complexUpdate, id);
//这里返回了一个新的部分状态,也会实时的触发更新
return { loading: false };
}
delayBroadcast, более активно снижать частоту времени рендеринга
Для некоторого общего состояния, когда экземпляр часто его меняет, используйтеdelayBroadcast
Активно контролируйте отложенное распространение этого состояния на другие экземпляры, чтобы достичьБолее активно сокращайте время рендеринга
function ImputComp() {
const ctx = useConcent('foo');
const { name } = ctx.state;
const changeName = e=> ctx.setState({name: e.currentTarget.value});
//setState第四位参数是延迟分发时间
const changeNameDelay = e=> ctx.setState({name: e.currentTarget.value}, null, null, 1000);
return (
<div>
<input value={name} onChange={changeName} />
<input value={name} onChange={changeName} />
</div>
);
}
function App(){
return (
<>
<ImputComp />
<ImputComp />
<ImputComp />
</>
);
}
усилить реакцию
мы упоминали ранееctx
Объекты — это «герои», которые усиливают реакцию, потому что каждый экземпляр имеетconcent
построен дляctx
Object, очень удобно добавлять под него множество новых функций и новых возможностей.
добавлены новые функции
Как было сказано выше о модулеcomputed
,watch
Такие ключевые слова, у прочитавших их читателей наверняка остались какие-то вопросы.На самом деле мотивация и опыт их появления такие же как иvue
тоже самое.
-
computed
определить каждыйstateKey
При значении функция вычисления должна запускаться, а ее результат кэшироваться, только когдаstateKey
Счетчик сработает только при повторном изменении значения.Узнайте больше о вычисляемых -
watch
определить каждыйstateKey
Функция обратного вызова, которая будет запущена, когда значениеstateKey
Он будет запущен только тогда, когда значение снова изменится, что обычно используется для обработки некоторых асинхронных задач.Узнать больше о часах. если я изsetState
Чтобы объяснить его природу, вы сможете понять, что эти функции естественным образом предоставляются пользователям.
setState
Переданный параметрpartialState
,такconcent
Знайте, какие из них с самого началаstateKey
изменилось, естественно нам нужно только выставить конфигурациюcomputed
,watch
, то когда экземпляр отправляет новое частичное состояние после улучшенияsetState
Естественно, он сможет вызвать соответствующий обратный вызов.
установка дает компонентам больше возможностей
упомянутый вышеcomputed
,watch
Значение для модуля, нам нужно настроить его отдельно для экземпляраcomputed
,watch
Как с этим бороться?
Настройка — это очень важная функция, предусмотренная для экземпляров компонентов. Ее можно использовать как в компонентах класса, так и в компонентах-функциях.ctx.settings
, он не будет выполняться повторно, поэтому в нем можно определить функции-ловушки, такие как вычисляемый экземпляр, наблюдение за экземпляром и эффект экземпляра, а также можно настраивать другие функции бизнес-логики и возвращать их для облегчения использования компонентов.
Основываясь на характеристиках времени выполнения настройки, это эквивалентно предоставлению компоненту дополнительного пространства, одновременному определению соответствующей персонализированной конфигурации для компонента, предоставлению компоненту дополнительных возможностей, особенно для функционального компонента, обеспечениюuseConcent
копироватьregister
Все возможности интерфейса, результаты возврата которого собираются вctx.settings
Функция в этом позволяет функциональным компонентам определять все методы одновременно вsetup
Таким образом, устраняется слабость многократного создания временных замыкающих функций во время повторного рендеринга функциональных компонентов, что снижает нагрузку на сборщик мусора.
использовать
useConcent
Так что вы все еще можете использовать классикуdispatch&&reducer
режим для написания основной бизнес-логики и не исключает другие функции хуков инструментов (такие какuseWindowSize
д.), смешиваются между собой.
разрешите намsetup
Бар! ! ! Посмотрите на магию установки, котораяeffect
Функция крюка - идеальная заменаuseEffect
.Узнать больше о настройке
const setup = ctx => {
//count变化时的副作用函数,第二位参数可以传递多个值,表示任意一个发生变化都将触发此副作用
ctx.effect(() => {
console.log('count changed');
}, ['count']);
//每一轮渲染都会执行
ctx.effect(() => {
console.log('trigger every render');
});
//仅首次渲染执行的副作用函数
ctx.effect(() => {
console.log('trigger only first render');
}, []);
//定义实例computed,因每个实例都可能会触发,优先考虑模块computed
ctx.computed('count', (newVal, oldVal, fnCtx)=>{
return newVal*2;
});
//定义实例watch,区别于effect,执行时机是在组件渲染之前
//因每个实例都可能会触发,优先考虑模块watch
ctx.watch('count', (newVal, oldVal, fnCtx)=>{
//发射事件
ctx.emit('countChanged', newVal);
api.track(`count changed to ${newVal}`);
});
//定义事件监听,concent会在实例销毁后自动将其off掉
ctx.on('changeCount', count=>{
ctx.setState({count});
});
return {
inc: () => setCount({ count: ctx.state.count + 1 }),
dec: () => setCount({ count: ctx.state.count - 1 }),
};
}
выгода отsetup
атрибуты и все экземпляры concent содержат объект контекстаctx
, компоненты классов и компоненты функций обеспечивают 100% унифицированные возможности вызовов API, что означает, что стили кодирования этих двух компонентов в высокой степени согласованы, а стоимость взаимного преобразования равна нулю.
Функциональные компоненты, обеспечивающие доступ к настройке
import { useConcent } from 'concent';
function HooklFnComp() {
//setup只会在初次渲染前调用一次
const ctx = useConcent({ setup, module:'foo' });
const { state , settings: { inc, dec } } = ctx;
return (
<div>
count: {state.count}
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</div>
);
}
Доступ к классовому компоненту установки
@register('foo')
class ClassComp extends React.Component() {
?setup(ctx){
//复用刚才的setup定义函数, 这里记得将结果返回
return setup(ctx);
}
render(){
const ctx = this.ctx;
//ctx.state 等同于 this.state
const { state , settings: { inc, dec } } = ctx;
return (
<div>
count: {state.count}
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</div>
);
}
}
После улучшения способности вы можете свободно выбирать подходящий способ обновления статуса в соответствии со сценой.
@register("foo")
class HocClassComp extends Component {
render() {
const { greeting } = this.state; // or this.ctx.state
const {invoke, sync, set, dispatch} = this.ctx;
// dispatch will find reducer method to change state
const changeByDispatch = e => dispatch("changeGreeting", evValue(e));
// invoke cutomized method to change state
const changeByInvoke = e => invoke(changeGreeting, evValue(e));
// classical way to change state, this.setState equals this.ctx.setState
const changeBySetState = e => this.setState({ greeting: evValue(e) });
// make a method to extract event value automatically
const changeBySync = sync('greeting');
// similar to setState by give path and value
const changeBySet = e=> set('greeting', evValue(e));
return (
<>
<h1>{greeting}</h1>
<input value={greeting} onChange={changeByDispatch} /><br />
<input value={greeting} onChange={changeByInvoke} /><br />
<input value={greeting} onChange={changeBySetState} /><br />
<input value={greeting} onChange={changeBySync} /><br />
<input value={greeting} onChange={changeBySet} />
</>
);
}
}
На следующем рисунке представлена схема полного жизненного цикла компонента компонента:
Поддержка промежуточного программного обеспечения и плагинов
Хороший фреймворк должен предоставлять некоторые механизмы, которые можно подключать к другим библиотекам для гибкого расширения дополнительных возможностей, что способствует дополнительной настройке пользователями некоторых персонализированных потребностей, тем самым способствуя экологическому развитию вокруг фреймворка, поэтому дизайн концентрат в начале , Он сохраняет промежуточное программное обеспечение и механизм подключаемых модулей, что позволяет определению промежуточного программного обеспечения перехватывать все записи отправки изменений данных для дополнительной обработки, а также поддерживает настраиваемые подключаемые модули для получения различных сигналов во время выполнения для расширения возможностей согласия.
определить промежуточное ПО и использовать
Промежуточное звено — это обычная функция
import { run } from 'concent';
const myMiddleware = (stateInfo, next)=>{
console.log(stateInfo);
next();//next一定不能忘记
}
run(
{...}, //store config
{
middlewares: [ myMiddleware ]
}
);
определить плагины и использовать
Плагин — это простой объект, который должен содержать метод установки.
import { cst, run } from 'concent';
const myPlugin = {
install: ( on )=>{
//监听来自concent运行时的各种信号,并做个性化处理
on(cst.SIG_FN_START, (data)=>{
const { payload, sig } = data;
//code here
})
}
return { name: 'myPlugin' }//必需返回插件名
}
На основе механизма плагинов были предоставлены следующие плагины
- concent-plugin-loading, плагин для простого управления статусом загрузки концентрированного приложения
- concent-plugin-redux-devtool, разрешите приложению concent получить доступ к инструменту отладки redux-dev-tool, который удобен и нагляден для отслеживания истории изменения состояния.
Используйте существующую экосистему реагирования
Конечноconcent
Не будет создавать бессмысленные колеса, по-прежнему настаивать на использовании различных превосходных ресурсов существующей экологии реагирования, таких как предоставляемыеreact-router-concent, перемычкаreact-router
Адаптируйте его к приложению concent.
глобальное воздействиеhistory
объекта, наслаждайтесь программными навигационными переходами.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { ConnectRouter, history, Link } from 'react-router-concent';
import { run, register } from 'concent';
run();
class Layout extends Component {
render() {
console.log('Layout Layout');
return (
<div>
<div onClick={() => history.push('/user')}>go to user page</div>
<div onClick={() => history.push('/user/55')}>go to userDetail page</div>
{/** 可以基于history主动push,也可以使用Link */}
<Link to="/user" onClick={to => alert(to)}>to user</Link>
<div onClick={() => history.push('/wow')}>fragment</div>
<Route path="/user" component={User_} />
<Route path="/user/:id" component={UserDetail_} />
<Route path="/wow" component={F} />
</div>
)
}
}
const App = () => (
<BrowserRouter>
<div id="app-root-node">
<ConnectRouter />
<Route path="/" component={Layout} />
</div>
</BrowserRouter>
)
ReactDOM.render(<App />, document.getElementById('root'));
Нажмите на меня, чтобы увидеть онлайн-пример
Заключение и мысли
concent
Ядром рабочего механизма являетсятег зависимости,коллекция цитат,государственное распределение, создавая глобальный контекст и контекст экземпляра и взаимодействуя между ними для достижения требований управления состоянием и дальнейшего расширения возможностей компонентов.
Основываясь на этом принципе в теории, он может быть использован для другихpull based
Платформа пользовательского интерфейса механизма обновления реализует управление состоянием и обеспечивает их соответствие возможностям вызовов API и стилям написания кода, таким как小程序
изthis.setData
,omi
изthis.update
.
В то же время, потому чтоconcent
Объект контекста экземпляра предоставляетсяctx
модернизировать возможности компонентов, поэтому, если мы поставим перед собой цель: можем сделать响应式
а также不可变
сосуществование, кажется, работает, просто нужно добавить еще один иstate
Эквивалентная наблюдаемая находится вctx
на, предполагаяthis.ctx.data
это наблюдаемая, которую мы строим, а затем упомянутое响应式
Необходимо иметь дело с разными платформами в соответствии с разными стратегиями для достижения цели сосуществования.
- для себя
响应式
Фреймворкangualr
а такжеvue
,поставкаthis.ctx.data
Непосредственное изменение состояния эквивалентно соединению исходного механизма обновления, в то время какreducer
Возвращенное состояние в конечном итоге падает доthis.ctx.data
изменить для управления рендерингом вида. - против
pull based
кадр какreact
,поставкаthis.ctx.data
это просто псевдоответчикthis.ctx.data
Собранные изменения по-прежнему попадают вthis.setState
Чтобы управлять обновлением представления, но это заставляет пользователя чувствовать, что это иллюзия, что представление управляется прямым манипулированием данными. Итак, если объединение этого слоя достигнуто, правильно ли это?concent
Могут ли все фреймворки пользовательского интерфейса быть написаны одним и тем же способом кодирования?
Конечно, желание великого объединения прекрасно, но нужно ли его реализовывать? Решения по управлению состоянием в каждой структуре очень зрелые, и люди с ограниченной энергией для достижения этого видения должны выбрать самый трудный путь, поэтому здесь просто личное заявление о响应式
а также不可变
Сосуществование мышления и сортировки предоставляет читателям некоторые справочные мнения, чтобы они могли подумать о тенденции развития между управлением состоянием и инфраструктурой пользовательского интерфейса.
Если вы используете стихотворение для описания управления состоянием и структуры пользовательского интерфейса, я лично думаю, что это
Как только золотой ветер и нефритовая роса встретятся, они победят, но в мире бессчетное количество людей.
Эти двое достигли друг друга, поддерживали и развивали друг друга и стали свидетелями замены различных государственных библиотек на протяжении многих лет.
В настоящее времяconcent
Пока только считайreact
Занимайтесь интеграцией, работайте над улучшением молчаливого взаимопонимания между ними и надейтесь постепенно улучшитьredux
И второй братmobx
под территорией, занимайте маленькую базу, чтобы выжить, если читателю нравится эта статья, даconcent
Заинтересованы, добро пожаловатьstar, я верю, что огонь революции обязательно продолжится,concent
Идея должна идти дальше.