create app
1. 同构理念
- 服务端和客户端进行 url 输入,router 解析 url 匹配对应的 mvc 组件
- 模块加载器加载组件,然后初始化 Controller
- 调用 Controller.init 方法,返回 view 实例
- 调用 view-engine 将 view 的实例根据环境渲染成 html 或者 native-ui 等
2. 配置理念
- 加载 controller 模块的方式
- server 端为同步加载 commonjsLoader
- 客服端是是异步加载使用 webpackLoader
- view-engine
- 在客户端被配置为ReactDOM
- 在服务端被配置为ReactDOMServer
3. 目录结构
Create-app 的目录结构,每个页面都是单独的文件夹包含Controller,Model,View. 整个项目页面使用routers路由串联起来,采用整站SPA的模式。 全局只有一个入口文件index.js
├─ publish // 源码 build 的目标静态文件夹
├─ src // 源代码目录
│ ├─ components // 全局共享组件
│ ├─ pages // 页面目录
│ │ ├─ home // 具体页面
│ │ │ ├─ controller.js// 控制器
│ │ │ ├─ model.js // 模型
│ │ │ └─ view.js // 视图
│ │ ├─ * // 其他页面
│ ├─ shared // 全局共享文件
│ │ └─ BaseController // 继承基类 Controller 的项目层 Controller
│ │ └─ sharedActions // 项目共享 actions
│ │ └─ sharedInitialState // 项目共享 state
│ ├─ index.js // 路由配置
4. IMVC 的具体工程实施
- node.js 运行时,npm 包管理
- expressjs 服务端框架babel 编译ES2015+ 代码到 ES5
- webpack 打包和压缩源码
- standard.js 检查代码规范
- prettier.js + git-hook 代码自动美化排版
- mocha 单元测试
IMVC 分别指什么
Model 是模型,指状态及其状态变化函数的集合,包含 initialState 状态(immutable data)和 actions 函数(pure function),不建议包含 side effect 副作用
View 是视图,指React 组件(尽可能使用 functional stateless component),不建议包含 side effect 副作用
Controller 是控制器,包含生命周期方法、事件处理器、localStorage 处理、同构工具方法以及负责同步 View 和 Model 的中间媒介, 承担 side effect 的对象
Controller
Controller Property
- controller.location -> object
- controller.history -> object
- controller.context -> object
- controller.View -> React Component
- controller.View 属性,应该是一个 React Component 组件。该组件的 props 结构如下
- props.state 是 controller.store.getState() 里的 global state 状态树
- props.handlers 是 controller 实例里,以 handleXXX 形式定义的事件处理器的集合对象
- props.actions 是 controller.store.actions 里的 actions 集合对象
- controller.Model -> object -> { initialState, ...actions }
- controller.Model 属性,是一个对象,除了 initialState 属性之外,其余属性都是 pure function
- Model 属性将被用来创建 controller.store, store = createStore(actions, initialState)
- 创建 store 使用的是 redux-like 的库 relite
- controller.store -> object
- 由 controller.Model 创建出来的 store,内部用的是 relite
- store 里的 global state,默认数据有几个来源
- controller.initialState 或 controller.Model.initialState
- react-imvc 会把 context 里的 { basename, publicPath, restapi, isClient, isServer } 对象填充进 state
- react-imvc 会把 controller.location 对象填充至 state.location 里。
- controller.handlers -> object
- controller.handlers 是在初始化时,从 controller 的实例里收集的以 handle 开头,以箭头函数形式定义的方法的集合对象。用来传递给 controller.View 组件
Controller API
- controller.fetch(url=string, options=object)
- controller.get(url=string, params=object, options=object)
- controller.post(url=string, data=object, options=object)
- controller.getCookie(key=string)
- controller.refreshView(ReactElement)
Controller Life Cycle Method
- getInitialState
- shouldComponentCreate
- componentWillCreate
- componentDidFirstMount
- componentDidMount
- pageWillLeave
- componentWillUnmount
- pageDidBack
- windowWillUnload
Event handler
react-imvc 建议除了把 state 从 component 里抽离出来,组成 global state 以外,也应该把 event handler 从 component 里抽离出来,写在 controller 里面,组成 global handlers 传入 View 组件内。
event handler 必须是 arrow function 箭头函数的语法,这样可以做到内部的 this 值永远指向 controller 实例,不需要 bind this,在 view 组件里直接使用即可。
Useful Components
react-imvc 有一些内置的 React Component,可以便利地实现某些特定需求
- Link 组件,可以用来实现页面的单页路由跳转效果
- Script 组件,用来防范 querystring 的 XSS 风险,放置 window.__INITIAL_STATE 里执行恶意代码
- Prefetch 组件,可以预加载特定页面的 js bundle 文件
- Style 组件,用来将 controller.preload 里配置的 css,展示在页面上
- Input 组件,用来将表单跟 store 联系起来
Hooks Api
- useCtrl 在 react 组件里获取到当前 controller 的实例
- useModel 在 react 组件里获取到当前 model 对应的 state 状态和 actions 行为
- useModelState 在 react 组件里获取倒当前 model 对应的 state 状态
- useModelActions 在 react 组件里获取到当前 store 里的 actions 对象
IMVC Configuration
- Title Keywords Description 配置
- imvc.config.js 配置
- Config Babel
- Config Webpack
High Order Component
connect(selector)(ReactComponent)
connect 是一个高阶函数,第一次调用时接受 selector 函数作为参数,返回 withData 函数。
withData 函数接受一个 React 组件作为参数,返回新的 React 组件。withData 会将 selector 函数返回的数据,作为 props 传入新的 React 组件。
selector({ state, handlers, actions }) 函数将得到一个 data 参数,其中包含三个字段 state, handlers, acitons,分别对应 controller 里的 global state, global handlers 和 actions 对象。
Error Handling
- errorDidCatch(error, type)
- getComponentFallback(displayName, Component)
- getViewFallback()
- ErrorBoundary 组件