2.1.4 • Published 5 years ago

beatle v2.1.4

Weekly downloads
6
License
MIT
Repository
-
Last release
5 years ago

Beatle · GitLab license

Beatle是一套轻量级前端框架,借助React、Redux实现应用界面构建流程。

特性

  1. 简单化Api,快速掌握开发技巧,只需掌握React框架。
  2. 轻量概念,React-like-Model 构建数据模型(state存储数据状态,setState变更数据状态)
  3. MVVM实现VM模块自动化,即自动化绑定数据和视图逻辑。
  4. 应用中间件,数据通信过程的设置中间件,方便应用接入外部扩展。
  5. 由简入繁,多应用嵌套构建复杂应用,方便管理应用之间的通用服务通信 以及 数据通信。

启动和打包

  // 启动
  honeypack start

  // 打包
  honeypack build -c webpack.build.js

Beatle-API

Class: Beatle

  • Beatle支持多应用场景,每个应用都需要通过New Beatle来生成实例。
  const app = new Beatle(options);
  app.run(options);

以下出现的 app 皆为 Beatle 的实例, 在初始化传入的配置options:

属性描述默认
name String应用实例名N/A
store Object应用数据中心的初始化数据{}
middlewares Array应用数据处理中间件,通过中间件可以变更数据结果[]
ajax Object应用接口请求对象初始化依赖的配置项{}
root DOM应用唯一挂载的DOM树节点document.body
base String应用启动,路由访问的根路径/
query Object设置路由全局参数N/A
autoLoadModel Boolean是否自动加载数据模型,如果开启则加载assets/auto_models.js文件true
autoLoadRoute Boolean是否自动加载路由,如果开启则加载assets/auto_routes.js文件false
models Object︱Function︱Context需要注册的数据模型N/A
routes Object︱Function︱Context需要注册的路由N/A
routeType Boolean路由处理器类型,主要分为hash还是原生browserHistory, 参考:browserHistoryhashHistory
subApp Boolean是否为子应用false

应用实例app有相应的方法来完成应用构建,包括注册数据模型, 注册路由, 应用启动等。

Beatle.getApp(appName)

  • return <Beatle> 返回指定的Beatle实例

多应用场景下通过选择指定应用实例,从而完成单个应用的构建。

  class Root extends React.Component{
    render() {
      return <h5>Hello Worlld {this.props.location.query.appName}!</h5>
    }
  }
  // 创建应用A,输出Hello World A!
  const appA = new Beatle({
    name: 'appA',
    ...
  });
  appA.route('/', Root, {query: {appName: 'A'}});
  appA.run();
  // 应用B,处处Hello Wolrd B!
  const appB = new Beatle({
    name: 'appB',
    ...
  });
  appB.route('/', Root, {query: {appName: 'B'}});
  appB.run();

  // 通过指定应用名来获取应用A实例
  Beatle.getApp('appA');

Beatle.createModel(model, resource)

  • model <Model> 需要组合的数据模型
  • resource <Resource> 组合需要的接口封装对象
  • return <Model> 返回组合好的Model

在Beatle中数据模型Model是指一类数据的集合,一个数据模型包含了数据基础结构, 改变数据的行为方法 以及跨数据模型的监听

resource是接口调用的封装对象,一般来说,我们会愿意把接口单独定义到业务逻辑之外的对象中。

  • 来了解一下在Beatle中应用Model
  // 1. 定义一个数据模型
  const model = {
    // 定义数据模型实例名(注意,以下通过实例名来获取到Beatle生成的model的实例
    displayName: 'test',
    // 定义数据结构
    state: {
      value: 1
    },
    // 定义数据行为
    actions: {
      // model注册后,set会变成一个方法,出入的参数,会写到paylaod.arguments属性。
      set: (nextState, payload) => {
        // nextState是state的最新版本, set定义第一个参数值,赋值给value属性
        nextState.value = payload.arguments[0];
        return nextState;
      }
    }
  }
  // 2. 初始化Beatle应用
  const app = new Beatle({name: 'main'});
  // 3. 注册model
  app.model(model);
  // 获取model实例
  const modelInst = app.model('test')
  // 打印输出 value: 1
  console.log('value :' + modelInst.state.value); 
  // 调用行为,写入数据
  modelInst.set(2);
  // 打印输出 value: 2
  console.log('value :' + modelInst.state.value);
  // 4. 定义组件,并挂在路由
  class Root extends React.Component{
    render() {
      return <span>component props value :{this.props.test.value}</span>
    }
  }
  // 5. 把组件和数据模型进行绑定
  const ConnectRoot = Beatle.connect(['test'], Root);
  // 路由响应时,组件输出 component props value :2
  app.route('/', ConnectRoot);
  // 调用行为,写入数据
  modelInst.set(3);
  // 组件自动更新,并输出 component props value :3

  // 运行应用
  app.run();
  • 数据模型可以通过class来创建
// 1. 定义一个数据模型
  class Model extends Beatle.BaseModel {
    static displayName: 'test';
    state = {
      value: 1
    }
    set(v) {
      // this.setState(obj, ...args), 通过底部传入参数,通过payload.argumnets获取
      return this.setState({
        value: (nextState, payload) => {
          return payload.argumens[0];
        }
      }, v);
    }
  }
  • model的数据行为异步时
  // 1. 通过纯对象创建
  const model = {
    displayName: 'test',
    state: {
      value: 1
    },
    actions: {
      // set: callback 改为 set: {callback}, 异步行为包括数据预处理,都需要通过exec属性来返回。
      set: {
        // 通过exec返回的数据(获取promise,其接受的数据)。通过payload.data获取
        exec: (v) => {
          return new Promise(resolve => {
            setTimeout(() => {
              resolve(v)
            }, 100);
          })
        },
        callback: (nextState, payload) => {
          nextState.value = payload.data;
          return nextState;
        }
      }
    }
  }
  // 2. 通过class创建
  class Model extends Beatle.BaseModel {
    static displayName: 'test';
    state = {
      value: 1
    }
    set(v) {
      return this.setState({
        // value: callback 改为 value: {callback}
        value: {
          // exec: () => promise 可以改为exec: promise, exec返回的数据,通过payload.data获取
          exec: (v) => {
            return new Promise(resolve => {
              setTimeout(() => {
                resolve(v)
              }, 100);
            })
          },
          callback: (nextState, payload) => {
            // 单一职责,返回的数据,只会更新value值
            return payload.data;
          }
        }
      }, v);
    }
  }
  • 数据模型的异步行为通过接口获取数据
  class Model extends Beatle.BaseModel {
    static displayName: 'test';
    state = {
      value: 1
    }
    set(v) {
      return this.setState({
        value: {
          // !!注意,此处exec只是接口的配置,这是Beatle支持的一种特殊的exec形式,其内部会通过fetch来触发调用
          exec: {
            url: 'http://api.github.com', 
            method: 'get', 
            data: {id: v}
          }
          /**
           * 相同的方式,还有2种办法
           * 1. exec: fetch('http://api.github.com', v),
           * 2. exec: v => return fetch('http://api.github.com', {id: v});
           */
          callback: (nextState, payload) => {
            return payload.data;
          }
        }
      }, v);
    }
  }
  • 回到主题,createModel的目的就是抽象exec,独立维护在model之外
  const resource = {
    set: {
      url: 'http://api.github.com', 
      method: 'get', 
      data: {id: v}
    }
  }

  // 以下是装饰器用法,也可以通过 Model = Beatle.createMode(resource)(Model);
  @Beatle.createMode(resource)
  class Model extends Beatle.BaseModel {
    static displayName: 'test';
    state = {
      value: 1
    }
    set(v) {
      return this.setState({
        value: {
          // !!注意,exec为合并进来的Resource对应的属性,如果没有找到则当做exec不存在
          exec: 'set'
          callback: (nextState, payload) => {
            return payload.data;
          }
        }
      }, v);
      // 更简洁的写法, 通过value对应的函数名来指定exec
      /**
       * return this.setState({
       *  value: function set(nextState, payload){
       *    return payload.data;
       *  }
       * })
       */
    }
  }

这样下来,所有的接口都单独定义在resource对象下,对于大的应用会存在很多resource。对于resource我们可以在业务之外单独做调试,这样服务分层的管理,代码更加健壮和清晰。

Model的使用下面API有更详细介绍

Beatle的其他静态属性

属性描述
Ajax接口调用Ajax类,可单独初始化ajax实例
Poller轮询调用Poller类
Link封装了react-router的Link, 带上全局base和query
ReduxSeed数据驱动机制可单独使用,不依赖Beatle,包含完整的数据模型以及Redux处理一整套机制

下面我们来看下Beatle实例app有哪些方法和对象可以使用。

app.ajax

app是有new Beatle初始化的实例,在初始化同时时,内部还会初始化2个实例:ajaxseed, 分别为Ajax的实例 和 ReduxSeed的实例。

ajax可以设置实例级别的事件监听,分别通过以下方法来设置

方法参数类型描述
setHeader(headers)headers Object设置headers配置
beforeRequest(fn)fn Function请求之前beforeRequest的处理,此时可以更改接口配置或者更多
beforeResponse(fn)fn Function接口结果预处理beforeResponse
afterResponse(fn)fn Function接口结果后处理afterResponse
set(name, value)name String, value any前4个方法都可以通过set方法来设置,简化操作
  const app = new Beatle();

  // 在请求之前,监听事件处理
  app.ajax.beforeRequest(
    function (ajaxOptions) {
      // 场景1,更改接口请求配置
      ajaxOptions.data = {};
      // 场景2,直接返回mock数据,并中断后续接口请求(只要返回promise,就能中断请求)
      return Promise.resolve({
        // mock数据
      })
    }
  );

  app.ajax.beoreResponse(
    function(response, ajaxOptions, request){
      // 和afterResponse的区别在于,response拿到的是Response实例,还未对数据进行解析处理
      // 比如接口返回的是JSON的stringify数据,那么需要通过json方法进行解析
      // 具体以何种方法来解析数据,需要通过ajaxOptions的`dataType`属性来指定
      return response.json();
    }
  )

  // 请求成功,并解析response数据成功后进入
  app.ajax.afterResponse(
    function (result, ajaxOptions, request) {
      // 这里result是解析完成的数据
      if(result instanceof Error){
        // status不为200到299之间的报错均封装成Error实例
      }else if(result.code !== 'SUCCESS'){
        // 接口自身数据判断是否有问题
      }else{
        // 返回正确的数据
        return result.data;
      }
    }
  );

接口请求配置dataType用于声明如何解析接口数据

app.seed

seed实例是ReduxSeed实例,app.getStore()实际上是通过seed实例中获取store对象。

app实例开放API

方法参数类型描述
getStore() ObjectN/A获取redux状态容器
getRoutes() ArrayN/A获取react-router的路由配置
use(middleware)middleware Function注册中间件,中间件是在处理处理过程中变更数据结构或者做一些必要的监控
getResolvePath(routeConfig) StringrouteConfig Object根据路由配置获取真实的路径
route(path, component)path String︱Array︱Object︱Context, component ReactComponent只有一个参数,此时为字符串则查找路由配置,否则是批量注册路由配置;2个参数未显示注册单个路由配置
routesFactory(routes, option)routes Array︱Object︱Context, option Object批量注册路由,可以传入option做更多处理
model(Model)Model Object注册数据模型
connect(bindings, component, context, flattern)bindings String︱Object︱Array, component ReactComponent, context Object, flattern Boolean设置视图, binding指定注入数据模型或者根据数据模型注入数据和方法
service(providers, isGlobal)providers <Object|Function|Array>, isGlobal Boolean注册全局服务(通用js对象)
observer(obj)obj <Array|Promise|Observable>把数据转为观察序列
view(Selector, component, providers)Selector Object, component: ReactComponent, providers: Array<Object|Function|Array>设置视图,并注入context
run(rootDom, basePath)rootDom Object, basePath String启动应用

当app为Beatle的主应用时,可以通过Beatle.xxx直接调用app对应的方法。 所有app实例的开放api都可以通过Beatle进行访问

  const mainApp = new Beatle({});
  const subApp = new Beatle({subApp: true});

  // Beatle.run 等同于mainApp.run, 相同的还有`use`, `model`等

在new Beatle的配置项options中有subApp属性来声明是否为子应用,否则就是主应用。 在Beatle支持多应用的场景下,主应用必须只为一个,其他均为子应用,否则将会出现预想不到的问题。

app.getStore()

在new Beatle产生实例app时,应用内部会创建一个单一的数据共享对象,后面统一称之为状态容器store, 如果你熟悉Redux,当前store也可以在Redux技术体系下正常工作。

  const app = new Beatle();

  const store = app.getStore();
  store.substribe(function () {
    console.log('current state ==>', sotre.getState());
  });

app.model(model)

应用中注入数据模型Model,注册功能后,数据模型的将交给store进行托管。

  // 此时user还未注册到app中,所以将没有任何内容被绑定
  app.connect('user', ReactComponent);
  app.model({
    displayName: 'user',
    ...
  });
  // 此时绑定有效,user数据模型和组件建立了绑定,后续一旦这个user发生数据变更时,组件将自动调用render来更新视图。
  app.connect('user', ReactComponent);

app.connect(modelList, component, flattern)

  • modelList <String|Object|Array> 指定需要绑定的数据模型实例名
  • component <ReactComponent> 指定组件来绑定
  • flattern <boolean> 是否平铺属性
  • return <ReactComponent> 返回新的React组件
  import React from 'react';
  import Beatle from 'beatle';

  const app = new Beatle();

  const Model = {
    displayName: 'user',
    store: {
      nickname: 'anonymous'
    },
    actions: {
      login: {
        // 同步的action,直接从arguments中取值,arguments = ['Trump']
        callback: (nextStore, payload) => {
          nextStore.nickname = payload.arguments[0];
        }
      }
    }
  }

  class SayHello extends React.Component {
    componentDidMount() {
      this.props.user.login('Trump');
    }
    render() {
      return 'hello ' + this.props.user.nickname;
    }
  };

  app.model(Model);
  const component = app.connect(['user'], SayHello);
  app.route('/', component);
  app.run();
  // 访问/, console输出为:hello Trump!

app.service(providers, isGlobal)

  • providers <Function|Object|Array> 注入的全局的服务JS类
  • isGlobal <Boolean> 是否是全局的(跨所有应用)
  const app = new Beatle({name: 'main'});
  function A() {
    return {
      v: 1
    }
  };
  // B依赖于A,通过数组最后一位是服务定义,其他项为依赖的服务名
  const B = ['a', function(a) {
    return {
      v: a.v + 1
    }
  }];
  // B是其中的一种依赖方式,C是另外一种依赖方式,通过contextTypes属性声明
  class C{
    static contextTypes: {
      b: React.PropTypes.object.isRequired
    }
    get v() {
      return this.context.b.v + 1;
    }
  }
  
  function D(c) {
    return c.v + 1;
  }
  // D是另外一种依赖方式,通过$inject声明,是不是很熟悉,ng 1.x中服务依赖也是如此
  D.$inject = ['c'];

  // 注册服务A,通过displayName来指定服务名
  A.displayName = 'a';
  const a = app.service('a');
  // 输出 1
  console.log(a.v);
  // 注册服务, 通过key来指定服务实例名称
  app.service({
    b: B,
    c: C,
    d: D
  });
  // 通过名称获取服务实例
  const b = app.service('b');
  // 输出2, 因为b依赖于a + 1, B的依赖会从全局服务中找
  console.log(a.v);
  const d = app.service('d');
  // 输出4, 因为d依赖于c + 1, c依赖于b + 1, b依赖于a + 1
  console.log(d.v);

app.observer(obj)

  • obj <Array|Promise|Observable> 指定需要转为序列的数据
  • return <Observable> 返回可订阅序列
const stream = app.observer([1, 2, 3]);
stream.subscribe(v => {
  console.log(v + ', ');
});
// 输出 1, 2, 3

const stream = app.observer(Promise.resolve('123'));
stream.subscribe(v => {
  console.log(v);
});
// 输出 123

const promise = new Promise(resolve => {
  setTimeout(() => {
    resolve({name: 123});
  }, 1000);
});
// 针对react特殊定义,可以输出异步组件
ReactDOM.render( => (<div>Hi, {app.observer(promise).render(d => d.name)}</div>), document.body);

Observable序列是rxjs中的概念,适用于把异步数据按时间轴转换为有顺序的序列数据,方便操作。

app.view(Selector, component, providers)

  • Selector <Object> 指定需要绑定的数据选择器
  • component <ReactComponent> 指定组件来绑定
  • providers <Array<Object|Function|Array>> 注入其他的服务,使得组件通过this.context可以访问到。
  • return <ReactComponent> 返回新的React组件
  class UserModel extends Beatle.BaseModel {
    state = {
      profile: {
        name: 'Guest'
      }
    }
    login(name) {
      return this.setState({
        profile: {
          exec: fetch('https://api.github.com/users/' + name),
          // 每次都需要写callback,而很多callback的处理基本都是统一的,比如接口的CRUD的处理。
          // 以下等同于Beatlep.crud.get
          callback: (nextProps, payload) => {
            return payload.data;
          }
        }
      })
    }
  }

  class Selector extends Beatlep.BaseSelector{
    // inputs相当于connect的 stateMergeToProps, 相对应的outputs等同于connect的 actionMergeToProps
    get inputs() {
      return (state, dispatch) => {
        return {
          profile: state.user.profile
        }
      }
    }
    // 这是数据选择器的钩子函数,在组件初始化完成时自动触发。
    initialize() {
      this.getModel('user').login('baqian');
    }
  }

  class Root extends React.Component{
    static propTypes = {
      profile: React.Proptypes.object
    }

    static contextTypes = {
      test: React.Proptypes.object
    }

    render() {
      // 组件会先输出 Hello Guest!,接口调用成功后,更新为 Hello baqian
      return (<div>{this.context.test.title} {this.props.profile.login}!</div>)
    }
  }
  // 和Beatlep.connect不同,connect绑定数据模型,view绑定数据选择器
  Root = Beatlep.view(Selector, Root, {
    test: function() {
      return {
        title: 'Hello'
      }
    }
  });

数据选择器是一个新的概念,在复杂的场景,一个组件往往会调用多个model,通过数据选择器来统一管理model,提高代码可读性

  • Beatlep.crud

这是action数据处理的模板,以上的UserModel通过crud重新处理如下:

  class UserModel extends Beatle.BaseModel {
    state = {
      profile: {
        name: 'Guest'
      }
    }
    login(name) {
      return this.setState({
        profile: {
          exec: fetch('https://api.github.com/users/' + name),
          callback: Beatlep.crud.get
        }
      })
    }
  }

crud的全部接口

  crud = {
    item: {},
    itemsEntry: {
      data: [],
      loading: false,
      total: 0,
      pageSize: 10,
      page: 1
    },
    get,    // 获取数据
    create, // 新增
    update, // 更新
    query,  // 分页形式
    reset   // 恢复为初始化数据
  }

