閱讀90 返回首頁    go 汽車大全


ThinkJS 簡介

簡介

最近幾年,前端技術呈現出突飛勐進的發展,湧現出了一大批優秀的前端框架,今天給大家帶來的就是基於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 https://${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 21:34:50

  上一篇:go  考察數據科學家支持向量機(SVM)知識的25道題,快來測測吧
  下一篇:go  Spring Boot 整合 Thymeleaf 完整 Web 案例