首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThinkJS 简介

ThinkJS 简介

作者头像
xiangzhihong
发布2018-02-06 19:43:05
2.8K0
发布2018-02-06 19:43:05
举报
文章被收录于专栏:向治洪向治洪

简介

最近几年,前端技术呈现出突飞猛进的发展,涌现出了一大批优秀的前端框架,今天给大家带来的就是基于node的一款优秀的优秀的前端框架。之前一直用Express来搭建比较简单的Node应用,但是对于相对复杂的应用来说,Express还是太轻量了。而作为一款优秀的国产前端框架,ThinkJS整合了大量的项目最佳实践,让企业级开发变得更加简单、高效。从 3.0 开始,框架底层基于 Koa 2.x 实现,兼容 Koa 的所有功能。在最新的版本中,ThinkJS全面支持ES6和ES7的语法规则。

特性

  • 基于 Koa 2.x,兼容 middleware
  • 内核小巧,支持 Extend、Adapter 等插件方式
  • 性能优异,单元测试覆盖程度高
  • 内置自动编译、自动更新机制,方便快速开发
  • 使用更优雅的 async/await 处理异步问题,不再支持 */yield
  • 从 3.2 开始支持 TypeScript

架构模型

ThinkJS的架构模型如下:

这里写图片描述
这里写图片描述

环境搭建

借助 ThinkJS 提供的脚手架,可以快速的创建一个项目。需要注意的是使用ThinkJS框架需要Node 6.x以上环境的支持。大家ThinkJS环境需要用到如下的步骤:

安装 ThinkJS 命令

npm install -g think-cli

安装完成后,系统中会有 thinkjs 命令(可以通过 thinkjs -v 查看 think-cli 的版本号,此版本号非 thinkjs 的版本号)。 注:如果是从 2.x 升级,需要将之前的命令删除,然后重新安装。

卸载旧版本命令

npm uninstall -g thinkjs

创建项目

执行 命令“thinkjs new [project_name]” 来创建项目,如:

$ thinkjs new demo;
$ cd demo;
$ npm install; 
$ npm start;
这里写图片描述
这里写图片描述

运行后可以看到相关的命令。

项目结构

默认创建的项目结构如下:

|--- development.js   //开发环境下的入口文件
|--- nginx.conf  //nginx 配置文件
|--- package.json
|--- pm2.json //pm2 配置文件
|--- production.js //生产环境下的入口文件
|--- README.md
|--- src
| |--- bootstrap  //启动自动执行目录 
| | |--- master.js //Master 进程下自动执行
| | |--- worker.js //Worker 进程下自动执行
| |--- config  //配置文件目录
| | |--- adapter.js  // adapter 配置文件 
| | |--- config.js  // 默认配置文件 
| | |--- config.production.js  //生产环境下的默认配置文件,和 config.js 合并 
| | |--- extend.js  //extend 配置文件 
| | |--- middleware.js //middleware 配置文件 
| | |--- router.js //自定义路由配置文件
| |--- controller  //控制器目录 
| | |--- base.js
| | |--- index.js
| |--- logic //logic 目录
| | |--- index.js
| |--- model //模型目录
| | |--- index.js
|--- view  //模板目录
| |--- index_index.html
|--- www
| |--- static  //静态资源目录
| | |--- css
| | |--- img
| | |--- js

2.x 到3.x的变化

由于2.x到3.x对接口改动较大,所以建议直接删除掉之前的版本,然后重新安装信息的版本。2.x到3.x变化的内容有:

核心变化

3.0 抛弃了 2.x 的核心架构,基于 Koa 2.x 版本构建,兼容 Koa 里的所有功能。主要变化为:

  • 之前的 http 对象改为 ctx 对象
  • 执行完全改为调用 middleware 来完成
  • 框架内置的很多功能不再默认内置,可以通过扩展来支持

启动方式

2.x 中项目启动时,会自动加载 src/bootstrap/ 目录下的所有文件。3.0 中不再自动加载所有的文件,而是改为(分为两种情况):

  • 在 Master 进程中加载 src/boostrap/master.js 文件;
  • 在 Worker 进程中加载 src/boostrap/worker.js 文件;

配置

2.x 中会自动加载 src/config/ 目录下的所有文件,3.0 中改为根据功能加载对应的文件。