举个例子,通过crud创建一个UserModel,能节省大量代码

  class UserModel extends BaseModel {
    static displayName = 'user';

    state = {
      user: crud.item,
      usersEntry: crud.itemsEntry
    }
    // 必须有id属性,用来识别指定数据项,从而判断是更新还是创建
    id = 'id';

    get(id) {
      return this.setState({
        user: {
          exec, // exec是异步逻辑处理
          callback: crud.get
        }
      }, {id: id});
    }

    delete(id) {
      return this.setState({
        usersEntry: {
          exec,
          callback: crud.delete
        }
      }, {id: id});
    }

    update(user) {
      return this.setState({
        usersEntry: {
          exec,
          callback: crud.update
        }
      }, user);
    }

    create(user) {
      return this.setState({
        usersEntry: {
          exec,
          callback: crud.create
        }
      }, user);
    }

    query(params) {
      return this.setState({
        usersEntry: {
          exec,
          callback: crud.query
        }
      }, params);
    }
  }

app.route(path, routes)

  • path <String>, 当存在path时,则是配置单个路由,此时routes应该为React组件或者Beatle子应用。
  • routes <ReactComponent|Beatle|ReactRouter>, 不存在path时基于ReactRouter的路由的配置.
  • app.route(routes)
  // routes为标准的react-router的路由配置项
  app.routes([
    {
      path: '/',
      component: RootComponent,
      childRoues: [
        {
          path: 'profile',
          component: ProfileComponent
        }
      ]
    }, {
      path: '*',
      component: 404Component
    }
  ])
  • app.route(path, component)
  app.route('/', RootComponent);
  // 这种形式,如果想要配置childRoutes
  RootComponent.routeOptions = {
    childRoues: [
      {
        path: 'profile',
        component: ProfileComponent
      }
    ]
  }
  app.route('/', RootComponent);
  • app.route(path, subApp) 把子应用挂在主父级应用下,子应用的路由会继承下来,但需要追加根路径来访问。
  const subApp = new Beatle({subApp: true});
  subApp.route('/', subAppRootComponent);
  subApp.route('/profile', subAppProfileComponent);
  app.route('/subApp', subApp);
  app.run();
  // 访问/subApp/ 会触达subAppRootComponent视图
  // 访问/subApp/profile 会触达subAppProfileComponent视图

app.run(root, base)

  • root <DOM>, app最终需要挂载到真实的DOM节点下.
  • base <string>, app访问的路由,统一加上跟路由路径.
  const app = new Beatle();
  app.route('/', RootComponent);
  app.route('/profile', ProfileComponent);
  app.run(document.body, '/beatle');
  // 此时访问/不能匹配任何路由
  // 访问/beatle/ 会触达RootComponent视图
  // 访问/beatle/profile 会触达ProfileComponent视图

Model

Model数据模型是一类数据的集合,包含了数据的初始化结构,以及改变这些数据的行为方法。

描述一个Model对象,需要具备以下数据结构:

