0.1.2 • Published 6 years ago

@lianjia-fe/bucky-core v0.1.2

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

Bucky

基于 koa 开发的 node 框架 with ♥︎ by lianjia-fe

     __                __
    |  |--.--.--.----.|  |--.--.--.
    |  _  |  |  |  __||    <|  |  |
    |_____|_____|____||__|__|___  |
                            |_____|
    with ♥︎ by lianjia-fe

使用方式

通过 bucky-cli 生成你的项目 参见 https://github.com/LianjiaTech/bucky-cli

之后通过下面命令启动

$ npm install
$ npm run dev

项目结构

your-project
├── bin            // 上线 op 启动脚本
└── src            // 项目目录
    ├── actions    // 动作
    ├── apis       // 接口
    ├── configs    // 配置
    ├── models     // 数据对象
    ├── responses  // 返回封装
    ├── services   // 插件
    └── views      // 模版

CONFIG

bucky 很多工作都可以使用配置完成,配置的设置会分布在各个地方 需要了解 配置中环境的覆盖特征

通过 configs/nev 中可以设置当前的环境,默认使用 process.env.NODE_ENV 来外部注入

下面是 configs/app 的例子

import prodConfig from './_prod'

export default {

  name: PKG.name,

  // 项目监听的端口
  port: prodConfig.base.PORT,

  // 是否全局绑定 lodash
  _: true,

  // 是否前端有代理
  // 如果你的server 前面还有 nginx 等的代理则选择 true
  proxy: true
}

// 下面的特定环境可以深度合并到上面的默认环境
// 线上环境是上面的默认环境,不要乱改哦

// 开发环境配置
export const development = {
  port: 3000,
  proxy: false
}
// 测试环境配置
export const testing = {
  port: 3000,
  proxy: false
}

在 default, development, testing 中可以写不同的配置。

  • 在 production 环境中 配置为 default
  • 在 development 环境中 配置为 default + development
  • 在 testing 环境中 配置为 default + testing

数据是 两个数据的 深度 merge 得到的。

API

api 用于数据接口调用,规定数据格式和调用方式

在 apis 目录中添加配置文件,会自动生成api

可以通过 bucky api myAPI 创建

export default api => {

  api.config = {
    // 接口超时时间
    timeout: 10000,

    // 默认的返回值简单约定, {code: , data:, msg:} 的形式返回
    // 如果是这种返回形式的话,那么通过下面参数,做接口检查
    codeKey: 'code',
    dataKey: 'data',
    messageKey: 'msg',

    // 可以使用以下方法 处理 api 请求前的数据
    // requestHandler () {}

    // 可以使用以下方法 处理 api 返回的数据
    // responseHandler () {}

    // 当 [codeKey] === [successCode] 是认为请求成功
    successCode: 100000,

    // 接口的协议,域名,端口
    base: 'http://[domain]:[port]',

    // 上行格式 可以是:
    // application/x-www-form-urlencoded
    // multipart/form-data
    // application/json
    // 等等
    // contentType: 'application/x-www-form-urlencoded'
  }

  // 定义接口 这边的设置是在 config 基础上的
  // 内部的没有点的值复用 config 中的
  api('__api_name__', {

    // 接口 path, 可以使用 {xxx} 的方法动态修改 uri
    // 在请求的 uriReplacer 参数设置
    uri: 'i/{am}/a/path',

    // 接口访问方式 get | post | delete | put ...
    method: 'get',

    // 接口参数检查
    parameters: {
      // string: api.type.string.required,
      // number: api.type.number.required
    },

    // 是否使用缓存
    // 简单的缓存设置, 写使用的 redis 名即可
    // cache: 'cache',
    // 如果想自定义,可以使用下面的
    // cache: {
    //   redis: 'cache',
    //   key: req => req.method === 'GET' ? req.uri : null,
    //   fromCache: cache => JSON.parse(cache),
    //   toCache: data => JSON.stringify(data)
    // }
  })

  // api(....) 继续定义下一个接口
}

生成完之后,你可以用以下的方式调用

await API.[文件名].[定义接口名](data, option)

参数定义:

  • data {Object}: 接口请求参数,通过不同的 method,作为 search 或者 body
  • option {Object}: 可选 配置
    • query {Object}: 可选 post或着其他将data转化为body的时候动态加一些 query
    • uriReplacer {Object}: 可选 可以替换uri上 {xxxx} 中的值,动态修改uri
    • queryEncode {Boolean}: 可选 默认true 是否将 query 中的 value 做 encodeURIComponent

Model

Model 用于构建数据对象

在 models 目录中添加配置文件

可以通过 bucky model MyModel 创建

model 的命名必须首字母大写(通过 bucky-cli 会自动给你转化首字母),必须是个类

export default class MyModel {

  // 实例初始化方法
  constructor () {}

  // 静态方法
  static staticFunction () { }

  // 静态同步方法
  static async staticAsyncFunction () { }

   // 实例方法
  staticFunction () { }

  // 实例同步方法
  static async asyncFunction () { }
}

Action

action 是最终业务实现。一个业务对应一个action

在 actions 目录中添加配置文件

可以通过 bucky action /my/path/here 创建

在没有其他干扰的情况下, action 文件的目录路径即为访问路径

action 在 configs/action 有一些配置

  • defaultAction: action 中的默认值

一个action的配置

export default {

  // csrf 作用是判断来访者身份,如果是 false 不会判断
  // 默认操作是,如果是 post, put, delete 操作,并且 来访者 域名和当前域名不匹配则会拒绝访问
  // 如果 csrf 为 字符串 或者 字符串 数组(可以是minimatch),则相当于设置白名单,白名单中的会被认可
  // csrf: false,
  // csrf: [
  //  '*.lianjia.com',    // minimatch
  //  'aaa.lianjia.com',  // 域名
  //  '*',  // 匹配所有
  //  ''    // 允许 空 referer
  //  ],


  // cors 是否允许 异步跨域访问
  // cors: '*',
  // cors: 'http://lianjia.com',
  // cors: {
  //  origin: 'http://lianjia.com',
  //  methods: 'GET, POST',
  //  Headers: 'X-Requested-With'
  // },

  // 如果路由命中则会走这个方法
  // 可以在 rewrite redirect 修改路由指向
  async handler (ctx) {

    // ctx 为 koa 的 context 上下文
    // 详见: http://koajs.com/#context

    // ctx.request  为 koa request 对象
    // 详见: http://koajs.com/#request

    // ctx.response 为 koa response 对象
    // 详见: http://koajs.com/#response

    // 使用 ctx.request.query 获取 url query 参数
    // 使用 ctx.request.body 获取通过 x-www-form-urlencoded 方式提交的 body
    // 使用 ctx.request.form 获取 formData 方式提交的 form 表单

    // 使用 API.xxx, Model.xxx 调用 api model 中声明的 接口 和 类

    // 使用 ctx.render 渲染模版
    // 默认使用 ejs, 可以在 config/view 中设置
    // ctx.render('index', { title: 'bucky' })

    // 页面未找到
    // ctx.notFound()

    // 页面无权限
    // ctx.forbidden()

    // 页面跳转
    // ctx.redirect('http://lianjia.com')

    // 页面跳转(使用js方式)
    // ctx.redirect('http://lianjia.com', {viaJavascript: true})

    // 系统错误,并且报错
    // ctx.serverError(new Error('server error'))

    // ajax 返回
    // ctx.ajax({greeting: 'hello world'})

    // ajax 返回异常
    // ctx.ajax(null, {error: true, message: '加载失败'})
  },

  // 如果 handler 代码出错,可以在 catchError 中捕获处理错误
  // 如果没有 catchError, 则会自动抛 500 错误,并返回错误内容
  // async catchError (ctx, error) { },
}

Response

response 是对数据返回的封装

默认封装为

ctx.notFound() 返回页面不存在 使用 404.ejs 模版渲染空页面

ctx.forbidden() 返回页面没权限 使用 403.ejs 模版渲染空页面

ctx.serverError(error) 返回页面服务错误 使用 500.ejs 模版渲染空页面

  • error {Error} 错误对象

ctx.redirect(url, {viaJavascript}) 返回页面需要跳转 使用 302.ejs 模版渲染空页面

  • url {String} 跳转的页面地址
  • viaJavascript {Boolean} 是否使用 javascript 的方式跳转

ctx.ajax(data, {error, message}) 渲染 ajax 数据返回(json)

  • data {Any} 可选 返回数据对象
  • error {Boolean|Any} 可选 是否错误(默认错误类型), 制定错误类型 (错误类型在 configs/response 中规定)
  • message {Any} 可选 错误的描述

ctx.jsonp(data, {error, message, handlerKey}) 渲染 jsonp 数据返回(json)

  • data {Any} 可选 返回数据对象
  • error {Boolean|Any} 可选 是否错误(默认错误类型), 制定错误类型 (错误类型在 configs/response 中规定)
  • message {Any} 可选 错误的描述
  • handlerKey {String} 可选 jsonp 钩子的 query 的 key

ctx.render(templatePath, data, {layout})

  • templatePath {String} 对应的 views 目录下的文件的路径,从 views 算 relative 路径
  • data {Object} 渲染模版的数据
  • layout {False|String} 渲染模版外层layout模版(false为没有)

View

view 是模版渲染的模版的存放位置

可以在 configs/view 中设置模版引擎配置

export default {

  // 你想使用的模版类型,默认是 ejs
  template: 'ejs',

  // 项目 外层框架的模版 位置 (相对于 views 文件夹)
  layout: 'layout',

  // 拼装模版数据,在这边写,每次都会带上
  // 可以在这边定义 静态文件的版本号 之类的东西
  data: {
    title: 'bucky'
  },

  // ejs 的配置
  ejs: {
    compileDebug: false,
    delimiter: '%',
    ext: '.ejs'
  }

}

跳转

跳转规则设置

可以修改 configs/redirect 和 configs/rewrite 来修改跳转,

区别是 redirect 是通过 302 来实现的, rewrite 是通过内部修改 url 的情况实现的。

在 rewrite 中可以通过 ctx.originalUrl 为转化之前的 url

export default [

  // rewrite 规则
  // 动态改变 url 让其走到对应的路由
  // 是 app 内部逻辑
  // 规则从上到下,如果匹配则不会再往下走了

  // from 可以是 正则,方法,字符串
  // 当url 被 正则匹配 或 方法返回非空 或 字符串匹配 minimatch
  // 则会走到对应的 to
  // to 可以是字符串,也可以是方法
  // 如果是方法 参数接收 正则匹配的match值, 方法返回值, 字符串是否匹配的布尔值
  // 返回为 字符串

  // 这个例子是改写 页面图标 的路由
  { from: '/favicon.ico', to: '/public/favicon.ico' }

]

Redis

设置 redis 配置

export default {

  // 配置一个名为 "cache" 的 redis
  // 参见 http://redis.js.org/#api-rediscreateclient
  cache: {
    host: '127.0.0.1',
    port: '6379',
  }

}

使用

可以通过对应配置中 redis 名称,在全局 Redis.redis 名称 来获取对应的 redis 使用池 在会掉闭包中执行redis命令, 不需要释放 redis 句柄

记得对应的 async await 的使用 具体 redis 命令 查看 http://devdocs.io/redis/

const result = await Redis.cache(async redis => {
  await redis.hset(cacheKey, name, value)
  return await redis.hget(cacheKey, name)
})

hashCache

因为常见的 redis 使用是缓存,所以可以使用封装的 hashCache 方法,更加简单调用

await Redis.cache.hashCache.get('some_name', 'some_value')
await Redis.cache.hashCache.set('some_name')

通过 hashCache 使用默认的 redis key 存储对象 你可以通过 name_space 来区分存储 hash 池

await Redis.cache.hashCache('name_space').get('some_name', 'some_value')
await Redis.cache.hashCache('name_space').set('some_name')

MySQL

设置 MySQL 配置

export default {

  // 配置一个名为 "store" 的 mysql
  // 参见 https://www.npmjs.com/package/mysql#pooling-connections
  store: {
    host: '127.0.0.1',
    port: '3306',
    user: 'root',
    password: '123456',
    database: 'test'
  }

}

使用

可以通过对应配置中 mysql 名称,在全局 MySQL.mysql 名称 来获取对应的 mysql 使用池 在会掉闭包中执行mysql命令, 不需要释放 mysql 句柄

记得对应的 async await 的使用

const items = await MySQL.store(async store => {
  return await store.query(`select * from pet where name='${name}'`)
})
await MySQL.store(async store => {
  await store.query(`insert into pet(type, name) values('${type}','${name}')`)
})

可以使用 https://www.npmjs.com/package/sql 来声称 sql语句。 更加方便,并且避免sql注入

插件 service

书写自定义 koa 插件,也可以改造现有 koa 插件进来

Lodash

当在 configs/app.js 中设置 _: true, 即 全局中 _ 即位 lodash 句柄,哪都可以用

已经封装的功能

Form Parse / Body Parse

  • 如果 接口通过 'multipart/form-data' 或者 'application/octet-stream' 等方式发送的 formData 请求,请使用 ctx.form 或者 ctx.request.form 方式获取,如果是文件类型,返回是stream

  • 如果 接口通过 'application/x-www-form-urlencoded' 或者 'application/json' 等方式请求的,请使用 ctx.body 或者 ctx.request.body 方式获取(该方式不可能是文件)

Static Server (静态资源)

设置 configs/static.js

export default {

  // 静态路径
  staticPath: '/public',

  // 静态文件夹路径
  staticDir: path.resolve(__dirname, '../statics'),

  // 文件是否返回 etag 头
  etag: true,

  // 文件是否返回 Last-Modified 头
  lastModified: true,

  // 用户客户端缓存时间
  maxAge: 60
}

默认 Models

Utils

Model.Utils 提供一些可使用的方法集合

  • appendSearch 往 uri 上追加 search
  • @param {Object} object
  • @param {Boolean} encode
  • @return {String}
  • assert 错误检查,如果命中抛异常
  • @param {Boolean} 错误检查
  • @param {String} 输出信息模版
  • @param {...any} 模版的参数
  • request requset 库

  • requestAsync 异步 request 封装,同 requset 库

  • escapeHTML 转移 HTML 特殊字符

  • @param {String}
  • @param {String}
  • deepMerge 深拷贝
  • @param {Object}
  • @param {Object}
  • @param {Object}
  • formatPath 格式化 url 路径
  • @param {String}
  • @return {String}
  • /fsdfsd/fdsfdsf//fds///dfsds/ => /fsdfsd/fdsfdsf/fds/dfsds
  • getPkg 获取 package.json
  • @return {Object}
  • getType getType 获取对象类型
  • @param {any} object
  • @return {string} boolean|number|string|function|array|date|regexp|object|error
  • isIP 是否是 ip 格式
  • @param {String}
  • @param {Boolean}
  • match 匹配字符串
  • @param {String} string
  • @param {Regexp|Function|String} rule
  • @param {Any} 给 Function 类型的 rule 添加参数
  • @return {any}
  • md5 返回md5值
  • @param {String} string
  • @return {String}
  • noop 空方法

  • object2Search 将 object 转化成 query

  • @param {Object} object
  • @param {Boolean} encode
  • @return {String}
  • objectForEach 循环对象元素
  • @param {Object} object Object, 最好是一个纯对象
  • @param {Function} iteratee 循环函数
  • @return {Object} 类似Array.map 将 iteratee 返回的数据组成新对象
  • promisify 将 node callback 方式转化成 promise 的方式
  • @param {Function} originalFunction
  • @param {any} context
  • @param {Boolean} returnMulitArgs
  • @return {Promise}
  • string2Array 转换字符串到数组
  • @param {String} string
  • @param {String} spliter
  • @return {Array}
  • uuid 获取随机 id
  • @return {String}

测试部署

确保测试的 node 环境 >= 6.4.4

推荐测试安装 pm2 进行管理 node 服务,在 测试机 使用 npm install -g pm2 的方式安装。

测试可以通过 pm2 list, pm2 start, pm2 delete 等命令管理服务,简单明了。

安装完 pm2 后,通过如下代码启动 node 服务(测试环境)

pm2 delete <项目名,测试自己定,不要和其他项目重复就可以>
cd  <项目源码目录>
pm2 start npm --name "<项目名,测试自己定,不要和其他项目重复就可以>" -- run test

如果 pm2 命令找不到,请自行做下映射关系 /usr/local/node/bin/pm2 -> pm2

上线部署

  1. 需要注意,在 configs/_prod.js 文件,这个文件会读取 op 服务的配置。需要修改 hostName 变量为你的上线文件夹名(基本是你上线域名),其他代码一般不需要修改。

  2. 线上是通过 bin/run.js 启动服务的,而不是 npm script。一般这个文件不需要做修改。已经统一配置了。

推荐第三方 node modules

moment

npm install moment

A lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.

mathjs

npm install mathjs

Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices. Powerful and easy to use.

accounting-js

npm install accounting-js

accounting-js is a tiny JavaScript library for number, money and currency parsing/formatting. It's lightweight, fully localisable, has no dependencies, and works great client-side or server-side. Use standalone or as a nodeJS/npm and AMD/requireJS module.