模块化应用要点
开始新项目之前,需考虑:
⓵ 代码文件的组织结构
⓶ 确定模块边界
⓷ store的状态树设计
案例介绍: TODO应用
代码文件的组织方式
1. 按角色组织
├── src
│── controller
│ ├── todoController.js
│ ├── filterController.js
├── models
│ ├── todoModel.js
│ ├── filterModel.js
├── node_modules
├── views
│ ├── todo.js
│ ├── todoItem.js
│ ├── filter.js
├── app.js
mvc划分为 controller、 Model、 View,代表三种模块角色,这种组织代码的方式为“按角色组织”–>衍生适合 redux 应用的构建中,则变为如下目录结构:
├── src
│── reducers
│ ├── todoReducer.js
│ ├── filterReducer.js
├── actions
│ ├── todoActions.js
│ ├── filterActions.js
├── node_modules
├── components //无状态傻瓜组件
│ ├── todoList.js
│ ├── todoItem.js
│ ├── filter.js
├── containers //内容组件
│ ├── todoListContainer.js
│ ├── todoItemContainer.js
│ ├── filterContainer.js
├── app.js
➪ but 并不是很好的划分方式, 按需求修改。
2. 按功能组织
todoList/
actions.js
actionTypes.js
index.js
reducer.js
views/
cornponent.js
container.js
filter/
actions.js
actionTypes.js
index.js
reducer.js
views/
cornponent.js
container.js
这个应用有2个功能: todoList 和 filter, 每个目录下都包含
➪ actionTypes.js 定义 action 类型;
➪ actions.js 定义 action 构造函数,决定了这个功能模块可以接受的动作;
➪ reducer.js 定义这个功能模块如何相应 actions.js 中定义的动作;
➪ views 目录,包含这个功能模块中所有的 React 组件,包括傻瓜组件和容器组件;
➪ index.js 这个文件把所有的角色导人,然后统一导出 。
redux应用建议使用此方式划分目录结构。
状态树的设计
状态树设计需遵循以下几个原则:
➪ 一个模块控制一个状态节点
➪ 避免冗余数据
➪ 树形结构扁平
TODO应用
1. 状态树设计:
{
todos: [
{
text: 'First todo',
completed: false,
id: 0
},
{
text: 'First todo',
completed: false,
id: 1
}
],
filter: 'all'
}
2. action 构造函数
actionTypes.js 和 acitons.js ;
todos/actionTypes.js : 定义 todos 支持的 action 类型。 添加, 状态更改, 删除三种 aciton 类型
export const ADD_TODO = 'TODO/ADD';
export const TOGGLE_TODO = 'TODO/TOGGLE';
export const REMOVE_TODO = 'TODO/REMOVE';
为避免命名冲突,加入特定唯一的前缀 TODO/
和 FILTER/
todos/acitons.js : 构造函数:
import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes';
let nextTodoId = 0;
export const addTodo = (text) => ({
type: ADD_TODO,
completed: false,
id: nextTodoId ++,
text: text
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
id: id
});
export const removeTodo = (id) => ({
type: REMOVE_TODO,
id: id
});
filter/actionTypes.js :
export const SET_FILTER = 'FILTER/SET';
filter/actions.js :
import {SET_FILTER} from './actionTypes';
export const setFilter = filterType => ({
type: SET_FILTER,
filter: filterType
});
3. 组合 reducer
每个功能目录下都有一个 reducer , 但是 createStore 只接受一个 reducer, => 在 store.js 中使用 combineReducers 方法将多个 reducer 组合起来。
store.js :
import {createStore, combineReducers} from 'redux';
import {reducer as todoReducer} from './todos';
import {reducer as filterReducer} from './filter';
const reducer = combineReducers({
todos: todoReducer,
filter: filterReducer
});
export default createStore(reducer);
todos/reducer.js :
import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes';
export default (state = [], action) => {
switch (action.type) {
case ADD_TODO :
return [
{
id: action.id,
text: action.text,
completed: false
},
...state
];
case TOGGLE_TODO :
return state.map((todoItem) => {
if (todoItem.id === action.id) {
return {...todoItem, completed: !todoItem.completed};
} else {
return todoItem;
}
});
case REMOVE_TODO :
return state.filter((todoItem) => {
return todoItem.id !== action.id;
})
default :
return state;
}
}
filter/reducer.js :
import {SET_FILTER} from './actionTypes';
import {FilterTypes} from '../constants';
export default (state = FilterTypes.ALL, action) => {
switch(action.type) {
case SET_FILTER : {
return action.filter;
}
default:
return state;
}
}
4. 视图
todos/views/todo.js : 作为 todos 模块的view—>容器组件 包含添加 todo 组件和 todolist 组件
todos/views/addTodo.js : 添加 todo 组件
todos/views/todolist.js : todoList 组件, 容器组件—>包含 todoItem 组件
todos/views/todoItem.js : todoItem 组件
filter/views/filter.js : 作为 filter 模块的view—>容器组件 包含 link 筛选组件
filter/views/link.js : 筛选组件
5. 样式
各模块下的视图下的style.css
todo 示例: