decorator的mvc开发模式
koa-cola可以使用es7的decorator装饰器开发模式来写mvc,controller是必须用提供的decorator来开发(因为涉及到router相关的定义),model和view层则没有强制需要demo所演示的decorator来开发。
Controller
使用decorator装饰器来注入相关依赖,路由层的decorators包括router、中间件、response、view,响应阶段的decorators包括koa.Context、param、response、request等,比如以下例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const { Controller, Get, Use, Param, Body, Delete, Put, Post, QueryParam, View, Ctx, Response } = require('koa-cola/client'); import Ok from '../responses/ok';
@Controller('') class FooController { @Get('/some_api') @Response(Ok) some_api (@Ctx() ctx, @QueryParam() param : any) { return { foo : 'bar' } } }
|
Ok 模块:
1 2 3 4 5 6 7 8 9 10
| import * as Koa from 'koa'; export default function Ok(ctx : Koa.Context, data){ ctx.status = 200; if(data){ ctx.body = { code : 200, result : data }; } }
|
因为使用decorator定义router,所以在koa-cola里面不需要单独定义router。
View
page的view组件可以使用不同类型的react组件:
- React.Component组件
- stateless的函数组件
- react-redux组件
- koa-cola提供的Cola装饰器组件(基于react-redux的数据初始化组件)
React.Component组件
1 2 3 4 5 6 7 8 9 10 11 12
| class Index extends React.Component<Props, States> { constructor(props: Props) { super(props); } static defaultProps = { }; render() { return <h1>Wow koa-cola!</h1> } }; export default Index
|
stateless组件
1 2 3
| export default function({some_props}) { return <h1>Wow koa-cola!</h1> }
|
react-redux组件
1 2 3 4 5 6 7 8
| import { connect } from 'react-redux' const Index = function({some_props}) { return <h1>Wow koa-cola!</h1> } export default connect( mapStateToProps, mapDispatchToProps )(Index)
|
Cola 装饰器组件
使用Cola装饰器来封装基于react-redux的组件
如果有子组件也是使用Cola 装饰器封装,则需要使用装饰器include
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import AddTodo from '../official-demo/containers/AddTodo'; import FilterLink from '../official-demo/containers/FilterLink'; import VisibleTodoList from '../official-demo/containers/VisibleTodoList'; const { Cola include } = require('koa-cola/client');
@Cola({ initData : { todosData : async ({ params, helpers, store: { dispatch } }) => { const api = new GetTodoList({}); const data = await api.fetch(helpers.ctx); dispatch({ type: 'INIT_TODO', data: data.result.result }); return data.result.result; } }, reducer : { todos, visibilityFilter } }) @include({ AddTodo, FilterLink, VisibleTodoList }) class ColastyleDemo extends React.Component<Props, States> { constructor(props: Props) { super(props); } render() { return <App />; } } export default ColastyleDemo;
|
自定义 header 和 bundle 方式
koa-cola 渲染页面时,默认会找views/pages/layout.ts
封装页面的 html,如果没有这个layout
文件,则直接输出 page 组件返回的 html,如果 view 组件使用了doNotUseLayout
装饰器,则页面不会使用layout.ts
输出,这时你可以自定义header
和bundle
装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import * as React from 'react'; const { header, bundle, doNotUseLayout } = require('koa-cola/client'); @doNotUseLayout @bundle([ "/bundle.js", "/test.js" ]) @header(() => { return <head> <meta name="viewport" content="width=device-width" /> </head> }) function Page (){ return <h1>koa-cola</h1> }; export default Page
|
Model
controller
层、react 组件的view
层必须使用 decorator,而model
层则没有这个限制,它完全没有耦合。您可以使用 koa-cola 风格来编写 model,以获得更一致的开发体验,也可以随意使用任何 orm 或者 odm,或者不需要 model 层也可以。
你可以直接在目录 api/models 下创建如 user.ts:
1 2 3 4 5
| import * as mongoose from 'mongoose' export default mongoose.model('user', new mongoose.Schema({ name : String, email : String }))
|
然后就可以在其他代码里面使用:
1
| const user = await app.models.user.find({name : 'harry'})
|
使用 koa-cola 的风格写 model
首先在api/schemas
目录创建user.ts
1 2 3 4 5 6 7 8 9 10
| export const userSchema = function(mongoose){ return { name: { type : String }, email : { type : String } } }
|
在目录api/models
下创建 model 如user.ts
:
1 2 3
| import * as mongoose from 'mongoose' import userSchema from '../schemas/user' export default mongoose.model('user', new mongoose.Schema(userSchema(mongoose)))
|
当然也可以使用 decorator 方式定义 model,还可以定义相关hook,详情可以参考mongoose-decorators
1 2 3 4 5
| import { todoListSchema } from '../schemas/todoList'; const { model } = app.decorators.model;
@model(todoListSchema(app.mongoose)) export default class TodoList {}
|
使用 cli 生成 model 的 schema
koa-cola --schema
自动生成model的接口定义在typings/schema.ts
然后你可以在代码通过使用 typescript 的类型定义,享受 vscode 的 intellisense 带来的便捷
1 2
| import {userSchema} from './typings/schema' const user : userSchema = await app.models.user.find({name : 'harry'})
|
在前面提到的为何需要在api/schemas
定义 model 的 schema,除了上面的自动生成 schema 接口,还可以在浏览器端代码复用,比如数据Validate。详细可以查看文档
koa-cola提供了前后端universal的api接口定义,比如todolist demo的获取数据的接口定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { todoListSchema } from './typings/schema'; import { ApiBase, apiFetch } from 'koa-cola';
export class GetTodoList extends ApiBase< { }, { code: number; result: [todoListSchema]; }, { } > { constructor(body) { super(body); } url: string = '/api/getTodoList'; method: string = 'get'; }
|
在代码里面使用 api,并享受 ts 带来的便捷:
1 2
| const api = new GetTodoList({}); const data = await api.fetch(helpers.ctx);
|
又比如参数body
的定义,如果定义了必传参数,调用时候没有传,则 vscode 会提示错误
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { testSchema } from './typings/schema'; import { ApiBase, apiFetch } from 'koa-cola' export interface ComposeBody{ foo : string, bar? : number } export class Compose extends ApiBase<ComposeBody, testSchema, {}>{ constructor(body : ComposeBody){ super(body) } url : string = '/compose' method : string = 'post' }
|