简单说明一下代码文件结构背后的想法。
一、模块的概念
App
一个模块可以简单理解为一个Web App中的子App.
- Module: main
- Module: grapher
- Module: work
- Module: user
每个模块应该负责自己的内部MVC,同时相互之间尽量独立,并通过预先定义的接口来交互/交换数据。
View
根据Redux的编程规范,所有React Component分两种:
- Presentational / Stateless / Dumb Component:说白了这种Component只负责展示,数据和回调都来自props,本身没有state. (所有名字里Dumb最短,就用这个名字吧)
- Container / Smart Component:负责交互Redux中的数据的Component。(惯例,用最短的名字:Smart)
然后从项目的角度继续细分,Dumb Component分为2种:
- 用于布局的Layout:项目专属。
- 可以复用的Web Component:高可复用性。
对于高可复用的Web Component,应该放在独立的类似arsenal包里,作为组件引入进来。
这样的话,Module就应该包含这些View内容:
- containers:Smart Component,供Route用。
- layouts:项目专属的Dumb Component.
Model
根据Redux的用法,这部分包含:
- Action:意图,由Redux-thunk扩展之后的版本。
- Reducer:这里要遵守pure function的概念。
这样,Module就应该包含这些Model内容:
- actions:存放所有的actions常亮和action creators.
- reducers:存放所有reducers.
注:
这里有个建议:reducer在Module中或多或少,多的时候创建文件夹reducers,少的时候直接一个reducers.js文件,这里都用复数格式。作为export的情况,则用单数。
综合起来
综合以上的说明,View和Model部分在Module中区分好,还要暴露特定的exports给App。
所以Module目录下增加2个文件:
- route.js:这个文件引用所有给route用的Component(smart or dumb),组建局部Route,并输出这个Route.
- reducer.js:这个文件combine所有本Module中的Reducers,然后输出。
- 也许会有的interface.js:这个作为api的出口。
注:
- 由于route中有需要在跳转前进行init或auth,因此export routeFactory 而不是单纯输出route. 这个factory接收一个store参数,用于在init中发dispatch,或是auth中getState( ) 做逻辑判断。
- 关于interface.js:有时候要去校验用户是否登陆,如果直接用store.getState( )的话,要依赖于用户reducer的挂载点。把mount变量从user这个Module里引过来,不如直接让user提供API来调用。不过这个怎么用,还要结合Smart的生成研究一下。
结论
各Module按照功能区分开之后,App作为整个应用的入口(index.js),就要负责把Model和View组合起来,所以项目源码目录下放这两个文件:
- index.js:App入口,负责引入store,组装各种东西,包括Model的Route.
- store.js:项目的store(Redux就一个store),负责import所有module下的reducer.
然后得到项目的结构如下:
- index.js:App入口
- store.js:项目的store
- main/:Module main
- route.js:export View / Route
- containers/:Module下的Smart Components
- layouts/:项目专属Dumb Components
- reducer.js:export Model / Reducer
- reducers/:项目reducers
- actions/:项目actions
- grapher/:Module grapher
- work/:Module work
- user/:Module user