±à¼ÍƼö: |
±¾ÎÄÀ´×ÔÓÚ21CTO£¬±¾ÎĽÏÏêϸµÄ×ܽáÁ˸öÈË´Ó0µ½1´î½¨Ò»¸öÏîÄ¿¼Ü¹¹µÄ¹ý³Ì£¬¶ÔReact£¬
ReduxÓ¦ÓúÍÏîÄ¿¹¤³Ìʵ¼ù¶¼ÓÐÁ˸üÉîµÄÀí½â¼°Ë¼¿¼£¬ÔÚ´óǰ¶Ë³É³¤Ö®Â·¼ÌÐøíÆíÂǰÐС£
|
|
ǰÑÔ
ÏÖÔÚÒѾÓкܶà½ÅÊּܹ¤¾ß£¬Èçcreate-react-app£¨https://github.com/facebookincubator/create-react-app£©£¬Ö§³ÖÒ»¼ü´´½¨Ò»¸öReactÓ¦ÓÃÏîÄ¿½á¹¹£¬ºÜ·½±ã£¬µ«ÊÇÏíÊÜ·½±ãµÄͬʱ£¬Ò²Ê§È¥Á˶ÔÏîÄ¿¼Ü¹¹¼°¼¼ÊõÕ»ÍêÕûѧϰµÄ»ú»á£¬¶øÇÒͨ³£½ÅÊּܴ´½¨µÄÓ¦Óü¼Êõ¼Ü¹¹²¢²»ÄÜÍêÈ«Âú×ãÎÒÃǵÄÒµÎñÐèÇó£¬ÐèÒªÎÒÃÇ×Ô¼ºÐ޸ģ¬ÍêÉÆ£¬ËùÒÔÈç¹ûÏ£Íû¶ÔÏîÄ¿¼Ü¹¹ÓиüÉîÕÆ¿Ø£¬×îºÃ»¹ÊÇ´Ó0µ½1Àí½âÒ»¸öÏîÄ¿¡£
ÏîÄ¿½á¹¹Óë¼¼ÊõÕ»
ÎÒÃÇÕâ´ÎµÄʵ¼ù²»×¼±¸Ê¹ÓÃÈκνÅÊּܣ¬ËùÒÔÎÒÃÇÐèÒª×Ô¼º´´½¨Ã¿Ò»¸öÎļþ£¬ÒýÈëÿһ¸ö¼¼ÊõºÍÈý·½¿â£¬×îÖÕÐγÉÍêÕûµÄÓ¦Ó㬰üÀ¨ÎÒÃÇÑ¡ÔñµÄÍêÕû¼¼ÊõÕ»¡£
µÚÒ»²½£¬µ±È»ÊÇ´´½¨Ä¿Â¼£¬ÎÒÃÇÔÚÉÏһƪÒѾŪºÃ£¬Èç¹ûÄ㻹ûÓдúÂ룬¿ÉÒÔ´ÓGithub»ñÈ¡£º
git
clone https://github.com/codingplayboy/react-blog.git
cd react-blog |
Éú³ÉÏîÄ¿½á¹¹ÈçÏÂͼ£º

1. srcΪӦÓÃÔ´´úÂëĿ¼£»
2. webpackΪwebpackÅäÖÃĿ¼£»
3. webpack.config.jsΪwebpackÅäÖÃÈë¿ÚÎļþ£»
4. package.jsonΪÏîÄ¿ÒÀÀµ¹ÜÀíÎļþ£»
5. yarn.lockΪÏîÄ¿ÒÀÀµ°æ±¾ËøÎļþ£»
6. .babelrcÎļþ£¬babelµÄÅäÖÃÎļþ£¬Ê¹ÓÃbabel±àÒëReactºÍJavaScript´úÂ룻
7. eslintrcºÍeslintignore·Ö±ðΪeslintÓï·¨¼ì²âÅäÖü°ÐèÒªºöÂÔ¼ì²éµÄÄÚÈÝ»òÎļþ£»
8. postcss.config.jsΪCSSºó±àÒëÆ÷postcssµÄÅäÖÃÎļþ£»
9. API.mdΪAPIÎĵµÈë¿Ú£»
10. docsΪÎĵµÄ¿Â¼£»
11. README.mdΪÏîĿ˵Ã÷Îĵµ£»
½ÓÏÂÀ´µÄ¹¤×÷Ö÷Òª¾ÍÊǷḻsrcĿ¼£¬°üÀ¨´î½¨ÏîÄ¿¼Ü¹¹£¬¿ª·¢Ó¦Óù¦ÄÜ£¬»¹ÓÐ×Ô¶¯»¯£¬µ¥Ôª²âÊԵȣ¬±¾ÆªÖ÷Òª¹Ø×¢ÏîÄ¿¼Ü¹¹µÄ´î½¨£¬È»ºóʹÓü¼Êõջʵ¼ù¿ª·¢¼¸¸öÄ£¿é¡£
¼¼ÊõÕ»
ÏîÄ¿¼Ü¹¹´î½¨ºÜ´ó²¿·ÖÒÀÀµÓÚÏîÄ¿µÄ¼¼ÊõÕ»£¬ËùÒÔÏȶÔÕû¸ö¼¼ÊõÕ»½øÐзÖÎö£¬×ܽ᣺
1. reactºÍreact-dom¿âÊÇÏîĿǰÌ᣻
2. react·ÓÉ£»
3. Ó¦ÓÃ״̬¹ÜÀíÈÝÆ÷£»
4. ÊÇ·ñÐèÒªImmutableÊý¾Ý£»
5. Ó¦ÓÃ״̬µÄ³Ö¾Ã»¯£»
6. Òì²½ÈÎÎñ¹ÜÀí£»
7. ²âÊÔ¼°¸¨Öú¹¤¾ß»òº¯Êý£»
8. ¿ª·¢µ÷ÊÔ¹¤¾ß£»
¸ù¾ÝÒÔÉÏ»®·Ö¾ö¶¨Ñ¡ÓÃÒÔϵÚÈý·½¿âºÍ¹¤¾ß¹¹³ÉÏîÄ¿µÄÍêÕû¼¼ÊõÕ»£º
1. react£¬react-dom£»
2. react-router¹ÜÀíÓ¦Ó÷ÓÉ£»
3. redux×÷ΪJavaScript״̬ÈÝÆ÷£¬react-redux½«ReactÓ¦ÓÃÓëreduxÁ¬½Ó£»
4. Immutable.jsÖ§³ÖImmutable»¯×´Ì¬£¬redux-immutableʹÕû¸öredux
store״̬Ê÷Immutable»¯£»
5. ʹÓÃredux-persistÖ§³Öredux״̬Ê÷µÄ³Ö¾Ã»¯£¬²¢Ìí¼Óredux-persist-immutableÍØÕ¹ÒÔÖ§³ÖImmutable»¯×´Ì¬Ê÷µÄ³Ö¾Ã»¯£»
6. ʹÓÃredux-saga¹ÜÀíÓ¦ÓÃÄÚµÄÒì²½ÈÎÎñ£¬ÈçÍøÂçÇëÇó£¬Òì²½¶ÁÈ¡±¾µØÊý¾ÝµÈ£»
7. ʹÓÃjest¼¯³ÉÓ¦ÓòâÊÔ£¬Ê¹ÓÃlodash£¬ramdaµÈ¿ÉÑ¡¸¨ÖúÀ࣬¹¤¾ßÀà¿â£»
8. ¿ÉѡʹÓÃreactotronµ÷ÊÔ¹¤¾ß
Õë¶ÔÒÔÉÏ·ÖÎö£¬ÍêÉÆºóµÄÏîÄ¿½á¹¹Èçͼ£º

¿ª·¢µ÷ÊÔ¹¤¾ß
ReactÓ¦Óÿª·¢Ä¿Ç°ÒѾÓÐÖî¶àµ÷ÊÔ¹¤¾ß£¬³£ÓõÄÈçredux-devtools£¬Reactron£¨https://github.com/infinitered/reactotron£©µÈ¡£
redux-devtools
redux-devtoolsÊÇÖ§³ÖÈÈÖØÔØ£¬»Ø·Åaction£¬×Ô¶¨ÒåUIµÄÒ»¿îRedux¿ª·¢¹¤¾ß¡£
Ê×ÏÈÐèÒª°´ÕÕ¶ÔÓ¦µÄä¯ÀÀÆ÷²å¼þ£¬È»ºóÔÙReduxÓ¦ÓÃÖÐÌí¼ÓÏà¹ØÅäÖ㬾ÍÄÜÔÚä¯ÀÀÆ÷¿ØÖÆÌ¨Öв鿴µ½redux¹¤¾ßÀ¸ÁË£¬ÏêϸÎĵµµã´Ë²é¿´£¨https://github.com/zalmoxisus/redux-devtools-extension#installation£©¡£
È»ºó°²×°ÏîÄ¿ÒÀÀµ¿â£º
yarn add --dev redux-devtools
|
È»ºóÔÚ´´½¨redux storeʱ½«Æä×÷ΪreduxÇ¿»¯Æ÷´«ÈëcreateStore·½·¨£º
import { applyMiddleware, compose, createStore,
combineReducers } from 'redux'
// ĬÈÏΪreduxÌṩµÄ×éºÏº¯Êý
let composeEnhancers = compose
if (__DEV__) {
// ¿ª·¢»·¾³£¬¿ªÆôredux-devtools
const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
if (typeof composeWithDevToolsExtension ===
'function') {
// Ö§³Öredux¿ª·¢¹¤¾ßÍØÕ¹µÄ×éºÏº¯Êý
composeEnhancers = composeWithDevToolsExtension
}
}
// create store
const store = createStore(
combineReducers(...),
initialState,
// ×éºÏreduxÖмä¼ÛºÍ¼ÓÇ¿Æ÷£¬Ç¿»¯redux
composeEnhancers(
applyMiddleware(...middleware),
...enhancers
)
) |
1. ÔÚ¿ª·¢»·¾³Ï»ñÈ¡redux-devtoolsÌṩµÄÍØÕ¹×éºÏº¯Êý£»
2. ´´½¨storeʱʹÓÃÍØÕ¹×éºÏº¯Êý×éºÏreduxÖмä¼þºÍÔöÇ¿Æ÷£¬redux-dev-tools±ã»ñµÃÁËÓ¦ÓÃreduxµÄÏà¹ØÐÅÏ¢£»
Reactotron
Reactotron£¨https://github.com/infinitered/reactotron£©ÊÇÒ»¿î¿çƽ̨µ÷ÊÔReact¼°React
NativeÓ¦ÓõÄ×ÀÃæÓ¦Óã¬Äܶ¯Ì¬ÊµÊ±¼à²â²¢Êä³öReactÓ¦ÓõÈredux£¬action£¬sagaÒì²½ÇëÇóµÈÐÅÏ¢£¬Èçͼ£º

Ê×ÏȰ²×°£º
yarn
add --dev reactotron-react-js |
È»ºó³õʼ»¯ReactotronÏà¹ØÅäÖãº
import
Reactotron from 'reactotron-react-js';
import { reactotronRedux as reduxPlugin } from
'reactotron-redux';
import sagaPlugin from 'reactotron-redux-saga';
if (Config.useReactotron) {
// refer to https://github.com/infinitered/reactotron
for more options!
Reactotron
.configure({ name: 'React Blog' })
.use(reduxPlugin({ onRestore: Immutable }))
.use(sagaPlugin())
.connect();
// Let's clear Reactotron on every time we
load the app
Reactotron.clear();
// Totally hacky, but this allows you to not
both importing reactotron-react-js
// on every file. This is just DEV mode, so
no big deal.
console.tron = Reactotron;
}
|
È»ºóÆôʹÓÃconsole.tron.overlay·½·¨ÍØÕ¹Èë¿Ú×é¼þ£º
import
'./config/ReactotronConfig';
import DebugConfig from './config/DebugConfig';
class App extends Component {
render () {
return (
<Provider store={store}>
<AppContainer />
</Provider>
)
}
}
// allow reactotron overlay for fast design
in dev mode
export default DebugConfig.useReactotron
? console.tron.overlay(App)
: App |
ÖÁ´Ë¾Í¿ÉÒÔʹÓÃReactotron¿Í»§¶Ë²¶»ñÓ¦ÓÃÖз¢ÆðµÄËùÓеÄreduxºÍactionÁË¡£
×é¼þ»®·Ö
React×é¼þ»¯¿ª·¢ÔÔòÊÇ×é¼þ¸ºÔðäÖȾUI£¬×é¼þ²»Í¬×´Ì¬¶ÔÓ¦²»Í¬UI£¬Í¨³£×ñÑÒÔÏÂ×é¼þÉè¼ÆË¼Â·£º
1. ²¼¾Ö×é¼þ£º½ö½öÉæ¼°Ó¦ÓÃUI½çÃæ½á¹¹µÄ×é¼þ£¬²»Éæ¼°ÈκÎÒµÎñÂß¼£¬Êý¾ÝÇëÇó¼°²Ù×÷£»
2. ÈÝÆ÷×é¼þ£º¸ºÔð»ñÈ¡Êý¾Ý£¬´¦ÀíÒµÎñÂß¼£¬Í¨³£ÔÚrender()º¯ÊýÄÚ·µ»ØÕ¹Ê¾ÐÍ×é¼þ£»
3. չʾÐÍ×é¼þ£º¸ºÔðÓ¦ÓõĽçÃæUIչʾ£»
4. UI×é¼þ£ºÖ¸³éÏó³öµÄ¿ÉÖØÓõÄUI¶ÀÁ¢×é¼þ£¬Í¨³£ÊÇÎÞ״̬×é¼þ£»

Redux
ÏÖÔÚµÄÈκδóÐÍwebÓ¦ÓÃÈç¹ûÉÙÁË״̬¹ÜÀíÈÝÆ÷£¬ÄÇÕâ¸öÓ¦ÓþÍȱÉÙÁËʱ´úÌØÕ÷£¬¿ÉÑ¡µÄ¿âÖîÈçmobx£¬reduxµÈ£¬Êµ¼ÊÉÏ´óͬСÒ죬¸÷È¡ËùÐ裬ÒÔreduxΪÀý£¬reduxÊÇ×î³£ÓõÄReactÓ¦ÓÃ״̬ÈÝÆ÷¿â£¬¶ÔÓÚReact
NativeÓ¦ÓÃÒ²ÊÊÓá£
ReduxÊÇÒ»¸öJavaScriptÓ¦ÓõĿÉÔ¤²â״̬¹ÜÀíÈÝÆ÷£¬Ëü²»ÒÀÀµÓÚ¾ßÌå¿ò¼Ü»òÀà¿â£¬ËùÒÔËüÔÚ¶àÆ½Ì¨µÄÓ¦Óÿª·¢ÖÐÓÐ×ÅÒ»ÖµĿª·¢·½Ê½ºÍЧÂÊ£¬ÁíÍâËü»¹ÄܰïÎÒÃÇÇáËɵÄʵÏÖʱ¼äÂÃÐУ¬¼´actionµÄ»Ø·Å¡£

1. Êý¾Ýµ¥Ò»À´Ô´ÔÔò£ºÊ¹ÓÃRedux×÷ΪӦÓÃ״̬¹ÜÀíÈÝÆ÷£¬Í³Ò»¹ÜÀíÓ¦ÓõÄ״̬Ê÷£¬ËüÍÆ´ÓÊý¾Ýµ¥Ò»¿ÉÐÅÀ´Ô´ÔÔò£¬ËùÓÐÊý¾Ý¶¼À´×Ôredux
store£¬ËùÓеÄÊý¾Ý¸üÐÂÒ²¶¼ÓÉredux´¦Àí£»
2. redux store״̬Ê÷£ºredux¼¯ÖйÜÀíÓ¦ÓÃ״̬£¬×éÖ¯¹ÜÀíÐÎʽ¾ÍºÃ±ÈDOMÊ÷ºÍReact×é¼þÊ÷Ò»Ñù£¬ÒÔÊ÷µÄÐÎʽ×éÖ¯£¬¼òµ¥¸ßЧ£»
3. reduxºÍstore£ºreduxÊÇÒ»ÖÖFluxµÄʵÏÖ·½°¸£¬ËùÒÔ´´½¨ÁËstoreÒ»´Ê£¬ËüÀàËÆÓÚÉ̵꣬¼¯ÖйÜÀíÓ¦ÓÃ״̬£¬Ö§³Ö½«Ã¿Ò»¸ö·¢²¼µÄaction·Ö·¢ÖÁËùÓÐreducer£»
4. action£ºÒÔ¶ÔÏóÊý¾Ý¸ñʽ´æÔÚ£¬Í¨³£ÖÁÉÙÓÐtypeºÍpayloadÊôÐÔ£¬ËüÊǶÔreduxÖж¨ÒåµÄÈÎÎñµÄÃèÊö£»
5. reducer£ºÍ¨³£ÊÇÒÔº¯ÊýÐÎʽ´æÔÚ£¬½ÓÊÕstate£¨Ó¦Óþֲ¿×´Ì¬£©ºÍaction¶ÔÏóÁ½¸ö²ÎÊý£¬¸ù¾Ýaction.type(actionÀàÐÍ)Ö´Ðв»Í¬µÄÈÎÎñ£¬×ñѺ¯Êýʽ±à³Ì˼Ï룻
6. dispatch£ºstoreÌṩµÄ·Ö·¢actionµÄ¹¦ÄÜ·½·¨£¬´«µÝÒ»¸öaction¶ÔÏó²ÎÊý£»
7. createStore£º´´½¨storeµÄ·½·¨£¬½ÓÊÕreducer£¬³õʼӦÓÃ״̬£¬reduxÖмä¼þºÍÔöÇ¿Æ÷£¬³õʼ»¯store£¬¿ªÊ¼¼àÌýaction£»
Öмä¼þ£¨Redux Middleware£©
ReduxÖмä¼þ£¬ºÍNodeÖмä¼þÒ»Ñù£¬Ëü¿ÉÒÔÔÚaction·Ö·¢ÖÁÈÎÎñ´¦Àíreducer֮ǰ×öһЩ¶îÍ⹤×÷£¬dispatch·¢²¼µÄaction½«ÒÀ´Î´«µÝ¸øËùÓÐÖмä¼þ£¬×îÖÕµ½´ïreducer£¬ËùÒÔÎÒÃÇʹÓÃÖмä¼þ¿ÉÒÔÍØÕ¹ÖîÈç¼Ç¼ÈÕÖ¾£¬Ìí¼Ó¼à¿Ø£¬Çл»Â·Óɵȹ¦ÄÜ£¬ËùÒÔÖмä¼þ±¾ÖÊÉÏÖ»ÊÇÍØÕ¹ÁËstore.dispatch·½·¨¡£