hook 和 middleware

移除 2.x 里的 hook 和 middleware,改为 Koa 里的 middleware,middleware 的管理放在 src/config/middleware.js 配置文件中。2.x 下的 middleware 类无法在 3.0 下使用,3.0 下可以直接使用 Koa 的 middleware。

Controller

将基类 think.controller.base 改为 think.Controller,并移除 think.controller.rest 类。

Model

将基类 think.model.base 改为 think.Model。

View

模板的配置由原来的 src/common/config/view.js 迁移至 src/config/config.js 中,配置方法和之前基本一致。

其中老版本的 preRender() 方法已经废弃,新方法名为 beforeRender()。nunjucks 模板引擎的参数顺序由原来的 preRender(nunjucks, env, config) 修改为 beforeRender(env, nunjucks, config)。

阻止代码执行

在新的语法规则中,为了实现阻止某些代码的执行,对原来的语法进行了调整。移除了 think.prevent 等阻止后续执行的方法,替换为在 __before、xxxAction、__after 中返回 false 来阻止后续代码继续执行。

注:由于 3.0 改动了很多东西,所以不太容易基于原有项目代码简单修改来升级。建议使用新的脚手架工具创建项目,然后一一将之前的代码拷贝到新项目中进行修改。

基础概念

在介绍ThinkJS框架运行流程之前,我们先来看几个比较重要的概念:

Context

Context,在大多数的编程语言中,被称为上下文,也就是对象关联关系的。Context 是 Koa 中处理用户请求中的一个对象,贯穿整个请求生命周期。一般在 middleware、controller、logic 中使用,简称为 ctx。 例如,在 middleware 中使用 ctx 对象。

module.exports = options => {
  // 调用时 ctx 会作为第一个参数传递进来
  return (ctx, next) => {
    ...
  }
}

在 controller 中使用 ctx 对象。

module.exports = class extends think.Controller {
  indexAction() {
    // controller 中 ctx 作为类的属性存在,属性名为 ctx
    // controller 实例化时会自动把 ctx 传递进来
    const ip = this.ctx.ip;
  }
}

ThinkJS框架继承了Koa的一些属性和方法,并通过 Extend 机制扩展了很多非常有用的属性和方法。

Koa 内置 API

ctx.req:Node 的 request 对象。 ctx.res:Node 的 response 对象,不支持 绕开 Koa 对 response 的处理。 ctx.request:Koa 的 Request 对象。 ctx.response:Koa 的 Response 对象。 ctx.state:在中间件之间传递信息以及将信息发送给模板时,推荐的命名空间。避免直接在 ctx 上加属性,这样可能会覆盖掉已有的属性,导致出现奇怪的问题。例如:

ctx.state.user = await User.find(id);

这样后续在 controller 里可以通过 this.ctx.state.user 来获取对应的值。

module.exports = class extends think.Controller {
  indexAction() {
    const user = this.ctx.state.user;
  }
}

关于更多的介绍,读者可以访问ThinkJS 上下文介绍

Middleware

Middleware,又称为中间件,是 Koa 中一个非常重要的概念,利用中间件,可以很方便的处理用户的请求。由于 ThinkJS 3.0 是基于 Koa@2 版本之上构建的,所以完全兼容 Koa 里的中间件。

使用中间件的格式如下:

module.exports = options => {
  return (ctx, next) => {
    // do something
  }
}

中间件格式为一个高阶函数,外部的函数接收一个 options 参数,这样方便中间件提供一些配置信息,用来开启/关闭一些功能。执行后返回另一个函数,这个函数接收 ctx, next 参数,其中 ctx 为 context 的简写,是当前请求生命周期的一个对象,存储了当前请求的一些相关信息,next 为调用后续的中间件,返回值是 Promise,这样可以很方便的处理后置逻辑。相信做过异步程序开发的同学对这个不会陌生。这种中间件格式是常见的洋葱头模型。

这里写图片描述
这里写图片描述

例如,官方提供的一个例子:

const defaultOptions = {
  consoleExecTime: true // 是否打印执行时间的配置
}
module.exports = (options = {}) => {
  // 合并传递进来的配置
  options = Object.assign({}, defaultOptions, options);
  return (ctx, next) => {
    if(!options.consoleExecTime) {
      return next(); // 如果不需要打印执行时间,直接调用后续执行逻辑
    }
    const startTime = Date.now();
    let err = null;
    // 调用 next 统计后续执行逻辑的所有时间
    return next().catch(e => {
      err = e; // 这里先将错误保存在一个错误对象上,方便统计出错情况下的执行时间
    }).then(() => {
      const endTime = Date.now();
      console.log(`request exec time: ${endTime - startTime}ms`);
      if(err) return Promise.reject(err); // 如果后续执行逻辑有错误,则将错误返回
    })
  }
}

在 Koa 中,可以通过调用 app.use 的方式来使用中间件。

const app = new Koa();
const execTime = require('koa-execTime'); // 引入统计执行时间的模块
app.use(execTime({}));  // 需要将这个中间件第一个注册,如果还有其他中间件放在后面注册

扩展 app 参数

默认的中间件外层一般只是传递了 options 参数,有的中间件需要读取 app 相关的信息,框架在这块做了扩展,自动将 app 对象传递到中间件中。

module.exports = (options, app) => {
  // 这里的 app 为 think.app 对象
  return (ctx, next) => {

  }
}

如果在中间件中需要用到 think 对象上的一些属性或者方法,那么可以通过 app.think.xxx 来获取。

配置格式

为了方便管理和使用中间件,框架使用统一的配置文件来管理中间件,配置文件为 src/config/middleware.js(多模块项目配置文件为 sr/common/config/middleware.js)。

const path = require('path')
const isDev = think.env === 'development'

module.exports = [
  {
    handle: 'meta', // 中间件处理函数
    options: {   // 当前中间件需要的配置
      logRequest: isDev,
      sendResponseTime: isDev,
    },
  },
  {
    handle: 'resource',
    enable: isDev, // 是否开启当前中间件
    options: {
      root: path.join(think.ROOT_PATH, 'www'),
      publicPath: /^\/(static|favicon\.ico)/,
    },
  }
]

配置项为项目中要使用的中间件列表,每一项支持 handle,enable,options,match 等属性。

handle

中间件的处理函数,可以用系统内置的,也可以是引入外部的,也可以是项目里的中间件。handle 的函数格式为:

module.exports = (options, app) => {
  return (ctx, next) => {

  }
}

这里中间件接收的参数除了 options 外,还多了个 app 对象,该对象为 Koa Application 的实例。

enable

是否开启当前的中间件,比如:某个中间件只在开发环境下才生效。

{
  handle: 'resouce',
  enable: think.env === 'development' //这个中间件只在开发环境下生效
}

options

传递给中间件的配置项,格式为一个对象,中间件里获取到这个配置。

module.exports = [
  {
    options: {
      key: value
    } 
  }
]

有时候需要的配置项需要从远程获取,如:配置值保存在数据库中,这时候就要异步从数据库中获取,这时候可以将 options 定义为一个函数来完成:

module.exports = [
  {
    // 将 options 定义为一个异步函数,将获取到的配置返回
    options: async () => {
      const config = await getConfigFromDb();
      return {
        key: config.key,
        value: config.value
      }
    }
  }
]

match

匹配特定的规则后才执行该中间件,支持二种方式,一种是路径匹配,一种是自定义函数匹配。如:

module.exports = [
  {
    handle: 'xxx-middleware',
    match: '/resource' //请求的 URL 是 /resource 打头时才生效这个 middleware
  }
]

或者:

module.exports = [
  {
    handle: 'xxx-middleware',
    match: ctx => { // match 为一个函数,将 ctx 传递给这个函数,如果返回结果为 true,则启用该 middleware
      return true;
    }
  }
]

框架内置的中间件

框架内置了几个中间件,可以通过字符串的方式直接引用。常见的有:

  • meta 显示一些 meta 信息,如:发送 ThinkJS 的版本号,接口的处理时间等等
  • resource 处理静态资源,生产环境建议关闭,直接用 webserver 处理即可。
  • trace 处理报错,开发环境将详细的报错信息显示处理,也可以自定义显示错误页面。
  • payload 处理表单提交和文件上传,类似于 koa-bodyparser 等 middleware
  • router 路由解析,包含自定义路由解析
  • logic logic 调用,数据校验
  • controller controller 和 action 调用

