AdonisJS 入門篇

參考資料

前言

本篇文章僅對 AdonisJS 框架作簡要介紹及簡單語法提及,詳細(xì)語法跟配置請參考官方文檔。本篇的目的在于看完之后能夠大體了解 AdonisJS 的架構(gòu),并實現(xiàn)簡單的功能。

零、簡單介紹及安裝

1.Introduce

  • AdonisJS 官方介紹是 MVC Framework for Node.js to write webapps with less code,簡單的說就是一個 服務(wù)端渲染 的 MVC 框架。如果熟悉 Laravel 框架(PHP 框架),你會發(fā)現(xiàn)這是 Laravel 的一個 NodeJS 版本。
  • 優(yōu)點:AdonisJS 作為一個服務(wù)端渲染的框架,能夠解決很多現(xiàn)在流行的前端框架無法實行的問題(比如兼容 IE8 等)

2.Install

  • 既然 AdonisJS 是依賴于 NodeJS,那么安裝自然離不開 NodeJS 和 npm。官方指定版本(當(dāng)然,官方也建議 npm 版本 >= 6.1.x):
node 版本
npm 版本
  • 創(chuàng)建 Adonis project 很簡單,因為 Adonis 本身就是一個 npm package,可以運(yùn)行以下代碼啟動一個示例 project:
npm i -g adonis-cli           // 全局安裝 adois
adonis new awesome-project    // 當(dāng)前目錄下創(chuàng)建新工程
cd awesome-project    
// 然后你需要把 .env-example 文件改名為 .env
npm run serve:dev    // 啟動 project

3.Directory structure

文件夾結(jié)構(gòu)圖

一、應(yīng)用生命周期

1.Http Server

  • Providers 和它們的參數(shù)被定義在 bootstrap/app.js
  • 自定義 Provider 在 providers/, provider 文件設(shè)置注冊信息(注冊為異步注冊 ES2015 Generator)及依賴的模塊
  • 設(shè)置參數(shù)是為了方便引用 Provider 時書寫
// bootstrap/app.js
const provider = []
const aceProviders = []
const aliases = []
const commads = []
  • bootstrap/http.js 文件引用 Providers 并注冊在 IoC container
// bootstrap/http.js

// 引用 node_modules 提供的 providers
const app = require('./app')
const fold = require('adonis-fold')
const path = require('path')
const packageFile = path.join(__dirname, '../package.json')
require('./extend')
// 注冊
module.exports = function (callback) {
    ...
}
  • 注冊定義在 package.json 自動加載路徑
// 可以在此配置
{
    "autoload": {
        "App": "./app"
    }
}
  • 根據(jù) bootstrap/http.js 定義的結(jié)果繼續(xù)加載以下文件:

  • bootstrap/event.js: 注冊監(jiān)聽事件

  • app/Http/kernel.js: Http server & 中間件

  • app/Http/routes.js: Http server & 路由

  • database/factory.js: Model 管理

  • 最后,啟動 HTTP server 監(jiān)聽定義在 .env 文件的 hostport

  • 啟動 HTTP server 之前,AdonisJS 提供了 Http.onStart 鉤子,可用來自定義全局變量,如 View 的 全局變量過濾器 等。

2.Http Request

  • HTTP request 是在給定時間處理一個或多個 HTTP請求的 動態(tài)流。

  • 根據(jù) public 文件夾里邊的靜態(tài)文件檢查傳入的請求 URL ,如果存在則提供靜態(tài)文件。

  • 接下來,將根據(jù)請求的 URL ,搜索相應(yīng)的路由,并且執(zhí)行以下步驟:

  • 啟動所有全局中間件

  • 啟動所有路由中間件

  • 執(zhí)行路由操作(可以是 控制器方法 或者 閉包

  • 如果以上兩個步驟出錯,將會拋出一個 404 HttpException??梢栽?APP/httP.js 設(shè)置 error 時的 sendView(),以提高用戶體驗。

二、MVC 設(shè)計模式

本部分僅對 MVC 模式作簡要介紹,后續(xù)將進(jìn)一步介紹 View 和 Controller。

1.概述

  • 根據(jù)上節(jié)生命周期的說明,我們大致可以知道整個架構(gòu)的數(shù)據(jù)流程如果下圖所示:
MVC 設(shè)計模式示意圖
  • 這便是我們常說的 MVC 設(shè)計模式。MVC 模式將應(yīng)用分離成模型(Model),視圖(View)和控制器(Controller)。AdonisJS 支持這三者且使其結(jié)合起來非常簡單。而且,Adonis Router 也在處理 HTTP 請求并將其傳遞給控制器中起到重要作用。

2.Model

  • Model 是從數(shù)據(jù)庫(AdonisJS 使用的是 SQL)獲取數(shù)據(jù)的數(shù)據(jù)層,為了使獲取數(shù)據(jù)的過程簡單和安全,Adonis 使用了很好的 ORM 架構(gòu)(Lucid)。

3.Controller

  • 控制器主要控制 HTTP 請求數(shù)據(jù)流,利用 Model 獲取必要的數(shù)據(jù)(不僅數(shù)據(jù)庫,第三方服務(wù)器的數(shù)據(jù)等都在控制器里邊處理),并把獲取的數(shù)據(jù)傳遞給 View 去渲染 HTML 頁面。

4.View

  • View 是整個 MVC 模式數(shù)據(jù)流的最后部分,主要是利用動態(tài)的數(shù)據(jù)(Nunjuck Template)去渲染(jinja 引擎) HTML 頁面。

三、Rounting

本節(jié)簡單介紹路由,以及常用的幾個語法

  • 根據(jù)上節(jié)的數(shù)據(jù)流程所講,接下來我們先介紹路由。路由的作用便是將瀏覽器的 HTTP 請求傳遞到相應(yīng)的控制器中。當(dāng)然,你也可以直接在路由部分作簡單的邏輯處理,不過在這里比較建議將邏輯處理劃分到 控制器 中,方便代碼的管理。

  • 路由部分的代碼書寫在 app/Http/routes.js 中,簡單的例子如下:

const Route = use('Route')    // 類似 include,跟 require 不同

Route.get('/', function * (request, response) {
    yield response.sendView('home')    // 這里使用 ES6 generator 語法達(dá)到異步請求數(shù)據(jù)的效果
})
  • 路由參數(shù):路由參數(shù)是 URL 中的動態(tài)數(shù)據(jù)段,我們可以通過自定義的 URL 接受動態(tài)數(shù)據(jù)。如下例子:
Route.get('users/:id', function * (request, response) {
    const id = request.param('id')
    response.send('Profile for user with id ${id}')
})
  • 路由群:通常,會存在一系列路由共享同一個模塊的情況,這就可能有時候需要修改這個模塊的一些共同參數(shù),我們把這些路由“集合”到一起,就方便修改了。例如下面的例子:
Route.group('version1', function () {
    Route.get('users', function * (request, response) {  // 路由是 /api/v1/users
        // ...
    })
}).prefix('api/v1').middleware('auth')    // 該路由群都會加上 api/v1 的前綴,并經(jīng)過用戶登錄中間件的檢驗
  • 路由命名:書寫完整的路徑有時過于麻煩,為方便使用可以給路由命名。如下例子:
Route
    .get('users/:id', '與否.show')
    .as('profile') 

四、Controller

本節(jié)主要作控制器的簡要介紹,以及 request 和 response 的常用語法使用。最后簡單說說中間件。

1.概述

  • 控制器的作用正如前面所說,就是用來處理數(shù)據(jù)并傳遞數(shù)據(jù)給 View 渲染 HTML 頁面。處理的數(shù)據(jù)來源有來自瀏覽器的 HTTP 請求,也可以從數(shù)據(jù)庫獲取,也包括從第三方服務(wù)器請求的數(shù)據(jù)(需自行搭建服務(wù)器客戶端環(huán)境)??刂破鞯拇a結(jié)構(gòu)大致如下:
// 引入依賴文件
const path = require('path')

// 定義控制器
class UsersController {

  // 依賴注入
  static get inject () {
    return ['App/Model/User']   
  }

  // 以參數(shù)形式接收依賴注入
  constructor (User) { 
    this.User = User 
  }
  
  // 自定義數(shù)據(jù)處理方法
  static _processUserInfo() {
    // ...
  }
  
  // 異步調(diào)用的方法
  * index () {
    const users = yield this.User.all()
    // 傳遞數(shù)據(jù)給視圖渲染
    yield response.sendView('users',{
        userName,
        userInfo
    })
  }

}

module.exports = UserController

2.request

2.1 簡單示例

  • AdonisJS 中,為了方便接收 HTTP 請求信息,所有的控制器方法和路由器閉包都會接收一個 Request 實例 。請看一下簡單示例:
// 路由閉包中調(diào)用 request ,response
const Route = use('Route')

Route.get('post', function * (request, response) {
  const body = request.all()
   
  // cherry picking fields
  const body = request.only('title', 'description', 'categories')
})
// 控制器中調(diào)用 request, response
class IndexController {
  * index(requset, response) {
    // ...
  }
}

2.2 常用的 request 方法

  • request.all() :包括所有查詢字符串和請求體:
const data = request.all()
// example.html?id=1&name=John 
// <input name="gender" value="male" />
// => {"id":1, "name": "John", "gender": "male" }
  • request.only():跟 request.all() 類似,但是只包含定義的 key 值:
const data = request.only('name', 'email', 'age')
/* returns
{
  'name': '..',
  'email':'..',
  'age':'..'
}
*/
  • request.input(key,[defaultValue]):返回 input 標(biāo)簽對應(yīng)的鍵值對,如果 value 為空,則返回 defaultValue
const name = request.input('name')
const subscribe = request.input('subscribe', 'yes')
  • request.url() :獲取請求的 URL(出去查詢字符串):
// url - http://foo.com/users?orderBy=desc&limit=10
request.url()
// returns - http://foo.com/users
  • request.param(key, [defaultValue]) :返回查詢字符串:
// url - http://foo.com/users?orderBy=desc&limit=10
request.param('orderBy')
// returns - desc

// 返回所有的查詢字符串
request.params()
  • request.collect(key1, key2, ...):轉(zhuǎn)化數(shù)據(jù)格式,直接看下面例子:
<form method="POST" action="/users">
  <div class="row">
    <h2> User 1 </h2>
    <input type="email" name="email[]" />
    <input type="password" name="password[]" />
  </div>

  <div class="row">
    <h2> User 2 </h2>
    <input type="email" name="email[]" />
    <input type="password" name="password[]" />
  </div>

  <button type="submit"> Create Users </button>
</form>
request.only('email', 'password', )
/* returns 
{
  email: ['bar@foo.com', 'baz@foo.com'],
  password: ['secret', 'secret1']
}
*/
request.collect('email', 'password')
/* returns
[
  {
    email: 'bar@foo.com',
    password: 'secret'
  },
  {
    email: 'baz@foo.com',
    password: 'secret1'
  }
]
*/

3.response

  • 為了方便盡快響應(yīng) HTTP 請求,AdonisJS 提供了 response 類給我們使用??捎糜阡秩?nunjucks views 或者格式化數(shù)據(jù)。以下是簡單的示例代碼:
// Render Views
Route
  .get('/', function *() {
    yield response.sendView('welcome')    // resources/views/welcome.njk
})
// JSON Response
Route
  .get('user', function * () {
    const user = yield User.all()    // fetch users
    response.json(users)
})
  • 常用的還有重定向(Redirects)
response.location('/signup')
// or
response.location('back')

4.middleware

  • 中間件的使用十分常見,比如驗證用戶是否登錄。在 AdonisJS 中,中間件放在 app/Http/Middleware 文件夾下,以下是簡單示例:
'use strict'

const geoip = use('geoip-lite')  // npm module

class CountryDetector {
  
  * handle (request, response, next) {
    const ip = request.ip()
    request.country = geoip.lookup(ip).country
    yield next    // 如果想要把 req, res 傳遞給下個中間件或者路由行為,注意使用 yield next 
  }
}

五、View

本節(jié)簡要介紹 View 層以及 View 層常見的使用。

1.概述

  • 正如前邊的數(shù)據(jù)流圖所述,View 便是整個數(shù)據(jù)流程的末端,即利用前邊步驟傳來的數(shù)據(jù)進(jìn)行頁面渲染。AdonisJS 的視圖引擎是建立在 nunjucks 的基礎(chǔ)上的,所以 nunjucks 有的模板語法,在 AdonisJS 中一樣可以使用。另外,AdonisJS 也提供了一些特有的 過濾器(filter)。

  • 簡單示例:

// Route
const Route = use('Route')
Route.get('/greet/:user', 'UserController.greet')

// Controller
class UserController {
  * greet (request, response) {
     const user = request.param('user')  // user: "John"
     yield response.sendView('greet', {user})
  }
}
// View
<h2>Hello {{ user }}</h2>    // <h2>John</h2>

2.常見使用

  • 條件語法
{% if hungry %}
  I am hungry
{% else %}
  I am good
{% endif %}
{{ username | capitalize }}
// username = 'john' => output = John
  • 遍歷語法
{% for book in books %}
  book
{% endfor %}
  • 模板繼承
// resources/views/master.njk
<html>
  <body>

    <header class="header">
      {% block header %}
        Common Header
      {% endblock %}
    </header>

    <section class="sidebar">
      {% block sidebar %}
        Common Sidebar
      {% endblock %}
    </section>

    <section class="content">
      {% block content %}{% endblock %}
    </section>

  </body>
</html>
// resources/views/home.njk
{% extends 'master' %}

{% block content %}
  Here comes the content of the home page.
{% endblock %}
<html>
  <body>

    <header class="header">
      Common Header
    </header>

    <section class="sidebar">
      Common Sidebar
    </section>

    <section class="content">
      Here comes the content of the home page.
    </section>

  </body>
</html>
  • 引入
// resources/views/chat/message.njk
<div class="chat__message">
  <h2> {{ message.from }} </h2>
  <p> {{ message.body }} </p>
</div>
// resources/views/chat/index.njk
{% for message in messages %}
  {% include 'message' %}
{% endfor %}
  • macro & import
// resource/views/macros/button.njk
{% macro button(value, style='default') %}
  <button type="button" class="btn btn-{{style}}"> {{ value }} </button>
{% endmacro %}
// resources/views/home.njk
{% from 'macros.button' import button %}
{{ button('Create User', 'primary') }}

3.自定義全局變量和過濾器

  • app/Listeners/Http.js 中,提供了 Http.onStart 函數(shù)鉤子,供我們定義全局變量和過濾器。下面是兩個簡單的例子:
// 定義全局變量
Http.onStart = function () {
  const View = use('View')
  View.global('time', function () {
    return new Date().getTime()
  })
}
// 定義過濾器
const View = use('Adonis/Src/View')
const accounting = use('accounting') // npm module

View.filter('currency', function (amount, symbol) {
  return accounting.formatMoney(amount, {symbol})
})

// 使用過濾器
{{ 1000 | currency('$') }}

{# returns $1,000.00 #}

六、IoC Container & Service Providers

  • Ioc Container 主要概念就是在容器內(nèi)存儲/綁定依賴關(guān)系,然后從容器中取回它們,而不是手動請求它們。這種方法主要有以下三種好處:

  • 對 End-user 隱藏對象的配置,提供簡單明了的 API

  • 堅固支持依賴注入(DI),因為所有對象都是從單一的真實來源獲取的

  • 易于編寫第三方模塊/插件,因為你可以從 IoC 容器獲取依賴關(guān)系,而不是讓 End-user 手動傳遞它們。

  • 綁定依賴關(guān)系示例代碼如下:

const Ioc = require('adonis-fold').Ioc
const bugsnag = require('bugsnag')

Ioc.bind('Adonis/Src/Bugsnag', function (app) { 

  const Config = app.use('Adonis/Src/Config') 
  const bugSnagConfig = Config.get('services.bugsnag') 

  bugsnag.register(bugSnagConfig.apiKey, bugSnagConfig.options) 
  return bugsnag 

})
  • 綁定 Service Provider 的示例代碼如下:
const ServiceProvider = require('adonis-fold').ServiceProvider

class BugSnagProvider extends ServiceProvider {

  * register () { 
    this.app.bind('Adonis/Addons/BugSnag', (app) => {
      const BugSnag = require('./BugSnag')
      const Config = app.use('Adonis/Src/Config')
      return new BugSnag(Config)
    })
  }

  * boot () { 
    // Everything is registered do some hard work
  }

}

注:本篇對 model 部分的簡單略過。相關(guān)配置,Ace 開發(fā)工具等請參考官方文檔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,062評論 25 709
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,275評論 6 342
  • 并沒有那么多的話可以說,或許是生活上的漫不經(jīng)心.無非要說的話,無一定要去做的事.所以,每次打開QQ也不知道說些什么...
    HYY閱讀 325評論 0 1
  • 文/時間細(xì)流 手術(shù)室門前,林芝緊緊抓著我的手說,張海,你會沒事的,一定會沒事的,你給我記住了,我生命中最寶貴的禮物...
    時間細(xì)流閱讀 402評論 8 6

友情鏈接更多精彩內(nèi)容