Heim > Artikel > Web-Frontend > Tauchen Sie tief in den Redux-Technologie-Stack ein
Einführung in Redux
In diesem Artikel wird davon ausgegangen, dass jeder über einige Kenntnisse über Reaktions- und Flussarchitektur verfügt und auch Redux verwendet oder verstanden hat. Daher wird nicht mit dem grundlegendsten, sondern direkt mit Redux begonnen fasst zusammen. Wenn Sie Redux noch nicht verwendet haben, lesen Sie am besten zuerst hier
Um Redux zu verstehen, müssen wir zunächst einige Designprinzipien von Redux zusammenfassen:
Einzelne Daten Quelle
Redux verwendet nur eine einzige Objektbaumstruktur, um den Status der gesamten Anwendung zu speichern. Dabei handelt es sich um die Daten, die in der gesamten Anwendung verwendet werden und als Store (Speicher) bezeichnet werden. Zusätzlich zu den gespeicherten Daten kann der Store auch den Status der gesamten Anwendung speichern (einschließlich des Router-Status, der später eingeführt wird). Daher ist es über den Store möglich, eine Sofortspeicherfunktion zu implementieren (einen Snapshot erstellen). ) der gesamten Anwendung. Darüber hinaus bietet dieses Design auch die Möglichkeit für serverseitiges Rendering.
Der Status ist schreibgeschützt
Dies entspricht dem Designkonzept von Flux. Wir können den Status des Shops nicht ändern Komponenten (eigentlich generiert Redux einen Speicher basierend auf dem Reduzierer), kann jedoch nur Aktionen auslösen, um den aktuellen Status durch Versand zu iterieren. Hier ändern wir den Anwendungsstatus nicht direkt, sondern geben einen brandneuen Status zurück.
Zustandsänderungen bestehen aus reinen Funktionen
Der Prototyp des Reduzierers in Redux wird wie folgt aussehen, Sie können es sich so vorstellen Es ist die Formel vorheriger Zustand + Aktion = neuer Zustand:
(previousState, action) => newState
Jeder Reduzierer ist eine reine Funktion, was bedeutet, dass er keine Nebenwirkungen hat. Der Vorteil dieses Designs besteht nicht nur darin, den Reduzierer zum Ändern zu verwenden der Zustand Es wird einfach und kann rein getestet werden. Darüber hinaus kann Redux jeden Rückgabestatus speichern, um auf einfache Weise eine Zeitreise zu generieren und die Ergebnisse jeder durch die Aktion verursachten Änderung zu verfolgen.
Wenn wir Redux in React verwenden, benötigen wir sowohl React-Redux als auch Redux.
In diesem Teil geht es hauptsächlich um mein eigenes Verständnis. Es ist möglicherweise etwas abstrakt oder nicht ganz korrekt, sodass Sie es direkt überspringen können.
Die Kernmethode in Redux ist createStore. Die Kernfunktionen von React werden alle in createStore und seinem endgültig generierten Store abgedeckt. Die createStore-Methode selbst unterstützt die Übergabe von drei Parametern: Reducer, InitialState , und Enhancer, Enhancer kann als erweiterte Verpackungsfunktion verwendet werden, was bei uns nicht sehr häufig verwendet wird.
Diese Funktion verwaltet intern einen aktuellen Status, und dieser aktuelle Status kann über die Funktion getState (integriert) zurückgegeben werden. Darüber hinaus implementiert sie tatsächlich einen Publish-Subscribe-Modus und abonniert Ereignisse über store.subscribe. Diese Arbeit wird von React-Redux ausgeführt und hilft uns dabei, alle Listener auszulösen und den gesamten Zustandsbaum zu aktualisieren, wenn ein Versand erfolgt. Darüber hinaus löst die integrierte Dispatch-Funktion nach einer Reihe von Überprüfungen den Reduzierer aus, ändert dann den Status und ruft dann nacheinander den Listener auf, um die Aktualisierung des gesamten Statusbaums abzuschließen.
Freunde, die Redux verwendet haben, sind eigentlich keine Unbekannten für Middleware wie Redux-Thunk. Tatsächlich ist Redux in vielen Fällen auch unverzichtbar Ich denke, dieses Konzept ähnelt in gewisser Weise dem Middleware-Mechanismus von NodeJS: Die Aktion durchläuft nacheinander jede MiddleWare und wird dann an die nächste weitergeleitet. Jede MiddleWare kann auch andere Vorgänge ausführen, z. B. die Aktion unterbrechen oder ändern, bis die endgültige Verarbeitungsfunktion vorliegt an den Reduzierer übergeben.
Die applyMiddleware-Funktion von Redux ist sehr verfeinert:
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) //注意这里的dispatch并不是一开始的store.dispatch,实际上是变化了的 } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
Der Kern ist „dispatch = compose(...chain)(store.dispatch“) . Rufen Sie den Quellcode von compose auf:
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }
Rufen Sie das Ausführungsergebnis der vorherigen Funktion für die nächste Funktion auf.
Tatsächlich ist der Prozess des Schreibens einer Middleware sehr einfach. Redux-Trunk besteht beispielsweise tatsächlich aus Folgendem:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
Natürlich , Wir erklären zunächst, dass der React-Router des React-Tools-Sets nicht unbedingt mit Redux verwendet werden muss, aber Redux verfügt über einen anderen React-Router-Redux, der mit React-Router und Redux verwendet werden kann, und der Effekt ist sehr gut .
Da wir in diesem Teil nicht die Verwendung von React-Router vorstellen, lesen Sie bitte das chinesische Dokument zur Verwendung von React-Router.
ermöglichen es Entwicklern, Routen über JSX-Tags zu deklarieren, was das Schreiben unserer Routen sehr benutzerfreundlich macht und die Ausdrucksfähigkeit des deklarativen Routings relativiert stark.
Verschachtelte Routen und Routenabgleich: Sie können Parameter im angegebenen Pfad übergeben:
<Route path="/mail/:mailId" component={Mail} />
Außerdem, wenn die Parameter optional sind , wir können es in Klammern setzen (:optionaler Parameter).
Unterstützt mehrere Routenwechselmethoden: Wir wissen, dass die aktuellen Routenwechselmethoden nichts anderes sind als die Verwendung von Hashchange und PushState. Ersteres hat eine bessere Browserkompatibilität, ist aber nicht so echt Letzteres bietet uns zwar ein elegantes URL-Erlebnis, erfordert jedoch, dass der Server das Problem der Aktualisierung eines Pfads löst (der Server muss automatisch zur Startseite umleiten).
简单的说,react-router-redux让我们可以把路由也当作状态的一部分,并且可以使用redux的方式改变路由:直接调用dispatch:this.props.push(“/detail/”);,这样把路由也当作一个全局状态,路由状态也是应用状态的一部分,这样可能更有利于前端状态管理。
react-router-redux是需要配合react-router来使用的,并不能单独使用,在原本的项目中添加上react-router-redux也不复杂:
import { createStore, combineReducers, compose, applyMiddleware } from 'redux'; import { routerReducer, routerMiddleware } from 'react-router-redux'; import { hashHistory } from 'react-router'; import ThunkMiddleware from 'redux-thunk'; import rootReducer from './reducers'; import DevTools from './DevTools'; const finalCreateStore = compose( applyMiddleware(ThunkMiddleware,routerMiddleware(hashHistory)), DevTools.instrument() )(createStore); console.log("rootReducer",rootReducer); const reducer = combineReducers({ rootReducer, routing: routerReducer, }); export default function configureStore(initialState) { const store = finalCreateStore(reducer, initialState); return store; }
另外,上文提到的demoreact-router-redux-demo用了react-router和react-router-redux,当然也用到了redux的一些别的比较好的工作,比如redux-devtools,有兴趣的朋友可以点击这里
这一部分讲述的是一种组件书写规范,并不是一些库或者架构,这些规范有利于我们在复杂的项目中组织页面,不至于混乱。
从布局的角度看,redux强调了三种不同的布局组件,Layouts,Views,Components:
Layouts: 指的是页面布局组件,描述了页面的基本结构,可以是无状态函数,一般就直接设置在最外层router的component参数中,并不承担和redux直接交互的功能。比如我项目中的Layouts组件:
const Frame = (props) => <p className="frame"> <p className="header"> <Nav /> </p> <p className="container"> {props.children} </p> </p>;
Views组件,我认为这个组件是Components的高阶组件或者Components group,这一层是可以和redux进行交互并且处理数据的,我们可以将一个整体性功能的组件组放在一个Views下面(注:由于我给出的demo十分简单,因此Views层和Components层分的不是那么开)
Components组件,这是末级渲染组件,一般来说,这一层级的组件的数据通过props传入,不直接和redux单向数据流产生交互,可以是木偶般的无状态组件,或者是包含自身少量交互和状态的组件,这一层级的组件可以被大量复用。
总而言之,遵守这套规范并不是强制性的,但是项目一旦稍微复杂一些,这样做的好处就可以充分彰显出来。
redux的单向数据流相对于双向数据绑定,在处理表单等问题上的确有点力不从心,但是幸运的是已经开源了有几个比较不错的插件:
redux-form-utils,好吧,这个插件的star数目非常少,但是他比较简单,源代码也比较短,只有200多行,所以这是一个值得我们看源码学习的插件(它的源码结构也非常简单,就是先定一个一个高阶组件,这个高阶组件可以给我们自己定义的表单组件传入新的props,定制组件,后一部分就是定义了一些action和reducer,负责在内容变化的时候通知改变状态树),但是缺憾就是这个插件没有对表单验证做工作,所以如果我们需要表单验证,还是需要自己做一些工作的。
另外还有一地方,这个插件源代码写法中用到了::这种ES6的语法,这其实是一种在es6中class内部,使用babel-preset-stage-0即可使用的语法糖:::this.[functionName] 等价于 this.[functionName].bind(this, args?)
redux-form,这个插件功能复杂,代码完善,体量也非常庞大,可以参考文档进行使用,但是读懂源代码就是比较麻烦的事情了。不过这个插件需要在redux的应用的state下挂载一个节点,这个节点是不需要开发者自己来操控的,他唯一需要做的事情就是写一个submit函数即可。我在自己的demo中也把一个例子稍加改动搬了过来,感觉用起来比较舒服。
想要做到redux性能优化,我们首先就要知道redux的性能可能会在哪些地方受到影响,否则没有目标是没有办法性能优化的。
因为我也不是使用redux的老手,所以也并不能覆盖所有性能优化的点,我总结两点:
有的时候,我们需要的数据格式会自带冗余,可以抽取出一些公共的部分从而缩减大小,比如我们需要的数据格式可能是这样的:
[ { name:"Nike", title:"国家一级运动员","国家一级裁判员" } { name:"Jenny", title:"国家一级裁判员" } { name:"Mark", title:"国家一级运动员" } ]
这个时候实际上我们可以优化成这样:
[ { "国家一级运动员":"Nike","Mark" "国家一级裁判员":"Jenny","Nike" } ]
这个时候,我们可以直接把后者当作store的格式,而我们用reselect这个库再转变成我们所要的格式,关于reselect怎么用上述链接有更详细的例子,在这里我就不过多介绍了。
事实上,对于redux来说,每当store发生改变的时候,所有的connect都会重新计算,在一个大型应用中,浪费的时间可想而知,为了减少性能浪费,我们可以对connect中的selector做缓存。
Die oben erwähnte Reselect-Bibliothek verfügt über eine eigene Caching-Funktion. Wir können feststellen, ob Caching verwendet werden soll. Hier wird die reine Funktionsfunktion verwendet.
Die Caching-Funktion von Reselect kann vom Benutzer angepasst werden
Ausführlichere Artikel zum Redux-Technologie-Stack finden Sie auf der chinesischen PHP-Website !