自定义的中间件

在项目开发中,有时候需要根据一些特定需要添加中间件,那么我们可以自定义一些中间件,放在src/middleware目录下。例如:

module.exports = [
  {
    handle: 'csrf',
    options: {}
  }
]

引入外部的中间件非常简单,只需要 require 进来即可。这和JSX的语法是一样的。

const csrf = require('csrf'); 
module.exports = [
  ...,
  {
    handle: csrf,
    options: {}
  },
  ...
]

Router

Router,及路由,用来进行页面跳转的,用户访问一个地址时,需要有一个对应的逻辑进行处理。传统的处理方式下,一个请求对应的一个文件,如访问时 /user/about.php,那么就会在项目对应的目录下有 /user/about.php 这个实体文件。这种方式虽然能解决问题,但会导致文件很多,同时可能很多文件里逻辑功能其实比较简单。

在 MVC 开发模型里,一般都是通过路由来解决此类问题。由于 Node.js 是自己启动 HTTP(S) 服务的,所以已经天然将用户的请求汇总到一个入口了,这样处理路由映射就更简单了。

在 ThinkJS 中,当用户访问一个 URL 时,最后是通过 controller 里具体的 action 来响应的。所以就需要解析出 URL 对应的 controller 和 action,这个解析工作是通过 think-router 模块实现的。

路由配置

think-router 是一个 middleware,项目创建时默认已经加到配置文件 src/config/middleware.js 里了,其中 options 支持如下的参数:

  • defaultModule {String} 多模块项目下,默认的模块名。默认值为 home
  • defaultController {String} 默认的控制器名,默认值为 index
  • defaultAction {String} 默认的操作名,默认值为 index
  • prefix {Array} 默认去除的 pathname 前缀,默认值为 []
  • suffix {Array} 默认去除的 pathname 后缀,默认值为 [‘.html’]
  • enableDefaultRouter {Boolean} 在不匹配情况下是否使用默认路由解析,默认值为 true
  • subdomainOffset {Number} 子域名映射下的偏移量,默认值为 2
  • subdomain {Object|Array} 子域名映射列表,默认为 {}
  • denyModules {Array} 多模块项目下,禁止访问的模块列表,默认为 []

例如,下面是项目中的配置:

module.exports = [
  {
    handle: 'router',
    options: {
      defaultModule: 'home',
      defaultController: 'index',
      defaultAction: 'index',
      prefix: [],
      suffix: ['.html'],
      enableDefaultRouter: true,
      subdomainOffset: 2,
      subdomain: {},
      denyModules: []
    }
  }
];

路径预处理

当用户访问服务时,通过 ctx.url 属性,可以得到初始的 pathname,如:访问本页面 https://www.thinkjs.org/zh-cn/doc/3.0/router.html,初始 pathname 为 /zh-cn/doc/3.0/router.html。为了方便后续通过 pathname 解析出对应的 controller 和 action,需要对 pathname 进行预处理。

prefix & suffix

有时候为了搜索引擎优化或者一些其他的原因,URL 上会多加一些东西。比如:当前页面是一个动态页面,为了 SEO,会在 URL 后面加上 .html 后缀假装页面是一个静态页面,但 .html 对于路由解析来说是无用的,是要去除的。 prefix 与 subffix 为数组,数组的每一项可以为字符串或者正则表达式, 在匹配到第一个之后停止后续匹配。在ThinkJS中prefix的格式如下:

{
  prefix: [],
  suffix: ['.html'],
}

路由解析

通过 prefix & suffix 和 subdomain 预处理后,得到真正后续要解析的 pathname。默认的路由解析规则为 /controller/action,如果是多模块项目,那么规则为 /module/controller/action,根据这个规则解析出对应的 module、controller、action 值。

如果 controller 有子级,那么会优先匹配子级 controller,然后再匹配 action。常见的匹配有:

pathname

项目类型

子级控制器

module

controller

action

备注

/

单模块

index

index

controller、action 为配置的默认值

/user

单模块

user

index

action 为配置的默认值

/user/login

单模块

user

login

/console/user/login

单模块

console/user

login

有子级控制器 console/user

/console/user/login/aaa/bbb

单模块

console/user

login

剩余的 aaa/bbb 不再解析

/admin/user

多模块

admin

user

index

