@symph/react v2.0.0-canary.6
@symph/core
@symph/core
是一个 React 应用的轻量框架,基于 redux,简化了 redux 的使用及其复杂的概念,采用 MVC 的思想使代码和应用结构更加清晰,从而可以轻松高效的开发。
该框架只提供 MVC 组件支持,并不包含路由和构建相关的模块,这样可以更方便的集成到其它框架中(create-react-app、react-native 等)。如果你想要一个全栈的高可用框架,来帮你解决各种技术细节,快速的进入业务开发,请关注 @symph/joy
项目,它基于本项目,为开发提供更全面的项目能力。
安装
yarn add @symph/core
或者
npm install --save @symph/core
例子
with-create-react-app 使用create-react-app创建空白工程,并集成
@symph/core
初始化框架
import React, { Component } from "react";
import { create } from "@symph/core";
import { Provider } from "@symph/core/provider";
// 创建框架实例,然后就可以使用Controller和Model组件了
const app = create(
{
initialState: {},
},
{
initialReducer: {},
setupMiddlewares: (middlewares) => {
return middlewares;
},
}
);
// 启动框架
app.start();
// 在React绑定
class App extends Component {
render() {
return (
<Provider app={app}>
<div> you app content </div>
</Provider>
);
}
}
export default App;
创建 MVC 组件
- Model: 管理应用的行为和数据,普通 class 类,有初始状态,业务运行中更新 model 状态
- View: 展示数据,继承 React.Component
- Controller: 控制 View 的展示,绑定 Model 数据到 View,响应用户的操作,调用 Model 中的业务, 继承于 React.Component
组件间工作流程图
图中蓝色的箭头表示数据流的方向,红色箭头表示控制流的方向,他们都是单向流,store 中的state
对象是不可修改其内部值的,状态发生改变后,都会生成一个新的 state 对象,且只将有变化的部分更新到界面上,这和redux的工作流程是一致的。
这里只是对 redux 进行 MVC 层面的封装,并未添加新的技术,依然可以使用 redux 的原生接口,如果想深入了解 redux,请阅读其详细文档:redux
创建 Model
Model 管理应用的行为和数据,Model 拥有初始状态initState
和更新状态的方法setState(nextState)
,这和 Component 的 state 概念类似,业务在执行的过程中,不断更新state
,当state
发生改变时,和state
绑定的 View 也会动态的更新。这里并没有什么魔法和创造新的东西,只是将 redux 的action
、actionCreator
、reducer
、thunk
、saga
等复杂概念简化为业务方法和业务数据两个概念,让我们更专注于业务实现,代码也更简洁.
创建一个计数器 Model,计数器默认数值为 0,还有一个增加计数的方法。
// models/CalculateModel.js
import model from "@symph/core/model";
@model()
export default class CalculateModel {
//model的唯一标识,通过该名称来访问model中的状态数据
namespace = "calculate";
//初始状态数据
initState = {
counter: 0,
};
async add({ number }) {
let { counter } = this.getState();
counter += number;
// 更新model中的状态
this.setState({
counter,
});
}
}
我们使用@model()
将一个类声明为 Model 类,Model 类在实例化的时候会添加getState
、setState
,dispatch
等快捷方法。
Model API
namespace
model 将会被注册到 redux store 中,由 store 统一管理 model 的状态,使用store.getState()[namespace]
来访问对应 model 的 state, store 中不能存在两个相同的namespace
的 model。
initState
设置 model 的初始化状态,由于model.state
可能会被多个async
业务方法同时操作,所以为了保证 state 的有效性,请在需要使用 state 时使用getState()
来获取当前 state 的最新值,并使用setState(nextState)
方法更新当前的 state。
setState(nextState)
setState(nextState)
更新 model 的状态,nextState
是当前 state 的一个子集,系统将使用浅拷贝的方式合并当前的状态。
getState()
getState()
获取当前 model 的状态。
getStoreState()
getStoreState()
获取当前整个 store 的状态。
dispatch(action)
返回值:Promise,被调用业务的返回值。
在 model 中使用await this.dispatch(action)
调用其它业务方法,这和 redux 的store.dispatch(action)
的使用一样,由系统分发action
到指定的 model 业务方法中, action.type
的格式为modelNamespace/serviceFunction
。
如果是调用 model 自身的业务方法,可以使用await this.otherService({option})
的方式,this
指的是 model 本身。
业务方法
async service(action)
业务方法是async
函数,内部支持await
指令调用其它异步方法。在 controller 或者其他 model 中通过dispatch(action)
方法调用业务方法并获得其返回值。
Dva Model
兼容 dva 风格的 model 对象,使用方法:Dva Concepts ;
创建 Controller
Controller 需要申明其依赖哪些 Model,以及绑定 Model 的中的数据,和调用 Model 中的业务方法。它是一个 React 组件,可以像其它 React 组件一样创建和使用。
下面创建一个计数器 Controller,展示 Model 中存储的统计值,以及调用 Model 中的方法来修改统计值。
// models/CalculateController.js
import React, { Component } from "react";
import { controller, requireModel } from "@symph/core/controller";
import CalculateModel from "../models/CalculateModel";
@requireModel(CalculateModel)
@controller((state) => {
// 绑定calculateModel中的状态到当前组件
return {
counter: state.calculate.counter, // bind model's state to props
};
})
export default class CalculateController extends Component {
add = async () => {
let { dispatch } = this.props;
// 调用calculateModel中的业务方法
await dispatch({
type: "calculate/add",
number: 1,
});
};
render() {
let { counter } = this.props;
return (
<div>
<div>counter: {counter}</div>
<button onClick={this.add}>add 1</button>
</div>
);
}
}
创建和使用 Controller 的步骤:
使用
@controller(mapStateToProps)
装饰器将一个普通的 Component 声明为一个 Controller,参数mapStateToProps
实现 model 状态和组件 props 属性绑定,当 model 的 state 发生改变时,会触发组件使用新数据重新渲染界面。使用
@requireModel(ModelClass)
注册 controller 需要依赖的 model,这样可以将 controller 依赖的 model 打包到一个 thunk 中,只有在 controller 运行时,才会去加载依赖的 model,通常只需要在第一次使用到 model 的时候加载一次即可,无需重复注册。每个 controller 的
props
都会被注入一个 redux 的dispatch
方法,dispatch
方法是 controller 调用 model 的唯一途径,该方法的返回值是业务方法的返回值(Promise 对象),这和 redux 的 dispatch 方法有差别。
如果项目的 babel 配置还不支持@
装饰器语法,请使用函数调用的方式来声明 Controller,例如:
// models/CalculateController.js
import React, { Component } from "react";
import { controller, requireModel } from "@symph/core/controller";
import CalculateModel from "../models/CalculateModel";
class CalculateController extends Component {
add = async () => {
let { dispatch } = this.props;
// 调用calculateModel中的业务方法
await dispatch({
type: "calculate/add",
number: 1,
});
};
render() {
let { counter } = this.props;
return (
<div>
<div>counter: {counter}</div>
<button onClick={this.add}>add 1</button>
</div>
);
}
}
const Controller = controller((state) => {
// 绑定calculateModel中的状态到当前组件
return {
counter: state.calculate.counter, // bind model's state to props
};
})(CalculateController);
const ModelBound = requireModel(CalculateModel)(Controller);
export default ModelBound;
创建 View
View 是一个普通的 React 组件,其只负责界面展示,展示的数据来自父组件,通过this.props
属性读取。
import React, { Component } from "react";
class TextView extends Component {
render() {
let { message } = this.props;
return <div>{message}</div>;
}
}
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago