Fork me on GitHub

ThinkJS学习笔记(一)

基本介绍

  • ThinkJS 是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中 async/await,或者 ES6 中的 */yield 特性彻底解决了 Node.js 中异步嵌套的问题。同时吸收了国内外众多框架的设计理念和思想,让开发 Node.js 项目更加简单、高效
  • 使用 ES6/7 特性来开发项目可以大大提高开发效率,是趋势所在。并且新版的 Node.js 对 ES6 特性也有了较好的支持,即使有些特性还没有支持,也可以借助 Babel 编译来支持。
  • 本次是基于ThinkJS 2.2 版本进行学习。

创建项目

安装 Node.js

  • ThinkJS 是一款 Node.js 的 MVC 框架,所以安装 ThinkJS 之前,需要先安装 Node.js 环境。安装完成后,在命令行执行 node -v,如果能看到对应的版本号输出,则表示安装成功。
  • ThinkJS 需要 Node.js 的版本 >=0.12.0,如果版本小于这个版本,需要升级 Node.js,否则无法启动服务。建议将 Node.js 版本升级到 4.2.1 或更高版本。

安装 ThinkJS

  • 通过下面的命令即可全局安装 ThinkJS:

    1
    npm install thinkjs@2 -g --verbose
  • 安装完成后,可以通过 thinkjs –versionthinkjs -V 命令查看安装的版本。

使用命令创建项目

  • ThinkJS 安装完成后,就可以通过下面的命令创建项目:
    1
    thinkjs new project_path; #project_path为项目存放的目录

安装依赖

  • 项目安装后,进入项目目录,执行 npm install 安装依赖,可以使用 taobao 源进行安装。
    1
    npm install --registry=https://registry.npm.taobao.org --verbose

启动项目

  • 在项目目录下执行命令 npm start,如果能看到类似下面的内容,表示服务启动成功。
    1
    2
    3
    4
    5
    6
    [2015-09-21 20:21:09] [THINK] Server running at http://127.0.0.1:8360/
    [2015-09-21 20:21:09] [THINK] ThinkJS Version: 2.0.0
    [2015-09-21 20:21:09] [THINK] Cluster Status: closed
    [2015-09-21 20:21:09] [THINK] WebSocket Status: closed
    [2015-09-21 20:21:09] [THINK] File Auto Reload: true
    [2015-09-21 20:21:09] [THINK] App Enviroment: development

访问项目

模块

  • ThinkJS 创建项目时支持多种项目模式,默认创建的项目是按模块来划分的,并且自动添加了 commonhome 2 个模块。每个模块有独立的配置、控制器、视图、模型等文件。
  • 使用模块的方式划分项目,可以让项目结构更加清晰。
  • 路由访问规则:
    1
    2
    域名/模块名/控制器名 /action名
    test.com/home/api/index

模块列表

  • src是源代码目录,使用 ES6 模式创建项目才有该目录。项目启动时会自动将 src 目录下的文件编译到 app 目录下。
  • 在ThinkJS中是用模块来分拆项目的。

common模块

  • common 模块是个通用模块,该模块下存放一些通用的功能,如: 通用的配置,runtime 目录,启动文件,错误处理控制器等。
  • :该模块下的控制器不能响应用户的请求。

    src/common/bootstrap
    • 项目启动目录,该目录下的文件会自动加载,无需手动 require 。
    • 可以在这个目录下文件里定义一些全局函数、注册中间件等常用的功能。

      1
      2
      3
      4
       // src/common/bootstrap/global.js
      global.formatDate = function() {
      ...
      }
    • :bootstrap 只能放在 common 模块里。

    src/common/config
    • 配置文件,这里放一些通用的配置。
    • 其中:路由配置、hook 配置、本地化配置等必须放在这里。
    src/common/controller
    • 控制器,放一些通用的控制器。其中 error.js 里错误处理的不同行为,项目里可以根据需要进行修改。

默认模块

  • 项目默认模块为 home 模块。当解析用户的请求找不到模块时会自动对应到 home 下。
  • 可以在 src/common/config/config.js 中修改配置default_module 来重新定义默认模块:
    1
    2
    3
    4
    //将默认模块名改为 blog
    export default {
    default_module: 'blog'
    }

添加模块

  • 添加模块直接通过 ThinkJS 命令即可完成。
  • 在当前项目目录下,执行 thinkjs module xxx,即可创建名为 xxx 的模块。

    1
    thinkjs module user //添加一个user模块
  • 如果模块名已经存在,则无法创建。

  • 模块下默认有四个文件夹:

    1
    2
    3
    4
    config---配置信息,如数据库等
    controller --- 控制器目录,控制器用于获取post与get数据和处理逻辑。一个 url 对应一个 controller 下的 action
    logic --- 这是thinkjs最有特色的一点,和controller一一对应,用于验证controller数据合法性与处理数据,在controller之前调用,可以降低controller里action的复杂度
    model --- 用于操作数据库,返回数据
  • 可以给模块增加一个service文件夹,用于存放公用方法,可供其他模块调用。假如在refund模块里面,要调用其/service里面common.js里的check()方法,则在/controller里面的api.js里面调用方式如下:

    1
    2
    3
    let refundService = think.service('common', 'refund');
    let refundInstance = new refundService();
    let refundCount = await refundInstance.check();
添加 controller
  • 可以在项目目录下通过 thinkjs controller [name] 命令来添加 controller。如:

    1
    thinkjs controller user;
  • 执行完成后,会创建 src/common/controller/user.js 文件,同时会创建 src/common/logic/user.js 文件。

  • 默认会在 common 模块下创建,如果想在其他模块下创建,可以通过指定模块创建。如:

    1
    thinkjs controller home/user;
  • 指定模块为 home 后,会创建 src/home/controller/user.js 文件。

添加 service
  • 可以在项目目录下通过 thinkjs service [name] 命令来添加 service。如:

    1
    thinkjs service github; #创建调用 github 接口的 service
  • 执行完成后,会创建 src/common/service/github.js 文件。

  • 默认会在 common 模块下创建,如果想在其他模块下创建,可以通过指定模块创建。如:

    1
    thinkjs service home/github;
  • 指定模块为 home 后,会创建 src/home/service/github.js 文件。

禁用模块

  • ThinkJS 默认会自动查找和识别项目下的模块,并认为所有的模块都是可用的。
  • 如果想禁用部分模块,可以修改配置文件 src/common/config/config.js,添加下面的配置:
    1
    2
    3
    export default {
    deny_module_list: ['xxx'] //禁用 xxx 模块
    }

控制器

  • 控制器是一类操作的集合,用来响应用户同一类的请求。

使用 async/await

  • 借助 Babel 编译,可以在控制器里使用 ES7 里的 async/await。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    'use strict';

    import Base from './base.js';

    export default class extends Base {
    /**
    * index action
    * @return {Promise} []
    */
    async indexAction(){
    let model = this.model('user'); //实例化模型 user
    let data = await model.select();
    return this.success(data);
    }
    }

Action

  • 控制器里的每一个Action 代表一个要执行的操作。如: url/home/article/detail,解析后的模块为 /home,控制器为 articleActiondetail,那么执行的 Action 就是文件 src/home/controller/aritcle 里的 detailAction 方法。

模型实例化

  • 在控制器中可以通过 this.model 方法快速获得一个模型的实例。
    1
    2
    3
    4
    5
    6
    export default class extends think.controller.base {
    indexAction(){
    let model = this.model('user'); //实例化模型 user
    ...
    }
    }

this 作用域的问题

  • Node.js 里经常有很多异步操作,而异步操作常见的处理方式是使用回调函数或者 Promise。这些处理方式都会增加一层作用域,导致在回调函数内无法直接使用 this,简单的处理办法是在顶部定义一个变量,将 this 赋值给这个变量,然后在回调函数内使用这个变量。如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    module.exports = think.controller({
    indexAction(){
    var self = this; //这里将 this 赋值给变量 self,然后在后面的回调函数里都使用 self
    this.model('user').find().then(function(data){
    return self.model('article').where({user_id: data.id}).select();
    }).then(function(data){
    self.success(data);
    })
    }
    })

JSON 输出

  • 项目中经常要提供一些接口,这些接口一般都是直接输出 JSON 格式的数据,并且会有标识表明当前接口是否正常。如果发生异常,需要将对应的错误信息随着接口一起输出。控制器里提供了 this.successthis.fail 方法来输出这样的接口数据。
输出正常的 JSON
  • 可以通过 this.success 方法输出正常的接口数据,如:

    1
    2
    3
    4
    5
    6
    export default class extends think.controller.base {
    indexAction(){
    let data = {name: "thinkjs"};
    this.success(data);
    }
    }
  • 输出结果为 {errno: 0, errmsg: “”, data: {“name”: “thinkjs”}},客户端可以通过 errno 是否为 0 来判断当前接口是否有异常。

输出含有错误信息的 JSON
  • 可以通过 this.fail 方法输出含有错误信息的接口数据,如:

    1
    2
    3
    4
    5
    export default class extends think.controller.base {
    indexAction(){
    this.fail(1000, 'connect error'); //指定错误号和错误信息
    }
    }
  • 输出结果为 {errno: 1000, errmsg: “connect error”},客户端判断 errno 大于 0,就知道当前接口有异常,并且通过 errmsg 拿到具体的错误信息。

配置错误号和错误信息
  • 如果每个地方输出错误的时候都要指定错误号和错误信息势必比较麻烦,比较好的方式是把错误号和错误信息在一个地方配置,然后输出的时候只要指定错误号,错误信息根据错误号自动读取。
  • 错误信息支持国际化,所以配置放在 src/common/config/locale/[lang].js 文件中。如:

    1
    2
    3
    export default {
    10001: 'get data error'
    }
  • 通过上面的配置后,执行 this.fail(10001) 时会自动读取到对应的错误信息。

友好的错误号
  • 在程序里执行 this.fail(10001) 虽然能输出正确的错误号和错误信息,但人不能直观的看出来错误号对应的错误信息是什么。
  • 这时可以将 key 配置为大写字符串,值为错误号和错误信息。如:

    1
    2
    3
    export default {
    GET_DATA_ERROR: [1234, 'get data error'] //key 必须为大写字符或者下划线才有效
    }
  • 执行 this.fail(‘GET_DATA_ERROR’) 时也会自动取到对应的错误号和错误信息。

格式配置
  • 默认输出的错误号的 key 为 errno,错误信息的 key 为 errmsg。如果不满足需求的话,可以修改配置文件 src/common/config/error.js。
    1
    2
    3
    4
    export default {
    key: 'errno', //error number
    msg: 'errmsg', //error message
    }
输出不包含错误信息的 JSON
  • 如果输出的 JSON 数据里不想包含 errno 和 errmsg 的话,可以通过 this.json 方法输出 JSON。如:
    1
    2
    3
    4
    5
    export default class extends think.controller.base {
    indexAction(){
    this.json({name: 'thinkjs'});
    }
    }

常用功能

通过get方法获取GET参数
1
2
3
4
5
6
export default class extends think.controller.base {
indexAction(){
let name = this.get('name'); //获取一个参数值
let allParams = this.get(); //获取所有 GET 参数,返回值为对象
}
}
  • 如果参数不存在,那么值为空字符串。
通过post方法获取 POST 参数
1
2
3
4
5
6
export default class extends think.controller.base {
indexAction(){
let name = this.post('name'); //获取一个参数值
let allParams = this.post(); //获取所有 POST 参数,返回值为对象
}
}
  • 如果参数不存在,那么值为空字符串。
------ 本文结束 ------