多模块项目,有名为 admin 的模块

/admin/console/user/login

多模块

admin

console/user

login

解析后的 module、controller、action 分别放在 ctx.module、ctx.controller、ctx.action 上,方便后续调用处理。如果不想要默认的路由解析,那么可以通过配置 enableDefaultRouter: false 关闭。

自定义路由规则

虽然默认的路由解析方式能够满足需求,但有时候会导致 URL 看起来不够优雅,我们更希望 URL 比较简短,这样会更利于记忆和传播。框架提供了自定义路由来处理这种需求。 自定义路由规则配置文件为 src/config/router.js(多模块项目放在 src/common/config/router.js),路由规则为二维数组:

module.exports = [
  [/libs\/(.*)/i, '/libs/:1', 'get'],
  [/fonts\/(.*)/i, '/fonts/:1', 'get,post'],
];

每一条路由规则也为一个数组,数组里面的项分别对应为:match、pathname、method、options:

  • match {String | RegExp} pathname 匹配规则,可以是字符串或者正则。如果是字符串,那么会通过path-to-regexp 模块转为正则
  • pathname {String} 匹配后映射后的 pathname,后续会根据这个映射的 pathname 解析出对应的controller、action
  • method {String} 该条路由规则支持的请求类型,默认为所有。多个请求类型中间用逗号隔开,如:get,post
  • options {Object} 额外的选项,如:跳转时指定 statusCode

Adapter

Adapter,适配器,用来解决一类功能的多种实现,这些实现提供一套相同的接口,类似设计模式里的工厂模式。如:支持多种数据库,支持多种模版引擎等。Adapter 一般配合 Extend 一起使用。

框架默认提供了很多种 Adapter,如: View、Model、Cache、Session、Websocket,项目中也可以根据需要进行扩展,也可以引入第三方的 Adapter。

Adapter 都是一类功能的不同实现,一般是不能独立使用的,而是配合对应的扩展一起使用。如:view Adapter(think-view-nunjucks、think-view-ejs)配合 think-view 扩展进行使用。

项目安装 think-view 扩展后,提供了对应的方法来渲染模板,但渲染不同的模板需要的模板引擎有对应的 Adapter 来实现,也就是配置中的 handle 字段。

Adapter 配置

Adapter 的配置文件为 src/config/adapter.js(多模块项目文件为 src/common/config/adapter.js),格式如下:

const nunjucks = require('think-view-nunjucks');
const ejs = require('think-view-ejs');
const path = require('path');

exports.view = {
  type: 'nunjucks', // 默认的模板引擎为 nunjucks
  common: { //通用配置
    viewPath: path.join(think.ROOT_PATH, 'view'),
    sep: '_',
    extname: '.html'
  },
  nunjucks: { // nunjucks 的具体配置
    handle: nunjucks
  },
  ejs: { // ejs 的具体配置
    handle: ejs,
    viewPath: path.join(think.ROOT_PATH, 'view/ejs/'),
  }
}

exports.cache = {
  ...
}
  • type 默认使用 Adapter 的类型,具体调用时可以传递参数改写
  • common 配置通用的一些参数,项目启动时会跟具体的 adapter 参数作合并
  • nunjucks ejs 配置特定类型的 Adapter 参数,最终获取到的参数是 common 参数与该参数进行合并
  • handle 对应类型的处理函数,一般为一个类

Adapter 配置支持运行环境,可以根据不同的运行环境设置不同的配置,如:在开发环境和生产环境的数据库一般都是不一样的,这时候可以通过 adapter.development.js 和 adapter.production.js 存放有差异的配置,系统启动后会读取对应的运行环境配置和默认配置进行合并。

Adapter 配置解析

Adapter 配置存储了所有类型下的详细配置,具体使用时需要对其解析,选择对应的一种进行使用。比如上面的配置文件中,配置了 nunjucks 和 ejs 二种模板引擎的详细配置,但具体使用时一种场景下肯定只会用其一种模板引擎。Adapter 的配置解析是通过 think-helper 模块中的 parseAdapterConfig 方法来完成的,如:

const helper = require('think-helper');
const viewConfig = think.config('view'); // 获取 view adapter 的详细配置

