React IMVC

2,279 阅读4分钟

IMVC(同构 MVC)的前端实践 -- React-IMVC 文档 -- react-imvc 源码

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 组件