2.0.0-canary.6 • Published 2 years ago

@symph/core v2.0.0-canary.6

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

@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

组件间工作流程图

app work flow

图中蓝色的箭头表示数据流的方向,红色箭头表示控制流的方向,他们都是单向流,store 中的state对象是不可修改其内部值的,状态发生改变后,都会生成一个新的 state 对象,且只将有变化的部分更新到界面上,这和redux的工作流程是一致的。

这里只是对 redux 进行 MVC 层面的封装,并未添加新的技术,依然可以使用 redux 的原生接口,如果想深入了解 redux,请阅读其详细文档:redux

创建 Model

Model 管理应用的行为和数据,Model 拥有初始状态initState和更新状态的方法setState(nextState),这和 Component 的 state 概念类似,业务在执行的过程中,不断更新state,当state发生改变时,和state绑定的 View 也会动态的更新。这里并没有什么魔法和创造新的东西,只是将 redux 的actionactionCreator、reducerthunksaga等复杂概念简化为业务方法和业务数据两个概念,让我们更专注于业务实现,代码也更简洁.

创建一个计数器 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 类在实例化的时候会添加getStatesetStatedispatch等快捷方法。

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>;
  }
}