const nunjucks = helper.parseAdatperConfig(viewConfig); // 获取 nunjucks 的配置,默认 type 为 nunjucks
/**
{
  type: 'nunjucks',
  handle: nunjucks,
  viewPath: path.join(think.ROOT_PATH, 'view'),
  sep: '_',
  extname: '.html'
}
*/

const ejs = helper.parseAdatperConfig(viewConfig, 'ejs') // 获取 ejs 的配置
/**
{
  handle: ejs,
  type: 'ejs',
  viewPath: path.join(think.ROOT_PATH, 'view/ejs/'),
  viewPath: path.join(think.ROOT_PATH, 'view'),
  sep: '_',
  extname: '.html'
}
*/

通过 parseAdapterConfig 方法就可以拿到对应类型的配置,然后就可以调用对应的 handle,传入配置然后执行了。当然,配置解析并不需要使用者在项目中具体调用,一般都是在插件对应的方法里已经处理。

Adapter使用

除了引入外部的 Adapter 外,项目内也可以创建 Adapter 来使用。Adapter 文件放在 src/adapter/ 目录下(多模块项目放在 src/common/adapter/),如:src/adapter/cache/xcache.js,表示加了一个名为 xcache 的 cache Adapter 类型,然后该文件实现 cache 类型一样的接口即可。

exports.cache = {
  type: 'file',
  xcache: {
    handle: 'xcache', //这里配置字符串,项目启动时会自动查找 src/adapter/cache/xcache.js 文件
    ...
  }
}

Extend

虽然框架内置了很多功能,但在实际项目开发中,提供的功能还是远远不够的。3.0 里引入了扩展机制,方便对框架进行扩展。支持的扩展类型为:think、application、context、request、response、controller、logic 和 service。框架内置的很多功能也是扩展来实现的,如:Session、Cache。

扩展配置

扩展配置文件路径为 src/config/extend.js(多模块项目文件路径为 src/common/config/extend.js),格式为数组:

const view = require('think-view');

module.exports = [
  view //make application support view
];

除了引入外部的 Extend 来丰富框架的功能,也可以在项目中对对象进行扩展,扩展文件放在 src/extend/ 目录下(多模块项目放在 src/common/extend/ 下)。

  • src/extend/think.js 扩展 think 对象,think.xxx
  • src/extend/application.js 扩展 Koa 里的 app 对象(think.app)
  • src/extend/request.js 扩展 Koa 里的 request 对象(think.app.request)
  • src/extend/response.js 扩展 Koa 里的 response 对象(think.app.response)
  • src/extend/context.js 扩展 ctx 对象(think.app.context)
  • src/extend/controller.js 扩展 controller 类(think.Controller)
  • src/extend/logic.js 扩展 logic 类(think.Logic)- logic 继承
  • controller 类,所以 logic 包含 controller 类所有方法
  • src/extend/service.js 扩展 service 类(think.Service)

比如:我们想给 ctx 添加个 isMobile 方法来判断当前请求是不是手机访问:

module.exports = {
  isMobile(){
    const userAgent = this.userAgent.toLowerCase();
    const mList = ['iphone', 'android'];
    return mList.some(item => userAgent.indexOf(item) > -1);
  }
}

然后使用ctx.isMobile() 来判断是否是手机访问。当然这个方法没有任何的参数,我们也可以变成一个 getter。

module.exports = {
  get isMobile(){
    const userAgent = this.userAgent.toLowerCase();
    const mList = ['iphone', 'android'];
    return mList.some(item => userAgent.indexOf(item) > -1);
  }
}

如果在 controller 中也想通过 this.isMobile 使用,怎么办呢? 可以给 controller 也扩展一个 isMobile 属性来完成。

module.exports = {
  get isMobile(){
    return this.ctx.isMobile;
  }
}

不过这种方式只能在本模块使用,如果要在其他项目中使用,可以将这些扩展发布为一个 npm 模块。这和React的写法是一样的。

const controllerExtend = require('./controller.js');
const contextExtend = require('./context.js');

// 模块入口文件
module.exports = {
  controller: controllerExtend,
  context: contextExtend
}

扩展里使用 app 对象

有些 Extend 需要使用一些 app 对象上的数据,那么可以导出为一个函数,配置时把 app 对象传递进去即可。

const model = require('think-model');
module.exports = [
  model(think.app) //将 think.app 传递给 model 扩展
];

