Beatle ·
Beatle是一套轻量级前端框架,借助React、Redux实现应用界面构建流程。
基础
- 模板JSX,模板是React提供的JSX语法,使得开发者可以像编写HTML标签的方式来描述视图的结构。简单来解释的话,JSX就像是附加了几个特殊语法的HTML代码(比如:附加的{}是用来嵌套JSX子结构或者JavaScript表达式),了解JSX可参考React
- 组件component,组件是包含了模板JSX的一种特殊自定义HTML标签。(组件是一种虚拟标签ShadowDOM,使用和HTML一样简单,其内部包含了真实的HTML结构)
概念
- 组件,组件是基于React框架的组件,组件是独立展示视图的最小单位,组件可以通过组合堆叠产生更大的组件。
- 前端应用
app
,app
是new Beatle
产生,应用包含所有构建应用的方法集。 - 状态容器
store
,store
是app
全局唯一的数据缓存对象,渲染应用所需的数据模型中存储的数据,都以一个对象树的形式储存在sotre
。 - 数据模型
model
, model
数据模型是定义一类数据的初始结构以及变更这些数据的行为方法。注册数据模型是吧数据模型交给store
进行托管,数据模型中行为方法触发时,有store代理合适更新model
数据。 - 数据绑定
connect
,connect
是指定数据模型和组件建立绑定关系,一旦该数据模型的数据发生变更,组件的render会自动触发,达到重新更新视图的效果。
启动和打包
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 | 应用实例名 | app |
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 , 参考:browserHistory 和 hashHistory |
subApp Boolean | 是否为子应用 | false |
应用实例app
有相应的方法来完成应用构建,包括注册数据模型
, 注册路由
, 应用启动
等。
Beatle.getApp(appName)
- return <
Beatle
> 返回指定的Beatle实例
多应用场景下通过选择指定应用实例,从而完成单个应用的构建。
new Beatle({
name: 'appA',
...
});
new Beatle({
name: 'appB',
...
});
Beatle.getApp('appA');
Beatle.createModel(model, resource)
在Beatle中数据模型Model
是指一类数据的集合,一个数据模型包含了数据基础结构
, 改变数据的行为方法
以及跨数据模型的监听
。
resource是接口调用的封装对象,一般来说,我们会愿意把接口单独定义到业务逻辑之外的对象中。
model = {
...
actions: {
getUser: {
exec: {
url: '...',
method: 'GET'
},
callback: {
start: (nextStore, playload) => {
},
success: (nextStore, payload) => {
},
error: (nextStore, payload) => {
}
}
}
}
}
resource = {
getUser: {
url: '...',
method: 'GET'
}
}
model = {
...
actions: {
getUser: {
callback: {
success: (nextStore, payload) => {
}
}
}
}
}
Model = Beatle.createModel(model, resource);
这样下来,所有的接口都单独定义在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个实例:ajax
和 seed
, 分别为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) {
ajaxOptions.data = {};
return Promise.resolve({
})
}
);
app.ajax.beoreResponse(
function(response, ajaxOptions, request){
return response.json();
}
)
app.ajax.afterResponse(
function (result, ajaxOptions, request) {
if(result instanceof Error){
}else if(result.code !== 'SUCCESS'){
}else{
return result.data;
}
}
);
接口请求配置dataType用于声明如何解析接口数据
app.seed
seed实例是ReduxSeed实例,app.getStore()
实际上是通过seed实例中获取store对象。
app实例开放API
方法 | 参数类型 | 描述 |
---|
getStore() Object | N/A | 获取redux状态容器 |
getRoutes() Array | N/A | 获取react-router的路由配置 |
use(middleware) | middleware Function | 注册中间件,中间件是在处理处理过程中变更数据结构或者做一些必要的监控 |
getResolvePath(routeConfig) String | routeConfig 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指定注入数据模型或者根据数据模型注入数据和方法 |
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});
在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
进行托管。
app.connect('user', ReactComponent);
app.model({
displayName: 'user',
...
});
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 '@ali/beatle';
const app = new Beatle();
const Model = {
displayName: 'user',
store: {
nickname: 'anonymous'
},
actions: {
login: {
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();
app.route([path, routes])
- path <
String
>, 当存在path时,则是配置单个路由,此时routes应该为React组件或者Beatle子应用。 - routes <
ReactComponent|Beatle|ReactRouter
>, 不存在path时基于ReactRouter的路由的配置.
app.routes([
{
path: '/',
component: RootComponent,
childRoues: [
{
path: 'profile',
component: ProfileComponent
}
]
}, {
path: '*',
component: 404Component
}
])
- app.route(path, component)
app.route('/', RootComponent);
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();
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');
Model
Model数据模型是一类数据的集合,包含了数据的初始化结构,以及改变这些数据的行为方法。
描述一个Model对象,需要具备以下数据结构:
属性 | 描述 | 默认值 |
---|
displayName | 实例名 | N/A |
store | 数据基础结构 | {} |
actions | 改变数据的行为方法 | N/A |
subscriptions | 跨数据模型的行为监听 | N/A |
const propTypes = React.PropTypes;
const modelShape = {
displayName: propTypes.string,
store: propTypes.object.isRequired,
actions: propTypes.object,
subscriptions: propTypes.object
}
const action = {
exec: propTypes.oneOfType([propTypes.object, propTypes.func]),
callback: propTypes.oneOfType([propTypes.object, propTypes.func]),
}
const action = {
callback: propTypes.func,
}
const subscriptions = {
`${modelName}/${actionName}/${status}: (nextStore, playload) => {
}
}
符合以上数据结构的Model可以通过app.model(Model)
注册到应用中。
何时使用Beatle.createModel(model, resource)
, 当你异步的action中exec需要单独维护到model外部时,通过Bealte.createModel组合进来,生成最终的Model
Model行为action的配置
属性 | 参数类型 | 描述 |
---|
exec | Object/Function | 异步行为的触发条件,Beatle内部通过exec来识别异步行为,当exec为接口配置,会转为一个接口调用函数,如果是函数则不用做变动 |
callback | Object/Function | 行为触发成功后进入,在同步行为时,callback只能为函数,异步行为时callback一般来说是对象,有3个回调函数,start , success 和 error |
reducer | Object/Function | 同上callback |
subscriptions | Object | 跨数据模型监听行为,从而变更自身数据 |
externalReducers | Object | 同上subscriptions |
行为调用的处理逻辑
- 行为方法触发时传入的参数会放到payload.arguments中
- 判断行为是否存在
exec
属性,则会当做异步行为进行调用 - 触发行为时,会先执行
start
回调, - 判断exec为函数时,执行函数返回非promise值,会直接进入到
success
回调,否则在promise的接收值时进入到success
回调,在拒绝值时进入到error
回调 - 如果exec为接口配置,则通过应用内部的ajax实例来发起接口调用,在接口成功并接收值时进入到
success
回调,在拒绝值时进入到error
回调 - 同步行为时直接进入到callback回调
callback: function (nextStore, payload){
}
属性 | 描述 |
---|
type | 当前行为处理状态,不同行为状态会进入到不同的回调中 |
store | 当前model的基础数据 |
arguments | 行为调用时传入的参数 |
data | 异步行为调用后,接收的数据 |
message | 当异步行为调用失败后,会存在错误信息 |
- 基于异步action调用以及跨数据模型监听行为的实现举个例子:
const UserModel = {
displayName: 'user',
store: {
profile: {
pending: true,
nickname: 'anonymous'
}
},
actions: {
login: {
callback: {
start: (nextStore, payload) => {
nextStore.profile = payload.store;
},
success: (nextStore, payload) => {
nextStore.profile = {
pending: false,
nickname: payload.data.nickname
};
}
}
}
}
};
const UserResource = {
login: {
url: '/login',
method: 'GET'
}
}
const AccountModel = {
dispayName: 'account',
store: {
nickname: ''
},
subscriptions: {
'user.login.success': (nextStore, user_login_payload) => {
nextStore.nickname = user_login_payload.data.nickname;
}
}
}
app.model(Beatle.createModel(UserModel, UserResource));
app.model(AccountModel);
Resource
Resource是接口配置对象,结合Beatle.createModel来使用
Beatle.createModel(model, resource)
时其内部将做如下处理
- 遍历resource对象,拿到每个属性和值
- 在model.actions中找到对应属性的行为,把值赋给行为的exec对象,找不到行为则,则丢弃掉
const userModel = {
...
actions: {
login: {...}
}
};
const userResource = {
login: {
url: '/login',
method: 'GET',
params: {
username: 'default username'
}
},
getUserList: {
url: '/user/list',
method: 'GET',
params: {
pageSize: 10,
pageNo: 1
}
}
};
Beatle.createModel(userModel, userResource);
Class: ReduxSeed
import {ReduxSeed} from '@ali/beatle';
const seed = new ReduxSeed({...});
- new ReduxSeed传入options配置项:
属性 | 描述 | 默认 |
---|
name String | ReduxSeed支持多实例,初始化一个seed实例需要指定实例名称 | main |
ajax Object | ajax实例 | N/A |
initialState Object | store的基础结构 | {} |
middlewares Array | 应用数据处理中间件,通过中间件可以变更数据结果 | [] |
Models Object | 注册多个数据模型 | {} |
ReduxSeed静态属性
名称 | 参数类型 | 描述 |
---|
createModel | model Object , resource Object | 组合resource到model中,等同于Beatle.createModel |
getRedux | name String | 获取指定的seed实例 |
seed实例方法
名称 | 参数类型 | 描述 |
---|
reducerBuilder | model Object , resource Object | 组合resource到model中,等同于Beatle.createModel |
register | model Object , resource Object | 注册一个model到seed实例 |
getActions | modelName 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 '@ali/Beatle';
Ajax.headers = {
csrfToken: '...'
}
Ajax.normalize = false;
const ajax = new Ajax({
normalize: true
});
ajax.get('...');
ajax.set('headers', {});
ajax.get('...')
实例级设置可以参考ajax实例设置实例级别的全局监听。
Ajax.request(ajaxOptions)
Ajax静态方法,其内部会初始化一个ajax实例,并调用ajax.request来执行
ajax实例方法
名称 | 参数类型 | 描述 |
---|
request | options Object | 接口请求调用,所有其他方式的请求最终都会走request来执行 |
get | path String , data Object/null , options Object/Function , dataType String/Function | get请求 |
post | path String , data Object/null , options Object/Function , dataType String/Function | post请求 |
put | path String , data Object/null , options Object/Function , dataType String/Function | put请求 |
delete | path String , data Object/null , options Object/Function , dataType String/Function | delete请求 |
patch | path String , data Object/null , options Object/Function , dataType String/Function | patch请求 |
ajax.request(options)
- options <
Object
> 接口请求配置 - return <
Promise|null
> 配置中有callback则不会返回内容,否则会返回调用的promise
属性 | 参数类型 | 描述 |
---|
url | String | 请求地址 |
method | String | 请求方法 |
headers | Object | 请求头部 |
mode | String | 请求模式,参考 cors , no-cors , same-origin , 默认no-cors |
credentials | String | 请求凭证, 参考omit , same-origin , include , 有凭证才带cookie,否则不带cookie |
cache | String | 缓存模式,参考 default , reload , no-cache , 默认default |
callback | Function | 回调处理函数,当存在callback时不会返回promise实例 |
dataType | String | 接口返回结果对数据解析处理基于dataType类型来决定,默认为json解析 |
取值 | 描述 |
---|
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
其他接口方法形式一致,包括post
、 delete
、put
和 patch
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 '@ali/beatle';
const poller = new Pooler({
action: () => {
return Beatle.Ajax.request({url: '', method: 'get'});
}
});
poller.subscribe((err, res) => {
});
poller实例方法
方法 | 参数类型 | 描述 |
---|
then | success Function , error Function | 注册回调队列,每次轮询产生结果时触发 |
subscribe | watcher Function | 开始订阅,同上注册回调队列,并且启动轮询 |
unsubscribe | N/A | 取消订阅,并关闭轮询 |
remove | N/A | 同上 |
start | N/A | 开始轮询 |
stop | N/A | 停止轮询 |
tick | N/A | 等当前产生结果后跳到下一个轮询 |
Class: Link
是React组件,封装了React Router中Link组件。用法同Link组件一致,所做的事情就是当app实例中设置了路由的统一前缀以及全局的query参数, 通过Link跳转时会自动带上。
const app = new Beatle({
base: '/example',
query: {debug: true}
});
app.route('/', (props) => {
return (<Link to="/">回到首页</Link>);
});
app.run();