ÔöÇ¿Æ÷£¨Store Enhancer£©
ÓÐЩʱºòÎÒÃÇ¿ÉÄܲ¢²»Âú×ãÓÚÍØÕ¹dispatch·½·¨£¬»¹Ï£ÍûÄÜÔöÇ¿store£¬reduxÌṩÒÔÔöÇ¿Æ÷ÐÎʽÔöÇ¿storeµÄ¸÷¸ö·½Ã棬ÉõÖÁ¿ÉÒÔÍêÈ«¶¨ÖÆÒ»¸östore¶ÔÏóÉϵÄËùÓнӿڣ¬¶ø²»½ö½öÊÇstore.dispatch·½·¨¡£
const
logEnhancer = (createStore) => (reducer,
preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState,
enhancer)
const originalDispatch = store.dispatch
store.dispatch = (action) => {
console.log(action)
originalDispatch(action)
}
return store
} |
×î¼òµ¥µÄÀý×Ó´úÂëÈçÉÏ£¬Ðº¯Êý½ÓÊÕreduxµÄcreateStore·½·¨ºÍ´´½¨storeÐèÒªµÄ²ÎÊý£¬È»ºóÔÚº¯ÊýÄÚ²¿±£´æstore¶ÔÏóÉÏij·½·¨µÄÒýÓã¬ÖØÐÂʵÏָ÷½·¨£¬ÔÚÀïÃæ´¦ÀíÍêÔöÇ¿Âß¼ºóµ÷ÓÃÔʼ·½·¨£¬±£Ö¤Ôʼ¹¦ÄÜÕý³£Ö´ÐУ¬ÕâÑù¾ÍÔöÇ¿ÁËstoreµÄdispatch·½·¨¡£
¿ÉÒÔ¿´µ½£¬ÔöÇ¿Æ÷ÍêÈ«ÄÜʵÏÖÖмä¼þµÄ¹¦ÄÜ£¬Æäʵ£¬Öмä¼þ¾ÍÊÇÒÔÔöÇ¿Æ÷·½Ê½ÊµÏֵģ¬ËüÌṩµÄcompose·½·¨¾Í¿ÉÒÔ×éºÏ½«ÎÒÃÇ´«ÈëµÄÔöÇ¿Æ÷ÍØÕ¹µ½store£¬¶øÈç¹ûÎÒÃÇ´«ÈëÖмä¼þ£¬ÔòÐèÒªÏȵ÷ÓÃapplyMiddleware·½·¨°ü×°£¬ÄÚ²¿ÒÔÔöÇ¿Æ÷ÐÎʽ½«Öмä¼þ¹¦ÄÜÍØÕ¹µ½store.dispatch·½·¨
react-redux
ReduxÊÇÒ»¸ö¶ÀÁ¢µÄJavaScriptÓ¦ÓÃ״̬¹ÜÀíÈÝÆ÷¿â£¬Ëü¿ÉÒÔÓëReact¡¢Angular¡¢Ember¡¢jQueryÉõÖÁÔÉúJavaScriptÓ¦ÓÃÅäºÏʹÓã¬ËùÒÔ¿ª·¢ReactÓ¦ÓÃʱ£¬ÐèÒª½«ReduxºÍReactÓ¦ÓÃÁ¬½ÓÆðÀ´£¬²ÅÄÜͳһʹÓÃRedux¹ÜÀíÓ¦ÓÃ״̬£¬Ê¹Óùٷ½ÌṩµÄreact-redux£¨https://github.com/reactjs/react-redux£©¿â¡£
class
App extends Component {
render () {
const { store } = this.props
return (
<Provider store={store}>
<div>
<Routes />
</div>
</Provider>
)
}
} |
react-redux¿âÌṩProvider×é¼þͨ¹ýcontext·½Ê½ÏòÓ¦ÓÃ×¢Èëstore£¬È»ºó¿ÉÒÔʹÓÃconnect¸ß½×·½·¨£¬»ñÈ¡²¢¼àÌýstore£¬È»ºó¸ù¾Ýstore
stateºÍ×é¼þ×ÔÉíprops¼ÆËãµÃµ½ÐÂprops£¬×¢Èë¸Ã×é¼þ£¬²¢ÇÒ¿ÉÒÔͨ¹ý¼àÌýstore£¬±È½Ï¼ÆËã³öµÄÐÂpropsÅжÏÊÇ·ñÐèÒª¸üÐÂ×é¼þ¡£
¸ü¶à¹ØÓÚreact-reduxµÄÄÚÈÝ¿ÉÒÔÔĶÁ֮ǰµÄÎÄÕ£ºReact-Redux·ÖÎö
createStore
ʹÓÃreduxÌṩµÄcreateStore·½·¨´´½¨redux store£¬µ«ÊÇÔÚʵ¼ÊÏîÄ¿ÖÐÎÒÃdz£³£ÐèÒªÍØÕ¹reduxÌí¼ÓijЩ×Ô¶¨Ò幦ÄÜ»ò·þÎñ£¬ÈçÌí¼ÓreduxÖмä¼þ£¬Ìí¼ÓÒì²½ÈÎÎñ¹ÜÀísaga£¬ÔöÇ¿reduxµÈ£º
//
creates the store
export default (rootReducer, rootSaga, initialState)
=> {
/* ------------- Redux Configuration -------------
*/
// Middlewares
// Build the middleware for intercepting and
dispatching navigation actions
const blogRouteMiddleware = routerMiddleware(history)
const sagaMiddleware = createSagaMiddleware()
const middleware = [blogRouteMiddleware, sagaMiddleware]
// enhancers
const enhancers = []
let composeEnhancers = compose
// create store
const store = createStore(
combineReducers({
router: routerReducer,
...reducers
}),
initialState,
composeEnhancers(
applyMiddleware(...middleware),
...enhancers
)
)
sagaMiddleware.run(saga)
return store;
} |
reduxÓëImmutable
reduxĬÈÏÌṩÁËcombineReducers·½·¨ÕûºÏreduersÖÁredux£¬È»¶ø¸ÃĬÈÏ·½·¨ÆÚÍû½ÓÊÜÔÉúJavaScript¶ÔÏó²¢ÇÒËü°Ñstate×÷ΪÔÉú¶ÔÏó´¦Àí£¬ËùÒÔµ±ÎÒÃÇʹÓÃcreateStore·½·¨²¢ÇÒ½ÓÊÜÒ»¸öImmutable¶ÔÏó×÷Ó¦Óóõʼ״̬ʱ£¬reducer½«»á·µ»ØÒ»¸ö´íÎó£¬Ô´´úÂëÈçÏ£º
if
(!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of
"` + ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
+
".Expected argument to be an object with
the following +
`keys:"${reducerKeys.join('", "')}"`
)
} |
ÈçÉϱíÃ÷£¬ÔʼÀàÐÍreducer½ÓÊܵÄstate²ÎÊýÓ¦¸ÃÊÇÒ»¸öÔÉúJavaScript¶ÔÏó£¬ÎÒÃÇÐèÒª¶ÔcombineReducersÆä½øÐÐÔöÇ¿£¬ÒÔʹÆäÄÜ´¦ÀíImmutable¶ÔÏó£¬redux-immutable
¼´Ìṩ´´½¨Ò»¸ö¿ÉÒÔºÍImmutable.jsÐ×÷µÄRedux combineReducers¡£
import
{ combineReducers } from 'redux-immutable';
import Immutable from 'immutable';
import configureStore from './CreateStore';
// use Immutable.Map to create the store state
tree
const initialState = Immutable.Map();
export default () => {
// Assemble The Reducers
const rootReducer = combineReducers({
...RouterReducer,
...AppReducer
});
return configureStore(rootReducer, rootSaga,
initialState);
} |
ÈçÉÏ´úÂ룬¿ÉÒÔ¿´¼ûÎÒÃÇ´«ÈëµÄinitialStateÊÇÒ»¸öImmutable.MapÀàÐÍÊý¾Ý£¬ÎÒÃǽ«reduxÕû¸östateÊ÷´Ô¸ùÔ´¿ªÊ¼Immutable»¯£¬ÁíÍâ´«ÈëÁË¿ÉÒÔ´¦ÀíImmutable
stateµÄreducersºÍsagas¡£
ÁíÍâÿһ¸östateÊ÷½ÚµãÊý¾Ý¶¼ÊÇImmutable½á¹¹£¬ÈçAppReducer£º
const
initialState = Immutable.fromJS({
ids: [],
posts: {
list: [],
total: 0,
totalPages: 0
}
})
const AppReducer = (state = initialState, action)
=> {
case 'RECEIVE_POST_LIST':
const newState = state.merge(action.payload)
return newState || state
default:
return state
} |
ÕâÀïĬÈÏʹÓÃImmutable.fromJS()·½·¨×´Ì¬Ê÷½Úµã¶ÔÏóת»¯ÎªImmutable½á¹¹£¬²¢ÇÒ¸üÐÂstateʱʹÓÃImmutable·½·¨state.merge()£¬±£Ö¤×´Ì¬Í³Ò»¿ÉÔ¤²â¡£
React·ÓÉ
ÔÚReact webµ¥Ò³ÃæÓ¦ÓÃÖУ¬Ò³Ãæ¼¶UI×é¼þµÄչʾºÍÇл»ÍêÈ«ÓÉ·ÓÉ¿ØÖÆ£¬Ã¿Ò»¸ö·Óɶ¼ÓжÔÓ¦µÄURL¼°Â·ÓÉÐÅÏ¢£¬ÎÒÃÇ¿ÉÒÔͨ¹ý·ÓÉͳһ¸ßЧµÄ¹ÜÀíÎÒÃǵÄ×é¼þÇл»£¬±£³ÖUIÓëURLͬ²½£¬±£Ö¤Ó¦ÓõÄÎȶ¨ÐÔ¼°ÓѺÃÌåÑé¡£
react-router
React RouterÊÇÍêÕûµÄReact ·Óɽâ¾ö·½°¸£¬Ò²ÊÇ¿ª·¢ReactÓ¦ÓÃ×ʹÓõÄ·ÓɹÜÀí¿â£¬Ö»ÒªÓùýËü£¬¾ø¶Ô»áϲ»¶ÉÏËüµÄÉè¼Æ£¬ËüÌṩ¼òµ¥µÄAPI£¬ÒÔÉùÃ÷ʽ·½Ê½ÊµÏÖÇ¿´óµÄ·Óɹ¦ÄÜ£¬ÖîÈç°´Ðè¼ÓÔØ£¬¶¯Ì¬Â·Óɵȡ£
1. ÉùÃ÷ʽ£ºÓï·¨¼ò½à£¬ÇåÎú£»
2. °´Ðè¼ÓÔØ£ºÑÓ³Ù¼ÓÔØ£¬¸ù¾ÝʹÓÃÐèÒªÅжÏÊÇ·ñÐèÒª¼ÓÔØ£»
3. ¶¯Ì¬Â·ÓÉ£º¶¯Ì¬×éºÏÓ¦Ó÷Óɽṹ£¬¸üÁé»î£¬¸ü·ûºÏ×é¼þ»¯¿ª·¢Ä£Ê½£»
¶¯Ì¬Â·ÓÉÓ뾲̬·ÓÉ
ʹÓÃreact-router v4°æ±¾¿ÉÒÔ¶¨Òå¿çƽ̨µÄÓ¦Óö¯Ì¬Â·Óɽṹ£¬ËùνµÄ¶¯Ì¬Â·ÓÉ£¨Dynamic
Routing£©¼´ÔÚäÖȾ¹ý³ÌÖз¢Éú·ÓɵÄÇл»£¬¶ø²»ÐèÒªÔÚ´´½¨Ó¦ÓÃǰ¾ÍÅäÖúã¬ÕâÒ²ÕýÊÇÆäÇø±ðÓÚ¾²Ì¬Â·ÓÉ£¨Static
Routing£©ËùÔÚ£¬¶¯Ì¬Â·ÓÉÌá¸ß¸üÁé»îµÄ·ÓÉ×éÖ¯·½Ê½£¬¶øÇÒ¸ü·½±ã±àÂëʵÏÖ·Óɰ´Ðè¼ÓÔØ×é¼þ¡£
ÔÚreact-router v2ºÍv3°æ±¾ÖУ¬¿ª·¢ReactÓ¦ÓÃÐèÒªÔÚ¿ªÊ¼äÖȾǰ¾Í¶¨ÒåºÃÍêÕûµÄÓ¦Ó÷Óɽṹ£¬ËùÓеÄ·Óɶ¼ÐèҪͬʱ³õʼ»¯£¬²ÅÄÜÔÚÓ¦ÓÃäÖȾºóÉúЧ£¬»á²úÉúºÜ¶àǶÌ×»¯Â·ÓÉ£¬É¥Ê§Á˶¯Ì¬Â·ÓɵÄÁé»îÐԺͼò½àµÄ°´Ðè¼ÓÔØ±àÂ뷽ʽ¡£
react-router v4.x
ÔÚreact-router 2.xºÍ3.x°æ±¾ÖУ¬¶¨ÒåÒ»¸öÓ¦Ó÷Óɽṹͨ³£ÈçÏ£º
import
React from 'react'
import ReactDOM from 'react-dom'
import { browserHistory, Router, Route, IndexRoute
} from 'react-router'
import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'
ReactDOM.render(
<Router history={browserHistory}>
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='features' component={Features}
/>
</Route>
</Router>,
document.getElementById('app')
) |
ºÜ¼òµ¥£¬µ«ÊÇËùÓеÄ·Óɽṹ¶¼ÐèÒªÔÚäÖȾӦÓÃǰ£¬Í³Ò»¶¨Ò壬²ã²ãǶÌ×£»¶øÇÒÈç¹ûҪʵÏÖÒì²½°´Ðè¼ÓÔØ»¹ÐèÒªÔÚÕâÀï¶Ô·ÓÉÅäÖöÔÏó½øÐÐÐ޸ģ¬Ê¹ÓÃgetComponentAPI£¬²¢ÇÖÈë¸ÄÔì¸Ã×é¼þ£¬ÅäºÏwebpackµÄÒì²½´ò°ü¼ÓÔØAPI£¬ÊµÏÖ°´Ðè¼ÓÔØ£º
1. ·Óɲã²ãǶÌ×£¬±ØÐëÔÚäÖȾӦÓÃǰͳһÉùÃ÷£»
2. API²»Í¬£¬ÐèҪʹÓÃgetComponent£¬Ôö¼Ó·ÓÉÅäÖöÔÏóµÄ¸´ÔÓÐÔ£»
3. <Route>Ö»ÊÇÒ»¸öÉùÃ÷·Óɵĸ¨Öú±êÇ©£¬±¾ÉíÎÞÒâÒ壻
¶øÊ¹ÓÃreact-router v4.xÔòÈçÏ£º
//
react-dom (what we'll use here)
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), el)
const App = () => (
<div>
<nav>
<Link to="/about">Dashboard</Link>
</nav>
<Home />
<div>
<Route path="/about" component={About}/>
<Route path="/features" component={Features}/>
</div>
</div>
) |
Ïà±È֮ǰ°æ±¾£¬¼õÉÙÁËÅäÖû¯µÄºÛ¼££¬¸ü͹ÏÔÁË×é¼þ»¯µÄ×éÖ¯·½Ê½£¬¶øÇÒÔÚäÖȾ×é¼þʱ²ÅʵÏָò¿·Ö·ÓÉ£¬¶øÈç¹ûÆÚÍû°´Ðè¼ÓÔØ¸Ã×é¼þ£¬Ôò¿ÉÒÔͨ¹ý·âװʵÏÖÒ»¸öÖ§³ÖÒì²½¼ÓÔØ×é¼þµÄ¸ß½××é¼þ£¬½«¾¹ý¸ß½××é¼þ´¦Àíºó·µ»ØµÄ×é¼þ´«Èë<Route>¼´¿É£¬ÒÀÈ»×ñÑ×é¼þ»¯ÐÎʽ£º
1. Áé»îÐÔ£ºÂ·ÓÉ¿ÉÒÔÔÚäÖȾ×é¼þÖÐÉùÃ÷£¬²»ÐèÒÀÀµÓÚÆäËû·ÓÉ£¬²»ÐèÒª¼¯ÖÐÅäÖã»
2. ¼ò½à£ºÍ³Ò»´«Èëcomponent£¬±£Ö¤Â·ÓÉÉùÃ÷µÄ¼ò½àÐÔ£»
3. ×é¼þ»¯£º<Route>×÷Ϊһ¸öÕæÊµ×é¼þ´´½¨Â·ÓÉ£¬¿ÉÒÔäÖȾ£»
·Óɹ³×Ó·½·¨
ÁíÍâÐèҪעÒâµÄÊÇ£¬Ïà¶ÔÓÚ֮ǰ°æ±¾ÌṩonEnter, onUpdate, onLeaveµÈ¹³×Ó·½·¨APIÔÚÒ»¶¨³Ì¶ÈÉÏÌá¸ßÁ˶Ô·ÓɵĿɿØÐÔ£¬µ«ÊÇʵÖÊÖ»ÊǸ²¸ÇÁËäÖȾ×é¼þµÄÉúÃüÖÜÆÚ·½·¨£¬ÏÖÔÚÎÒÃÇ¿ÉÒÔͨ¹ý·ÓÉäÖȾ×é¼þµÄÉúÃüÖÜÆÚ·½·¨Ö±½Ó¿ØÖÆÂ·ÓÉ£¬ÈçʹÓÃcomponentDidMount
»ò componentWillMount ´úÌæ onEnter¡£
·ÓÉÓëRedux
ͬʱʹÓÃReact-RouterºÍReduxʱ£¬´ó¶àÊýÇé¿öÊÇÕý³£µÄ£¬µ«ÊÇÒ²¿ÉÄܳöÏÖ·Óɱä¸ü×é¼þδ¸üеÄÇé¿ö£¬È磺
1. ÎÒÃÇʹÓÃreduxµÄconnect·½·¨½«×é¼þÁ¬½ÓÖÁredux£ºconnect(Home);
2. ×é¼þ²»ÊÇÒ»¸ö·ÓÉäÖȾ×é¼þ£¬¼´²»ÊÇʹÓÃRoute>×é¼þÐÎʽ£º<Route component={Home}
/>ÉùÃ÷äÖȾµÄ£»
ÕâÊÇÎªÊ²Ã´ÄØ£¿£¬ÒòΪRedux»áʵÏÖ×é¼þµÄshouldComponentUpdate·½·¨£¬µ±Â·Óɱ仯ʱ£¬¸Ã×é¼þ²¢Ã»ÓнÓÊÕµ½props±íÃ÷·¢ÉúÁ˱ä¸ü£¬ÐèÒª¸üÐÂ×é¼þ¡£
ÄÇôÈçºÎ½â¾öÎÊÌâÄØ£¿£¬Òª½â¾öÕâ¸öÎÊÌâÖ»ÐèÒª¼òµ¥µÄʹÓÃreact-router-domÌṩµÄwithRouter·½·¨°ü¹ü×é¼þ£º
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Home))
|
ReduxÕûºÏ
ÔÚʹÓÃReduxÒÔºó£¬ÐèÒª×ñÑreduxµÄÔÔò£ºµ¥Ò»¿ÉÐÅÊý¾ÝÀ´Ô´£¬¼´ËùÓÐÊý¾ÝÀ´Ô´¶¼Ö»ÄÜÊÇreudx
store£¬react·ÓÉ״̬Ҳ²»Ó¦ÀýÍ⣬ËùÒÔÐèÒª½«Â·ÓÉstateÓëstore stateÁ¬½Ó¡£
react-router-redux
Á¬½ÓReact RouterÓëRedux£¬ÐèҪʹÓÃreact-router-redux¿â£¬¶øÇÒreact-router
v4°æ±¾ÐèÒªÖ¸¶¨°²×°@next°æ±¾ºÍhsitory¿â£º
yarn
add react-router-redux@next
yarn add history |
È»ºó£¬ÔÚ´´½¨storeʱ£¬ÐèҪʵÏÖÈçÏÂÅäÖãº
1. ´´½¨Ò»¸öhistory¶ÔÏ󣬶ÔÓÚwebÓ¦Óã¬ÎÒÃÇÑ¡ÔñbrowserHisotry£¬¶ÔÓ¦ÐèÒª´Óhistory/createBrowserHistoryÄ£¿éÒýÈëcreateHistory·½·¨ÒÔ´´½¨history¶ÔÏó£»
µã´Ë²é¿´¸ü¶àhistoryÏà¹ØÄÚÈÝ£¨https://reacttraining.com/react-router/web/api/history£©
2. Ìí¼ÓrouterReducerºÍrouterMiddlewareÖмä¼þ¡°£¬ÆäÖÐrouterMiddlewareÖмä¼þ½ÓÊÕhistory¶ÔÏó²ÎÊý£¬Á¬½ÓstoreºÍhistory£¬µÈͬÓھɰ汾µÄsyncHistoryWithStore£»
import
createHistory from 'history/createBrowserHistory'
import { ConnectedRouter, routerReducer, routerMiddleware,
push } from 'react-router-redux'
// Create a history of your choosing (we're
using a browser history in this case)
export const history = createHistory()
// Build the middleware for intercepting and
dispatching navigation actions
const middleware = routerMiddleware(history)
// Add the reducer to your store on the `router`
key
// Also apply our middleware for navigating
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
)
return store |
ÔÚäÖȾ¸ù×é¼þʱ£¬ÎÒÃdzéÏó³öÁ½¸ö×é¼þ£º
1. ³õʼ»¯äÖȾ¸ù×é¼þ£¬¹ÒÔØÖÁDOMµÄ¸ù×é¼þ£¬ÓÉ<Provider>×é¼þ°ü¹ü£¬×¢Èëstore£»
2. ·ÓÉÅäÖÃ×é¼þ£¬ÔÚ¸ù×é¼þÖУ¬ÉùÃ÷·ÓÉÅäÖÃ×é¼þ£¬³õʼ»¯±ØÒªµÄÓ¦Ó÷Óɶ¨Ò弰·ÓɶÔÏó
import
createStore from './store/'
import Routes from './routes/'
import appReducer from './store/appRedux'
const store = createStore({}, {
app: appReducer
})
/**
* ÏîÄ¿¸ù×é¼þ
* @class App
* @extends Component
*/
class App extends Component {
render () {
const { store } = this.props
return (
<Provider store={store}>
<div>
<Routes />
</div>
</Provider>
)
}
}
// äÖȾ¸ù×é¼þ
ReactDOM.render(
<App store={store} />,
document.getElementById('app')
) |
ÉÏÃæµÄ<Routes>×é¼þÊÇÏîÄ¿µÄ·ÓÉ×é¼þ£º
import
{ history } from '../store/'
import { ConnectedRouter } from 'react-router-redux'
import { Route } from 'react-router'
class Routes extends Component {
render () {
return (
<ConnectedRouter history={history}>
<div>
<BlogHeader />
<div>
<Route exact path='/' component={Home} />
<Route exact path='/posts/:id' component={Article}
/>
</div>
</div>
</ConnectedRouter>
)
}
}
|
Ê×ÏÈʹÓÃreact-router-reduxÌṩµÄConnectedRouter×é¼þ°ü¹ü·ÓÉÅäÖ㬸Ã×é¼þ½«×Ô¶¯Ê¹ÓÃ<Provider>×é¼þ×¢ÈëµÄstore£¬ÎÒÃÇÐèÒª×öµÄÊÇÊÖ¶¯´«ÈëhistoryÊôÐÔ£¬ÔÚ×é¼þÄÚ»áµ÷ÓÃhistory.listen·½·¨¼àÌýä¯ÀÀÆ÷LOCATION_CHANGEʼþ£¬×îºó·µ»Øreact-routerµÄ<Router
>×é¼þ£¬´¦Àí×÷Ϊthis.props.children´«ÈëµÄ·ÓÉÅäÖã¬ConnectedRouter×é¼þÄÚÈÝ´«ËÍ
dispatchÇл»Â·ÓÉ
ÅäÖÃÉÏÃæ´úÂëºó£¬¾ÍÄܹ»ÒÔdispatch actionµÄ·½Ê½´¥·¢Â·ÓÉÇл»ºÍ×é¼þ¸üÐÂÁË£º
import
{ push } from 'react-router-redux'
// Now you can dispatch navigation actions from
anywhere!
store.dispatch(push('/about')) |
Õâ¸öreducerËù×öµÄÖ»Êǽ«Appµ¼º½Â·ÓÉ״̬ºÏ²¢Èëstore¡£
redux³Ö¾Ã»¯
ÎÒÃÇÖªµÀä¯ÀÀÆ÷ĬÈÏÓÐ×ÊÔ´µÄ»º´æ¹¦Äܲ¢ÇÒÌṩ±¾µØ³Ö¾Ã»¯´æ´¢·½Ê½ÈçlocalStorage£¬indexDb£¬webSQLµÈ£¬Í¨³£¿ÉÒÔ½«Ä³Ð©Êý¾Ý´æ´¢ÔÚ±¾µØ£¬ÔÚÒ»¶¨ÖÜÆÚÄÚ£¬µ±Óû§ÔٴηÃÎÊʱ£¬Ö±½Ó´Ó±¾µØ»Ö¸´Êý¾Ý£¬¿ÉÒÔ¼«´óÌá¸ßÓ¦ÓÃÆô¶¯ËÙ¶È£¬Óû§ÌåÑé¸üÓÐÓÅÊÆ£¬ÎÒÃÇ¿ÉÒÔʹÓÃlocalStorage´æ´¢Ò»Ð©Êý¾Ý£¬Èç¹ûÊǽϴóÁ¿Êý¾Ý´æ´¢¿ÉÒÔʹÓÃwebSQL¡£
ÁíÍⲻͬÓÚÒÔÍùµÄÖ±½Ó´æ´¢Êý¾Ý£¬Æô¶¯Ó¦ÓÃʱ±¾µØ¶ÁȡȻºó»Ö¸´Êý¾Ý£¬¶ÔÓÚreduxÓ¦ÓöøÑÔ£¬Èç¹ûÖ»ÊÇ´æ´¢Êý¾Ý£¬ÄÇôÎÒÃǾ͵ÃΪÿһ¸öreducerÍØÕ¹£¬µ±ÔÙ´ÎÆô¶¯Ó¦ÓÃʱȥ¶ÁÈ¡³Ö¾Ã»¯µÄÊý¾Ý£¬ÕâÊDZȽϷ±Ëö¶øÇÒµÍЧµÄ·½Ê½£¬ÊÇ·ñ¿ÉÒÔ³¢ÊÔ´æ´¢reducer
key£¬È»ºó¸ù¾Ýkey»Ö¸´¶ÔÓ¦µÄ³Ö¾Ã»¯Êý¾Ý£¬Ê×ÏÈ×¢²áRehydrate reducer£¬µ±´¥·¢actionʱ¸ù¾ÝÆäreducer
key»Ö¸´Êý¾Ý£¬È»ºóÖ»ÐèÒªÔÚÓ¦ÓÃÆô¶¯Ê±·Ö·¢action£¬ÕâÒ²ºÜÈÝÒ׳éÏó³É¿ÉÅäÖõÄÍØÕ¹·þÎñ£¬Êµ¼ÊÉÏÈý·½¿âredux-persist£¨https://github.com/rt2zz/redux-persist£©ÒѾΪÎÒÃÇ×öºÃÁËÕâÒ»ÇС£
redux-persist
ҪʵÏÖreduxµÄ³Ö¾Ã»¯£¬°üÀ¨redux storeµÄ±¾µØ³Ö¾Ã»¯´æ´¢¼°»Ö¸´Æô¶¯Á½¸ö¹ý³Ì£¬Èç¹ûÍêÈ«×Ô¼º±àдʵÏÖ£¬´úÂëÁ¿±È½Ï¸´ÔÓ£¬¿ÉÒÔʹÓÿªÔ´¿âredux-persist£¬ËüÌṩpersistStoreºÍautoRehydrate·½·¨·Ö±ð³Ö¾Ã»¯±¾µØ´æ´¢store¼°»Ö¸´Æô¶¯store£¬ÁíÍ⻹֧³Ö×Ô¶¨Òå´«Èë³Ö¾Ã»¯¼°»Ö¸´storeʱ¶Ôstore
stateµÄת»»ÍØÕ¹¡£
³Ö¾Ã»¯store
ÈçÏÂÔÚ´´½¨storeʱ»áµ÷ÓÃpersistStoreÏà¹Ø·þÎñ-RehydrationServices.updateReducers()£º
//
configure persistStore and check reducer version
number
if (ReduxPersistConfig.active) {
RehydrationServices.updateReducers(store);
} |
¸Ã·½·¨ÄÚʵÏÖÁËstoreµÄ³Ö¾Ã»¯´æ´¢£º
//
Check to ensure latest reducer version
storage.getItem('reducerVersion').then((localVersion)
=> {
if (localVersion !== reducerVersion) {
// Çå¿Õ store
persistStore(store, null, startApp).purge();
storage.setItem('reducerVersion', reducerVersion);
} else {
persistStore(store, null, startApp);
}
}).catch(() => {
persistStore(store, null, startApp);
storage.setItem('reducerVersion', reducerVersion);
})
|
»áÔÚlocalStorage´æ´¢Ò»¸öreducer°æ±¾ºÅ£¬Õâ¸öÊÇÔÚÓ¦ÓÃÅäÖÃÎļþÖпÉÒÔÅäÖã¬Ê×´ÎÖ´Ðг־û¯Ê±´æ´¢¸Ã°æ±¾ºÅ¼°store£¬Èôreducer°æ±¾ºÅ±ä¸üÔòÇå¿ÕÔÀ´´æ´¢µÄstore£¬·ñÔò´«Èëstore¸ø³Ö¾Ã»¯·½·¨persistStore¼´¿É¡£
persistStore(store,
[config], [callback]) |
¸Ã·½·¨Ö÷ҪʵÏÖstoreµÄ³Ö¾Ã»¯ÒÔ¼°·Ö·¢rehydration action :
1. ¶©ÔÄ redux store£¬µ±Æä·¢Éú±ä»¯Ê±´¥·¢store´æ´¢²Ù×÷£»
2. ´ÓÖ¸¶¨µÄStorageEngine£¨ÈçlocalStorage£©ÖлñÈ¡Êý¾Ý£¬½øÐÐת»»£¬È»ºóͨ¹ý·Ö·¢
REHYDRATE action£¬´¥·¢ REHYDRATE ¹ý³Ì£»
½ÓÊÕ²ÎÊýÖ÷ÒªÈçÏ£º
1. store: ³Ö¾Ã»¯µÄstore£»
2. config£ºÅäÖöÔÏó
a. storage£ºÒ»¸ö ³Ö¾Ã»¯ÒýÇæ£¬ÀýÈç LocalStorage ºÍ AsyncStorage£»
b. transforms£º ÔÚ rehydration ºÍ storage ½×¶Î±»µ÷ÓõÄת»»Æ÷£»
c. blacklist£º ºÚÃûµ¥Êý×飬ָ¶¨³Ö¾Ã»¯ºöÂ﵀ reducers µÄ key£»
3. callback£ºehydration ²Ù×÷½áÊøºóµÄ»Øµ÷£»
»Ö¸´Æô¶¯
ºÍpersisStoreÒ»Ñù£¬ÒÀÈ»ÊÇÔÚ´´½¨redux storeʱ³õʼ»¯×¢²árehydrateÍØÕ¹£º
//
add the autoRehydrate enhancer
if (ReduxPersist.active) {
enhancers.push(autoRehydrate());
} |
¸Ã·½·¨ÊµÏֵŦÄܼܺòµ¥£¬¼´Ê¹Óà ³Ö¾Ã»¯µÄÊý¾Ý»Ö¸´(rehydrate) store ÖÐÊý¾Ý£¬ËüÆäʵÊÇ×¢²áÁËÒ»¸öautoRehydarte
reducer£¬»á½ÓÊÕǰÎÄpersistStore·½·¨·Ö·¢µÄrehydrate action£¬È»ºóºÏ²¢state¡£
µ±È»£¬autoRehydrate²»ÊDZØÐëµÄ£¬ÎÒÃÇ¿ÉÒÔ×Ô¶¨Òå»Ö¸´store·½Ê½£º
import
{REHYDRATE} from 'redux-persist/constants';
//...
case REHYDRATE:
const incoming = action.payload.reducer
if (incoming) {
return {
...state,
...incoming
}
}
return state; |
°æ±¾¸üÐÂ
ÐèҪעÒâµÄÊÇredux-persist¿âÒѾ·¢²¼µ½v5.x£¬¶ø±¾ÎĽéÉܵÄÒÔv5.xΪÀý£¬v4.x£¨https://github.com/rt2zz/redux-persist/tree/f4a6e86c66693a0bd5e6ea73043fd98b14f44a96£©²Î¿¼´Ë´¦£¬Ð°汾ÓÐһЩ¸üУ¬¿ÉÒÔÑ¡ÔñÐÔ¾ö¶¨Ê¹ÓÃÄĸö°æ±¾£¬ÏêϸÇëµã»÷²é¿´£¨https://github.com/rt2zz/redux-persist/releases£©¡£
³Ö¾Ã»¯ÓëImmutable
Ç°ÃæÒѾÌáµ½ReduxÓëImmutableµÄÕûºÏ£¬ÉÏÎÄʹÓõÄredux -persistĬÈÏÒ²Ö»ÄÜ´¦ÀíÔÉúJavaScript¶ÔÏóµÄredux
store state£¬ËùÒÔÐèÒªÍØÕ¹ÒÔ¼æÈÝImmutable¡£
redux-persist-immutable
ʹÓÃredux-persist-immutable£¨https://github.com/rt2zz/redux-persist-immutable£©¿â¿ÉÒÔºÜÈÝÒ×ʵÏÖ¼æÈÝ£¬Ëù×öµÄ½ö½öÊÇʹÓÃÆäÌṩµÄpersistStore·½·¨Ìæ»»redux-persistËùÌṩµÄ·½·¨£º
import
{ persistStore } from 'redux-persist-immutable'; |
transform
ÎÒÃÇÖªµÀ³Ö¾Ã»¯storeʱ£¬Õë¶ÔµÄ×îºÃÊÇÔÉúJavaScript¶ÔÏó£¬ÒòΪͨ³£Immutable½á¹¹Êý¾ÝÓкܶศÖúÐÅÏ¢£¬²»Ò×ÓÚ´æ´¢£¬ËùÒÔÐèÒª¶¨Òå³Ö¾Ã»¯¼°»Ö¸´Êý¾ÝʱµÄת»»²Ù×÷£º
import
R from 'ramda';
import Immutable, { Iterable } from 'immutable';
// change this Immutable object into a JS object
const convertToJs = (state) => state.toJS();
// optionally convert this object into a JS
object if it is Immutable
const fromImmutable = R.when(Iterable.isIterable,
convertToJs);
// convert this JS object into an Immutable
object
const toImmutable = (raw) => Immutable.fromJS(raw);
// the transform interface that redux-persist
is expecting
export default {
out: (state) => {
return toImmutable(state);
},
in: (raw) => {
return fromImmutable(raw);
}
}; |
ÈçÉÏ£¬Êä³ö¶ÔÏóÖеÄinºÍout·Ö±ð¶ÔÓ¦³Ö¾Ã»¯¼°»Ö¸´Êý¾ÝʱµÄת»»²Ù×÷£¬ÊµÏÖµÄÖ»ÊÇʹÓÃfromJS()ºÍtoJS()ת»»JsºÍImmutableÊý¾Ý½á¹¹£¬Ê¹Ó÷½Ê½ÈçÏ£º
import
immutablePersistenceTransform from '../services/ImmutablePersistenceTransform'
persistStore(store, {
transforms: [immutablePersistenceTransform]
}, startApp); |
ÔÚÏîÄ¿ÖÐÒýÈëImmutableÒÔºó£¬ÐèÒª¾¡Á¿±£Ö¤ÒÔϼ¸µã£º
1. redux storeÕû¸östateÊ÷µÄͳһImmutable»¯£»
2. redux³Ö¾Ã»¯¶ÔImmutableÊý¾ÝµÄ¼æÈÝ£»
3. React·ÓɼæÈÝImmutable£»
¹ØÓÚImmutable¼°Redux£¬ReselectµÈµÄʵ¼ù¿¼Ñé²é¿´Ö®Ç°Ð´µÄһƪÎÄÕ£ºImmutable.jsÓëReact,Redux¼°reselectµÄʵ¼ù£¨http://blog.codingplayboy.com/2017/09/14/immutable-react-redux/£©¡£
ImmutableÓëReact·ÓÉ
Ç°ÃæÁ½µãÒѾÔÚÇ°ÃæÁ½½Ú²ûÊö¹ý£¬µÚÈýµãreact-router¼æÈÝImmutable£¬Æäʵ¾ÍÊÇʹӦÓ÷ÓÉ״̬¼æÈÝImmutable£¬ÔÚReact·ÓÉÒ»½ÚÒѾ½éÉÜÈçºÎ½«React·ÓÉ״̬Á¬½ÓÖÁRedux
store£¬µ«ÊÇÈç¹ûÓ¦ÓÃʹÓÃÁËImmutable¿â£¬Ôò»¹ÐèÒª¶îÍâ´¦Àí£¬½«react-router stateת»»ÎªImmutable¸ñʽ£¬routeReducer²»ÄÜ´¦ÀíImmutable£¬ÎÒÃÇÐèÒª×Ô¶¨ÒåÒ»¸öеÄRouterReducer£º
import
Immutable from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
const initialState = Immutable.fromJS({
location: null
});
export default (state = initialState, action)
=> {
if (action.type === LOCATION_CHANGE) {
return state.set('location', action.payload);
}
return state;
}; |
½«Ä¬Èϳõʼ·ÓÉ״̬ת»»ÎªImmutable£¬²¢ÇÒ·Óɱä¸üʱʹÓÃImmutable API²Ù×÷state¡£
seamless-Immutable
µ±ÒýÈëImmutable.jsºó£¬¶ÔÓ¦ÓÃ״̬Êý¾Ý½á¹¹µÄʹÓÃAPI¾ÍµÃ×ñÑImmutable API£¬¶ø²»ÄÜÔÙʹÓÃÔÉúJavaScript¶ÔÏó£¬Êý×éµÈµÄ²Ù×÷APIÁË£¬ÖîÈ磬Êý×é½â¹¹£¨[a,
b] = [b, c]£©£¬¶ÔÏóÍØÕ¹·û£¨¡£©µÈ£¬´æÔÚһЩÎÊÌ⣺
1. ImmutableÊý¾Ý¸¨Öú½Úµã½Ï¶à£¬Êý¾Ý½Ï´ó£º
2. ±ØÐëʹÓÃImmutableÓï·¨£¬ºÍJavaScriptÓï·¨ÓвîÒ죬²»ÄܺܺõļæÈÝ£»
3. ºÍRedux£¬react-routerµÈJavaScript¿âдÐ×÷ʱ£¬ÐèÒªÒýÈë¶îÍâµÄ¼æÈÝ´¦Àí¿â£»
Õë¶ÔÕâЩÎÊÌ⣬ÉçÇøÓÐÁËseamless-immutable¿É¹©Ì滻ѡÔñ£º
1. ¸üÇ᣺Ïà¶ÔÓÚImmutable.jsseamless-immutable¿â¸üÇáС£»
2. Óï·¨£º¶ÔÏóºÍÊý×éµÄ²Ù×÷Óï·¨¸üÌù½üÔÉúJavaScript£»
3. ºÍÆäËûJavaScript¿âÐ×÷¸ü·½±ã£»
Òì²½ÈÎÎñÁ÷¹ÜÀí
×îºóÒª½éÉܵÄÄ£¿éÊÇÒì²½ÈÎÎñ¹ÜÀí£¬ÔÚÓ¦Óÿª·¢¹ý³ÌÖУ¬×îÖ÷ÒªµÄÒì²½ÈÎÎñ¾ÍÊÇÊý¾ÝHTTPÇëÇó£¬ËùÒÔÎÒÃǽ²Òì²½ÈÎÎñ¹ÜÀí£¬Ö÷Òª¹Ø×¢ÔÚÊý¾ÝHTTPÇëÇóµÄÁ÷³Ì¹ÜÀí¡£
axios
±¾ÏîÄ¿ÖÐʹÓÃaxios£¨https://github.com/axios/axios£©×÷ΪHTTPÇëÇó¿â£¬axiosÊÇÒ»¸öPromise¸ñʽµÄHTTP¿Í»§¶Ë£¬Ñ¡Ôñ´Ë¿âµÄÔÒòÖ÷ÒªÓÐÒÔϼ¸µã£º
1. ÄÜÔÚä¯ÀÀÆ÷·¢ÆðXMLHttpRequest£¬Ò²ÄÜÔÚnode.js¶Ë·¢ÆðHTTPÇëÇó£»
2. Ö§³ÖPromise£»
3. ÄÜÀ¹½ØÇëÇóºÍÏìÓ¦£»
4. ÄÜÈ¡ÏûÇëÇó£»
5. ×Ô¶¯×ª»»JSONÊý¾Ý£»
redux-saga
redux-sagaÊÇÒ»¸öÖÂÁ¦ÓÚʹӦÓÃÖÐÈçÊý¾Ý»ñÈ¡£¬±¾µØ»º´æ·ÃÎʵÈÒì²½ÈÎÎñÒ×ÓÚ¹ÜÀí£¬¸ßЧÔËÐУ¬±ãÓÚ²âÊÔ£¬ÄܸüºÃµÄ´¦ÀíÒì³£µÄÈý·½¿â¡£
Redux-sagaÊÇÒ»¸öreduxÖмä¼þ£¬Ëü¾ÍÏñÓ¦ÓÃÖÐÒ»¸öµ¥¶ÀµÄ½ø³Ì£¬Ö»¸ºÔð¹ÜÀíÒì²½ÈÎÎñ£¬Ëü¿ÉÒÔ½ÓÊÜÓ¦ÓÃÖ÷½ø³ÌµÄredux
actionÒÔ¾ö¶¨Æô¶¯£¬ÔÝÍ£»òÕßÊÇÈ¡Ïû½ø³ÌÈÎÎñ£¬ËüÒ²¿ÉÒÔ·ÃÎÊreduxÓ¦ÓÃstore state£¬È»ºó·Ö·¢action¡£
³õʼ»¯saga
redux-sagaÊÇÒ»¸öÖмä¼þ£¬ËùÒÔÊ×Ïȵ÷ÓÃcreateSagaMiddleware·½·¨´´½¨Öмä¼þ£¬È»ºóʹÓÃreduxµÄapplyMiddleware·½·¨ÆôÓÃÖмä¼þ£¬Ö®ºóʹÓÃcompose¸¨Öú·½·¨´«¸øcreateStore´´½¨store£¬×îºóµ÷ÓÃrun·½·¨Æô¶¯¸ùsaga£º
import
{ createStore, applyMiddleware, compose } from
'redux';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas/'
const sagaMiddleware = createSagaMiddleware({
sagaMonitor });
middleware.push(sagaMiddleware);
enhancers.push(applyMiddleware(...middleware));
const store = createStore(rootReducer, initialState,
compose(...enhancers));
// kick off root saga
sagaMiddleware.run(rootSaga); |
saga·ÖÁ÷
ÔÚÏîÄ¿ÖÐͨ³£»áÓкܶಢÁÐÄ£¿é£¬Ã¿¸öÄ£¿éµÄsagaÁ÷Ò²Ó¦¸ÃÊDz¢Áеģ¬ÐèÒªÒÔ¶à·ÖÖ§ÐÎʽ²¢ÁУ¬redux-sagaÌṩµÄfork·½·¨¾ÍÊÇÒÔпª·ÖÖ§µÄÐÎʽÆô¶¯µ±Ç°sagaÁ÷£º
import
{ fork, takeEvery } from 'redux-saga/effects'
import { HomeSaga } from './Home/flux.js'
import { AppSaga } from './Appflux.js'
const sagas = [
...AppSaga,
...HomeSaga
]
export default function * root() {
yield sagas.map(saga => fork(saga))
} |
ÈçÉÏ£¬Ê×ÏÈÊÕ¼¯ËùÓÐÄ£¿é¸ùsaga£¬È»ºó±éÀúÊý×飬Æô¶¯Ã¿Ò»¸ösagaÁ÷¸ùsaga¡£
sagaʵÀý
ÒÔAppSagaΪÀý£¬ÎÒÃÇÆÚÍûÔÚÓ¦ÓÃÆô¶¯Ê±¾Í·¢ÆðһЩÒì²½ÇëÇó£¬Èç»ñÈ¡ÎÄÕÂÁбíÊý¾Ý½«ÆäÌî³äÖÁredux
store£¬¶ø²»µÈ´ýʹÓÃÊý¾ÝµÄ×é¼þäÖȾÍê²Å¿ªÊ¼ÇëÇóÊý¾Ý£¬Ìá¸ßÏìÓ¦ËÙ¶È£º
const
REQUEST_POST_LIST = 'REQUEST_POST_LIST'
const RECEIVE_POST_LIST = 'RECEIVE_POST_LIST'
/**
* ÇëÇóÎÄÕÂÁбíActionCreator
* @param {object} payload
*/
function requestPostList (payload) {
return {
type: REQUEST_POST_LIST,
payload: payload
}
}
/**
* ½ÓÊÕÎÄÕÂÁбíActionCreator
* @param {*} payload
*/
function receivePostList (payload) {
return {
type: RECEIVE_POST_LIST,
payload: payload
}
}
/**
* ´¦ÀíÇëÇóÎÄÕÂÁбíSaga
* @param {*} payload ÇëÇó²ÎÊý¸ºÔØ
*/
function * getPostListSaga ({ payload }) {
const data = yield call(getPostList)
yield put(receivePostList(data))
}
// ¶¨ÒåAppSaga
export function * AppSaga (action) {
// ½ÓÊÕ×î½üÒ»´ÎÇëÇó£¬È»ºóµ÷ÓÃgetPostListSaga×ÓSaga
yield takeLatest(REQUEST_POST_LIST, getPostListSaga)
} |
1. takeLatest£ºÔÚAppSagaÄÚʹÓÃtakeLatest·½·¨¼àÌýREQUEST_POST_LISTaction£¬Èô¶Ìʱ¼äÄÚÁ¬Ðø·¢Æð¶à´Îaction£¬Ôò»áÈ¡ÏûÇ°ÃæÎ´ÏìÓ¦µÄaction£¬Ö»·¢Æð×îºóÒ»´Îaction£»
2. getPostListSaga×ÓSaga£ºµ±½ÓÊÕµ½¸Ãactionʱ£¬µ÷ÓÃgetPostListSaga£¬²¢½«payload´«µÝ¸øËü£¬getPostListSagaÊÇAppSagaµÄ×Ó¼¶Saga£¬ÔÚÀïÃæ´¦Àí¾ßÌåÒì²½ÈÎÎñ£»
3. getPostList£ºgetPostListSaga»áµ÷ÓÃgetPostList·½·¨£¬·¢ÆðÒì²½ÇëÇó£¬Äõ½ÏìÓ¦Êý¾Ýºó£¬µ÷ÓÃreceivePostList
ActionCreator£¬´´½¨²¢·Ö·¢action£¬È»ºóÓÉreducer´¦ÀíÏàÓ¦Âß¼£»
getPostList·½·¨ÄÚÈÝÈçÏ£º
/**
* ÇëÇóÎÄÕÂÁÐ±í·½·¨
* @param {*} payload ÇëÇó²ÎÊý
* eg: {
* page: Num,
* per_page: Num
* }
*/
function getPostList (payload) {
return fetch({
...API.getPostList,
data: payload
}).then(res => {
if (res) {
let data = formatPostListData(res.data)
return {
total: parseInt(res.headers['X-WP-Total'.toLowerCase()],
10),
totalPages: parseInt(res.headers['X-WP-TotalPages'.toLowerCase()],
10),
...data
}
}
})
} |
putÊÇredux-sagaÌṩµÄ¿É·Ö·¢action·½·¨£¬take£¬callµÈ¶¼ÊÇredux-sagaÌṩµÄAPI£¬¸ü¶àÄÚÈݲ鿴APIÎĵµ£¨https://redux-saga.js.org/docs/api/£©¡£
Ö®ºó±ã¿ÉÒÔÔÚÏîĿ·Óɸù×é¼þ×¢ÈëActionCreator£¬´´½¨action£¬È»ºósaga¾Í»á½ÓÊÕ½øÐд¦ÀíÁË¡£
sagaÓëReactotron
Ç°ÃæÒѾÅäÖúÿÉÒÔʹÓÃReactotron²¶»ñÓ¦ÓÃËùÓÐreduxºÍaction£¬¶øredux-sagaÊÇÒ»ÀàreduxÖмä¼þ£¬ËùÒÔ²¶»ñsagasÐèÒª¶îÍâÅäÖ㬴´½¨storeʱ£¬ÔÚsagaÖмä¼þÄÚÌí¼ÓsagaMonitor·þÎñ£¬¼àÌýsaga:
const
sagaMonitor = Config.useReactotron ? console.tron.createSagaMonitor()
: null;
const sagaMiddleware = createSagaMiddleware({
sagaMonitor });
middleware.push(sagaMiddleware); |
×ܽá
±¾ÎĽÏÏêϸµÄ×ܽáÁ˸öÈË´Ó0µ½1´î½¨Ò»¸öÏîÄ¿¼Ü¹¹µÄ¹ý³Ì£¬¶ÔReact£¬ ReduxÓ¦ÓúÍÏîÄ¿¹¤³Ìʵ¼ù¶¼ÓÐÁ˸üÉîµÄÀí½â¼°Ë¼¿¼£¬ÔÚ´óǰ¶Ë³É³¤Ö®Â·¼ÌÐøíÆíÂǰÐС£ |