运行流程

前面说了这么多基本的概念,现在再来看一下ThinkJS的运行流程。 Node.js 提供了 http 模块直接创建 HTTP 服务,用来响应用户的请求,比如 Node.js 官网提供的创建 HTTP 服务的例子:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

ThinkJS 也是调用 http.createServer 的方式来创建服务的,所以整个运行流程包含了启动服务和响应用户请求两个部分。

系统服务

  1. 执行 npm start 或者 node development.js;
  2. 实例化 ThinkJS 里的 Application 类,执行 run 方法。
  3. 根据不同的环境(Master 进程、Worker 进程、命令行调用)处理不同的逻辑。
  4. 如果是 Master 进程。加载配置文件,生成 think.config 和 think.logger 对象。 1)加载文件 src/bootstrap/master.js 文件 2)如果配置文件监听服务,那么开始监听文件的变化,目录为 src/。 3)文件修改后,如果配置文件编译服务,那么会对文件进行编译,编译到 app/ 目录下。 4)根据配置 workers 来 fork 对应数目的 Worker。Worker 进程启动完成后,触发 appReady 事件。(可以通过 think.app.on(“appReady”) 来捕获) 5)如果文件发生了新的修改,那么会触发编译,然后杀掉所有的 Worker 进程并重新 fork。
  5. 如果是 Worker 进程 1)加载配置文件,生成 think.config 和 think.logger 对象。 2)加载 Extend,为框架提供更多的功能,配置文件为 src/config/extend.js。 3)获取当前项目的模块列表,放在 think.app.modules 上,如果为单模块,那么值为空数组。 4)加载项目里的 controller 文件(src/controller/*.js),放在 think.app.controllers 对象上。 5)加载项目里的 logic 文件(src/logic/*.js),放在 think.app.logics 对象上。 6)加载项目里的 model 文件(src/model/*.js),放在 think.app.models 对象上。 7)加载项目里的 service 文件(src/service/*.js),放在 think.app.services 对象上。 8)加载路由配置文件 src/config/router.js,放在 think.app.routers 对象上。 9)加载校验配置文件 src/config/validator.js,放在 think.app.validators 对象上。 10)加载 middleware 配置文件 src/config/middleware.js,并通过 think.app.use 方法注册。 11)加载定时任务配置文件 src/config/crontab.js,并注册定时任务服务。 12)加载 src/bootstrap/worker.js 启动文件。 13)监听 process 里的 onUncaughtException 和 onUnhandledRejection 错误事件,并进行处理。可以在配置 src/config.js 自定义这二个错误的处理函数。 14)等待 think.beforeStartServer 注册的启动前处理函数执行,这里可以注册一些服务启动前的事务处理。 15)如果自定义了创建服务配置 createServer,那么执行这个函数 createServer(port, host, callback) 来创建服务;如果没有自定义,则通过 think.app.listen 来启动服务。 16)服务启动完成时,触发 appReady 事件,其他地方可以通过 think.app.on(“appReady”) 监听;创建的服务赋值给 think.app.server 对象。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
    • 特性
      • 架构模型
      • 环境搭建
        • 安装 ThinkJS 命令
          • 卸载旧版本命令
            • 创建项目
              • 项目结构
              • 2.x 到3.x的变化
                • 核心变化
                  • 启动方式
                    • 配置
                      • hook 和 middleware
                      • Controller
                      • Model
                      • View
                    • 阻止代码执行
                    • 基础概念
                      • Context
                        • Koa 内置 API
                          • Middleware
                            • 扩展 app 参数
                              • 配置格式
                                • handle
                                  • enable
                                    • options
                                      • match
                                        • 框架内置的中间件
                                          • 自定义的中间件
                                          • Router
                                            • 路由配置
                                              • 路径预处理
                                                • prefix & suffix
                                                  • 路由解析
                                                    • 自定义路由规则
                                                • Adapter
                                                  • Adapter 配置
                                                    • Adapter 配置解析
                                                      • Adapter使用
                                                      • Extend
                                                        • 扩展配置
                                                          • 扩展里使用 app 对象
                                                          • 运行流程
                                                            • 系统服务
                                                            相关产品与服务
                                                            消息队列 TDMQ
                                                            消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                                                            领券
                                                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档