属性描述默认值
displayName实例名N/A
store数据基础结构{}
actions改变数据的行为方法N/A
subscriptions跨数据模型的行为监听N/A
  // 以React的propTypes概念来描述属性类型,方便我们来描述每个属性的类型
  const propTypes = React.PropTypes;
  const modelShape = {
    displayName: propTypes.string,
    store: propTypes.object.isRequired,
    actions: propTypes.object,
    subscriptions: propTypes.object
  }
  // 在actions中每个action的结构,分2种情况
  // 异步action
  const action = {
    exec: propTypes.oneOfType([propTypes.object, propTypes.func]),
    callback: propTypes.oneOfType([propTypes.object, propTypes.func]),
  }
  // 同步action
  const action = {
    callback: propTypes.func,
  }
  // subscriptions每个subscription必须为方法
  const subscriptions = {
    `${modelName}/${actionName}/${status}: (nextStore, playload) => {
    }
  }

符合以上数据结构的Model可以通过app.model(Model)注册到应用中。

何时使用Beatle.createModel(model, resource), 当你异步的action中exec需要单独维护到model外部时,通过Bealte.createModel组合进来,生成最终的Model

Model行为action的配置

属性参数类型描述
execObject/Function异步行为的触发条件,Beatle内部通过exec来识别异步行为,当exec为接口配置,会转为一个接口调用函数,如果是函数则不用做变动
callbackObject/Function行为触发成功后进入,在同步行为时,callback只能为函数,异步行为时callback一般来说是对象,有3个回调函数,start, successerror
reducerObject/Function同上callback
subscriptionsObject跨数据模型监听行为,从而变更自身数据
externalReducersObject同上subscriptions
  • 行为action调用成功的回调函数

行为调用的处理逻辑 1. 行为方法触发时传入的参数会放到payload.arguments中 2. 判断行为是否存在exec属性,则会当做异步行为进行调用 1. 触发行为时,会先执行start回调, 2. 判断exec为函数时,执行函数返回非promise值,会直接进入到success回调,否则在promise的接收值时进入到success回调,在拒绝值时进入到error回调 3. 如果exec为接口配置,则通过应用内部的ajax实例来发起接口调用,在接口成功并接收值时进入到success回调,在拒绝值时进入到error回调 3. 同步行为时直接进入到callback回调

  callback: function (nextStore, payload){
    // nextStore是当前model可变的数据,我们知道model的基础数据是在store中定义,每次更新后会存到内存中,并不会改变store属性
    // 所以每次行为调用后的回调nextStore拿到可变的数据
    // payload是数据装载对象,最常用的,arguments是行为调用时传入的参数,而data属性是异步行为调用时接收的数据。
  }
  • 数据装在对象payload的结构
属性描述
type当前行为处理状态,不同行为状态会进入到不同的回调中
store当前model的基础数据
arguments行为调用时传入的参数
data异步行为调用后,接收的数据
message当异步行为调用失败后,会存在错误信息
  • 基于异步action调用以及跨数据模型监听行为的实现举个例子:
  const UserModel = {
    displayName: 'user',
    store: {
      profile: {
        pending: true,
        nickname: 'anonymous'
      }
    },
    actions: {
      login: {
        callback: {
          start: (nextStore, payload) => {
            // 每次请求之前都重置为初始化值,通过payload.store可以获取到初始化值
            nextStore.profile = payload.store;
          },
          success: (nextStore, payload) => {
            // 获取成功后,接口返回值通过playload.data可以获取到
            nextStore.profile = {
              pending: false,
              nickname: payload.data.nickname
            };
          }
        }
      }
    }
  };
  const UserResource = {
    login: {
      url: '/login',
      method: 'GET'
    }
  }

  const AccountModel = {
    dispayName: 'account',
    store: {
      nickname: ''
    },
    subscriptions: {
      // nextStore为当前model的可变数据对象,user_login_payload是user实例的login行为调用成功后的payload
      'user.login.success': (nextStore, user_login_payload) => {
        nextStore.nickname = user_login_payload.data.nickname;
      }
    }
  }
  // 你会发现,UserResource存在相同行为名称的属性,值为接口调用配置。通过Beatle.createModel会组装到UserModel中
  app.model(Beatle.createModel(UserModel, UserResource));
  app.model(AccountModel);
  // 当UserModel的login行为触发调用,成功后,AccountModel的监听也会被触发,从而更新AccountModel的数据

Resource

Resource是接口配置对象,结合Beatle.createModel来使用

  • Beatle.createModel(model, resource)时其内部将做如下处理
  1. 遍历resource对象,拿到每个属性和值
  2. 在model.actions中找到对应属性的行为,把值赋给行为的exec对象,找不到行为则,则丢弃掉
const userModel = {
  ...
  actions: {
    login: {...}
  }
};

// resource/user.js
const userResource = {
  login: {
    url: '/login',
    method: 'GET',
    params: {
      username: 'default username'
    }
  },
  getUserList: {
    url: '/user/list',
    method: 'GET',
    params: {
      pageSize: 10,
      pageNo: 1
    }
  }
};

// getUserList不会生成行为,只有login会组合到login行为中
Beatle.createModel(userModel, userResource);

Class: ReduxSeed

  • 通过new ReduxSeed产生seed实例
  import {ReduxSeed} from 'beatle';

  const seed = new ReduxSeed({...});
  • new ReduxSeed传入options配置项:
属性描述默认
name StringReduxSeed支持多实例,初始化一个seed实例需要指定实例名称main
ajax Objectajax实例N/A
initialState Objectstore的基础结构{}
middlewares Array应用数据处理中间件,通过中间件可以变更数据结果[]
Models Object注册多个数据模型{}

ReduxSeed静态属性

名称参数类型描述
createModelmodel Object, resource Object组合resource到model中,等同于Beatle.createModel
getReduxname String获取指定的seed实例

seed实例方法

名称参数类型描述
reducerBuildermodel Object, resource Object组合resource到model中,等同于Beatle.createModel
registermodel Object, resource Object注册一个model到seed实例
getActionsmodelName String获取指定的seed实例下的model的行为,为空时获取所有行为

Class: Ajax

  • 通过new Ajax产生ajax实例,传入的options配置项,设置实例级的全局配置:
名称描述
headers全局的Header配置, 默认取值window.ajaxHeader
delimeter请求url默认支持插值替换,delimeter是插值变量的语法
normalize请求url插值替换,是否都走data属性, 默认为false
beforeRequest(ajaxOptions)请求之前的钩子函数
beforeResponse(response, ajaxOptions, xhr)请求成功后处理response对象的钩子函数
afterResponse(result, ajaxOptions, xhr)请求成功后处理接口结果数据的钩子函数
origin配置请求地址前缀
  • Ajax的全局配置分为2种:全局(所有实例有效) 和 实例级,支持的值如上
  import {Ajax} from 'Beatle';
  Ajax.headers = {
    csrfToken: '...'
  }
  Ajax.normalize = false;

  const ajax = new Ajax({
    normalize: true
  });
  // ajax发起请求时,normalize为`true`,而headers值为`{ csrfToken: '...' }`
  ajax.get('...');

  ajax.set('headers', {});
  // 此时再发请求,normalize为`true`,headers为`{}`
  ajax.get('...')

实例级设置可以参考ajax实例设置实例级别的全局监听

Ajax.request(ajaxOptions)

Ajax静态方法,其内部会初始化一个ajax实例,并调用ajax.request来执行

ajax实例方法

名称参数类型描述
requestoptions Object接口请求调用,所有其他方式的请求最终都会走request来执行
getpath String, data Object/null, options Object/Function, dataType String/Functionget请求
postpath String, data Object/null, options Object/Function, dataType String/Functionpost请求
putpath String, data Object/null, options Object/Function, dataType String/Functionput请求
deletepath String, data Object/null, options Object/Function, dataType String/Functiondelete请求
patchpath String, data Object/null, options Object/Function, dataType String/Functionpatch请求

ajax.request(options)

  • options <Object> 接口请求配置
  • return <Promise|null> 配置中有callback则不会返回内容,否则会返回调用的promise
  • 常用接口配置
属性参数类型描述
urlString请求地址
methodString请求方法
headersObject请求头部
modeString请求模式,参考 cors, no-cors, same-origin, 默认no-cors
credentialsString请求凭证, 参考omit, same-origin, include, 有凭证才带cookie,否则不带cookie
cacheString缓存模式,参考 default, reload, no-cache, 默认default
callbackFunction回调处理函数,当存在callback时不会返回promise实例
dataTypeString接口返回结果对数据解析处理基于dataType类型来决定,默认为json解析
  • dataType解析数据类型
取值描述
arrayBuffer解析为ArrayBuffer的promise对象
blob解析为Blob的promise对象, URL.createObjectURL(Blob)转为base64
formData解析为FormData的promise对象
json解析为Json的promise对象
text解析为USVString的promise对象

ajax.get(path, data, options, dataType)

  • path <String> 请求地址
  • data <Object|null> 请求参数
  • options <Object|Function> 当为函数式,则是callback回调,否则为请求配置信息
  • dataType <String|Function> 请求数据进行数据解析类型,默认是json解析, 当dataType为函数时,则是callback回调,此时options必须为请求配置信息
  • return <Promise|null> 配置中有callback则不会返回内容,否则会返回调用的promise

其他接口方法形式一致,包括postdeleteputpatch

Class: Poller

通过new Poller产生poller实例,传入配置项options:

属性描述默认
delay Number每次轮询需要等待是时长5000
smart Boolean智能识别,当某个请求超过等待时长,会等待请求结束后才会轮询下个动作false
action Function每个轮询动作触发时,调用action函数N/A
catchError Function轮询中每个动作调用失败时都会进入到错误回调N/A
  import Beatle, {Poller} from 'beatle';
  const poller = new Pooler({
    action: () => {
      // 每个5秒会轮询调用改函数
      return Beatle.Ajax.request({url: '', method: 'get'});
    }
  });
  // 当subscribe订阅或者start方法调用时,轮询开始工作。
  poller.subscribe((err, res) => {
    // 这里每次轮询调用的结果会进来这里,err是错误信息,res是结果数据
  });

poller实例方法

方法参数类型描述
thensuccess Function, error Function注册回调队列,每次轮询产生结果时触发
subscribewatcher Function开始订阅,同上注册回调队列,并且启动轮询
unsubscribeN/A取消订阅,并关闭轮询
removeN/A同上
startN/A开始轮询
stopN/A停止轮询
tickN/A等当前产生结果后跳到下一个轮询

Class: Link

是React组件,封装了React Router中Link组件。用法同Link组件一致,所做的事情就是当app实例中设置了路由的统一前缀以及全局的query参数, 通过Link跳转时会自动带上。

  const app = new Beatle({
    base: '/example',     // 设置了路由前缀
    query: {debug: true}  // 设置了全局的query
  });
  // 访问/example/进来到此路由
  app.route('/', (props) => {
    // 这里的to只写了/,期望是跳转到根路径,实际上点击跳转到/example/?debug=true的路径。
    return (<Link to="/">回到首页</Link>);
  });
  app.run();
2.1.4

5 years ago

2.1.3

5 years ago

2.1.2

5 years ago

2.1.1

5 years ago

2.1.0

5 years ago

2.0.20

5 years ago

2.0.19

5 years ago

2.0.18

5 years ago

2.0.17

5 years ago

2.0.16

5 years ago

2.0.15

5 years ago

2.0.14

5 years ago

2.0.13

5 years ago

2.0.12

5 years ago

2.0.11

5 years ago

2.0.10

5 years ago

2.0.9

5 years ago

2.0.8

5 years ago

2.0.7

5 years ago

2.0.6

5 years ago

2.0.5

5 years ago

2.0.4

5 years ago

2.0.3

5 years ago

2.0.2

5 years ago

2.0.1

5 years ago

2.0.0

5 years ago

2.0.0-rc.37

5 years ago

2.0.0-rc.36

5 years ago

2.0.0-rc.35

5 years ago

2.0.0-rc.34

5 years ago

2.0.0-rc.33

5 years ago

2.0.0-rc.32

5 years ago

2.0.0-rc.31

5 years ago

2.0.0-rc.30

5 years ago

2.0.0-rc.29

5 years ago

2.0.0-rc.28

5 years ago

2.0.0-rc.27

5 years ago

2.0.0-rc.26

5 years ago

2.0.0-rc.25

5 years ago

2.0.0-rc.24

5 years ago

2.0.0-rc.23

5 years ago

2.0.0-rc.22

5 years ago

2.0.0-rc.21

5 years ago

2.0.0-rc.20

5 years ago

2.0.0-rc.19

5 years ago

2.0.0-rc.18

5 years ago

1.3.10

5 years ago

1.3.9

5 years ago

2.0.0-rc.17

5 years ago

2.0.0-rc.16

5 years ago

2.0.0-rc.15

5 years ago

1.3.8

5 years ago

2.0.0-rc.14

5 years ago

2.0.0-rc.13

5 years ago

2.0.0-rc.12

5 years ago

1.3.7

5 years ago

1.3.6

5 years ago

1.3.5

5 years ago

2.0.0-rc.11

5 years ago

2.0.0-rc.10

5 years ago

2.0.0-rc.9

5 years ago

2.0.0-rc.8

5 years ago

2.0.0-rc.7

5 years ago

2.0.0-rc.6

5 years ago

2.0.0-rc.5

5 years ago

2.0.0-rc.4

5 years ago

2.0.0-rc.3

5 years ago

2.0.0-rc.2

5 years ago

2.0.0-rc.1

5 years ago

2.0.0-beta.4

5 years ago

2.0.0-beta.3

5 years ago

2.0.0-beta.2

5 years ago

1.3.4

5 years ago

1.3.3

5 years ago

1.3.2

5 years ago

1.3.1

5 years ago

1.3.0

5 years ago

1.2.40

5 years ago

1.2.39

5 years ago

1.2.38

5 years ago

1.2.37

5 years ago

1.2.36

5 years ago

1.2.35

5 years ago

1.2.34

5 years ago

1.2.33

5 years ago

1.2.32

5 years ago

1.2.31

5 years ago

1.2.30

5 years ago

1.2.29

5 years ago

1.2.28

5 years ago

1.2.27

5 years ago

1.2.26

5 years ago

1.2.25

5 years ago

1.2.24

5 years ago

1.2.23

5 years ago

1.2.22

5 years ago

1.2.21

5 years ago

1.2.20

5 years ago

1.2.19

5 years ago

1.2.18

5 years ago

1.2.17

5 years ago

2.0.0-beta.1

5 years ago

1.2.16

5 years ago

1.2.15

5 years ago

1.2.14

5 years ago

1.2.13

5 years ago

1.2.12

5 years ago

1.2.11

5 years ago

1.2.10

5 years ago

1.2.9

5 years ago

1.2.8

5 years ago

1.2.7

5 years ago

1.2.6

5 years ago

1.2.5

5 years ago

1.2.4

5 years ago

1.2.3

6 years ago

1.2.2

6 years ago

1.2.1

6 years ago

1.2.0

6 years ago

1.1.27

6 years ago

1.1.26

6 years ago

1.1.25

6 years ago

1.1.24

6 years ago

1.1.23

6 years ago

1.1.22

6 years ago

1.1.21

6 years ago

1.1.20

6 years ago

1.1.19

6 years ago

1.1.18

6 years ago

1.1.17

6 years ago

1.1.16

6 years ago

1.1.15

6 years ago

1.1.14

6 years ago

1.1.13

6 years ago

1.1.13-beta

6 years ago

1.1.12

6 years ago

1.1.11

6 years ago

1.1.11-beta

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.11

6 years ago

1.0.10

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago

0.1.0

6 years ago

0.0.19

6 years ago

0.0.18

6 years ago

0.0.17

6 years ago

0.0.16

6 years ago

0.0.15

6 years ago

0.0.14

6 years ago

0.0.13

6 years ago

0.0.12

6 years ago

0.0.11

6 years ago

0.0.10

6 years ago

0.0.9

6 years ago

0.0.8

6 years ago

0.0.7

6 years ago

0.0.6

6 years ago

0.0.5

6 years ago

0.0.4-saga.1

6 years ago

0.0.4

6 years ago

0.0.3-rc.5

6 years ago

0.0.3-rc.4

6 years ago

0.0.3-rc.3

6 years ago

0.0.3-rc.2

6 years ago

0.0.3-rc.1

6 years ago

0.0.3

6 years ago

0.0.3-beta.3

6 years ago

0.0.3-beta.1

6 years ago

0.0.2

6 years ago

0.0.2-beta.3

6 years ago

0.0.2-beta.2

6 years ago

0.0.2-beta.1

6 years ago

0.0.1

6 years ago

0.0.0

6 years ago