EggJs快速入門

来源:https://www.cnblogs.com/rope/archive/2019/05/13/10859014.html
-Advertisement-
Play Games

Egg.js 簡介 Egg.js 為企業級框架和應用而生 ,幫助開發團隊和開發人員降低開發和維護成本。 專註於提供 Web 開發的核心功能和一套靈活可擴展的插件機制,不會做出技術選型,因為固定的技術選型會使框架的擴展性變差,無法滿足各種定製需求。 Egg 的插件機制有很高的可擴展性, 一個插件只做一 ...


Egg.js

簡介

Egg.js 為企業級框架和應用而生,幫助開發團隊和開發人員降低開發和維護成本。

專註於提供 Web 開發的核心功能和一套靈活可擴展的插件機制,不會做出技術選型,因為固定的技術選型會使框架的擴展性變差,無法滿足各種定製需求。

Egg 的插件機制有很高的可擴展性,一個插件只做一件事,Egg 通過框架聚合這些插件,並根據自己的業務場景定製配置,這樣應用的開發成本就變得很低。

Egg 奉行『約定優於配置』,按照一套統一的約定進行應用開發,Egg 有很高的擴展性,可以按照團隊的約定定製框架。使用 Loader 可以讓框架根據不同環境定義預設配置,還可以覆蓋 Egg 的預設約定。

特性

  • 提供基於 Egg 定製上層框架的能力
  • 高度可擴展的插件機制
  • 內置多進程管理
  • 基於 Koa 開發,性能優異
  • 框架穩定,測試覆蓋率高
  • 漸進式開發(根據設備的支持情況來提供更多功能,提供離線能力,推送通知,甚至原生應用的外觀和速度,以及對資源進行本地緩存。)

Egg.js 與 Koa

Koa 是一個新的 web 框架,由 Express 幕後的原班人馬打造, 致力於成為 web 應用和 API 開發領域中的一個更小、更富有表現力、更健壯的基石。
學習指南:koa_筆記

Context

和 Express 只有 Request 和 Response 兩個對象不同,Koa 增加了一個 Context 的對象,作為這次請求的上下文對象(在 Koa 1 中為中間件的 this,在 Koa 2 中作為中間件的第一個參數傳入)。我們可以將一次請求相關的上下文都掛載到這個對象上。(在後續任何一個地方進行其他調用都需要用到)的屬性就可以掛載上去。相較於 request 和 response 而言更加符合語義。

同時 Context 上也掛載了 Request 和 Response 兩個對象。和 Express 類似,這兩個對象都提供了大量的便捷方法輔助開發,例如

  • get request.query
  • get request.hostname
  • set response.body
  • set response.status

Egg 繼承於 Koa

如上述,Koa 是一個非常優秀的框架,然而對於企業級應用來說,它還比較基礎。

而 Egg 選擇了 Koa 作為其基礎框架,在它的模型基礎上,進一步對它進行了一些增強。

插件

眾所周知,在 Express 和 Koa 中,經常會引入許許多多的中間件來提供各種各樣的功能,而 Egg 提供了一個更加強大的插件機制,讓這些獨立領域的功能模塊可以更加容易編寫。

一個插件可以包含

  • extend:擴展基礎對象的上下文,提供各種工具類、屬性。
  • middleware:增加一個或多個中間件,提供請求的前置、後置處理邏輯。
  • config:配置各個環境下插件自身的預設配置項。

一個獨立領域下的插件實現,可以在代碼維護性非常高的情況下實現非常完善的功能,而插件也支持配置各個環境下的預設(最佳)配置,讓我們使用插件的時候幾乎可以不需要修改配置項。

Egg 與 Koa 的版本關係

Egg 1.x

  • 應用開發者可以選擇 async function(Node.js 8.x+) 或者 generator function(Node.js 6.x+)進行編寫。

Egg 2.x

  • 只支持 Node.js 8 及以上的版本。

快速入門

環境準備

  • 操作系統:支持 macOS,Linux,Windows
  • 運行環境:建議選擇Node.js穩定版本,最低要求 8.x。

快速初始化

npm i -g egg-init
egg-init egg-demo --type=simple //--type=simple可以去掉然後自己配置
cd egg-demo 
npm i

啟動項目:

npm run dev
瀏覽器打開:localhost:7001

逐步搭建

通常你可以通過上面的方式,快速選擇適合對應業務模型的腳手架,快速啟動 Egg.js 項目的開發。

現在我們需要自己手動一步步的搭建一個項目。

註意:實際項目中,我們推薦使用上一節的腳手架直接初始化。

初始化項目

先來初始化下目錄結構:

mkdir egg-example
cd egg-example
npm init
npm i egg --save
npm i egg-bin --save-dev

添加 npm scriptspackage.json

{
  "name": "egg-example",
  "scripts": {
    "dev": "egg-bin dev"//npm run dev
  }
}

編寫控制器

// ./app/controller/home.js
const Controller = require('egg').Controller;
class HomeController extends Controller {
  async index() {
    this.ctx.body = 'Hello world';//這個內容就可以顯示在body上面
  }
}
module.exports = HomeController;//把我創建的這個類預設暴露出去

配置路由:

// ./app/router.js
module.exports = app => {//app參數裡面包含了很多東西
  const { router, controller } = app;//我們從中結構出controller文件夾中的內容
  router.get('/', controller.home.index);//會找到home.js中預設暴露的類的index方法
};

加一個配置文件:

// ./config/config.default.js
exports.keys = '此處改為你自己的 Cookie 安全字元串';//自定義例如'abc1234'必須填寫

現在可以啟動應用來體驗下

npm run dev
打開瀏覽器:localhost:7001

註意:

  • Controller 有 classexports 兩種編寫方式,本文示範的是前者。exports不推薦使用是為了相容,可以自行到官方文檔查看
  • Config 也有 module.exportsexports 的寫法。
  • 開發期預設開啟了 development 插件,修改後端代碼後,會自動重啟,無需再次npm run dev

擴展

在基於 Egg 的框架或者應用中,我們可以通過定義 app/extend/{application,context,request,response}.js
這裡表示可以創建application,context,request,response四個js文件

來擴展 Koa 中對應的四個對象的原型,通過這個功能,我們可以快速的增加更多的輔助方法,例如我們在 app/extend/context.js 中寫入下列代碼:

// ./app/extend/context.js
module.exports = {
  get isIOS() {//get表示通過這個isIOS得到什麼記得添加
    const iosReg = /iphone|ipad|ipod/i;//正則
    return iosReg.test(this.get('user-agent'));
      //User Agent顯示使用的瀏覽器類型及版本、操作系統及版本、瀏覽器內核、等信息的標識。
  },
};

在 Controller 中,我們就可以使用到剛纔定義的這個便捷屬性了:

// ./app/controller/home.js
const Controller = require('egg').Controller;//從egg上引入控制器
class HomeController extends Controller {//聲明一個類並從constroller繼承
    async index() {//聲明一個函數
        this.ctx.body = this.ctx.isIOS
            ? '你的操作系統是IOS.'
            : '你的操作系統不是IOS.';
    }
}
module.exports = HomeController;//把這個類預設暴露出去

靜態資源

Egg 內置了static插件static 插件預設映射 app/public/ 目錄,我們把靜態資源都放到 app/public 目錄即可:

模板渲染

框架並不強制你使用某種模板引擎,只是約定了 View 插件開發規範,開發者可以引入不同的插件來實現差異化定製。

更多用法參見 View,在本例中,我們使用 Nunjucks 來渲染,先安裝對應的插件 egg-view-nunjucks

npm i egg-view-nunjucks --save

開啟插件:

// ./config/plugin.js
exports.nunjucks = {
  enable: true,//使用
  package: 'egg-view-nunjucks'//使用什麼插件
};
// ./config/config.default.js
exports.keys = '此處改為你自己的 Cookie 安全字元串';
// 添加 view 配置
exports.view = {
  defaultViewEngine: 'nunjucks',//預設視圖引擎
  mapping: {//.tpl結尾的文件
    '.tpl': 'nunjucks',
  },
};

為列表頁編寫模板文件,一般放置在 ./app/view 目錄下

<!-- ./app/view/news/list.tpl -->
<!-- {% %} 來當做模板,與現有的html標記混用。和php乾的事情是一樣的。-->
<html>
  <head>
    <title>Hacker News</title>
    <link rel="stylesheet" href="/public/css/news.css" />
  </head>
  <body>
    <ul class="news-view view">
      {% for item in list %}<!-- 這裡可以直接拿到dataList中的list但無法拿到dataList -->
        <li class="item">
          <a href="{{ item.url }}">{{ item.title }}</a>
        </li>
      {% endfor %}<!-- 結束for迴圈 -->
    </ul>
  </body>
</html>

添加 Controller 和 Router

// ./app/controller/news.js
const Controller = require('egg').Controller;
class NewsController extends Controller {
  async list() {
    const dataList = {
      list: [
        { id: 1, title: 'this is news 1', url: '/news/1' },
        { id: 2, title: 'this is news 2', url: '/news/2' }
      ]
    };
    await this.ctx.render('news/list.tpl', dataList);
      //渲染list.tpl文件且把dataList的內容傳過去
  }
}

module.exports = NewsController;
// ./app/router.js
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/news', controller.news.list);
};

啟動瀏覽器,訪問 http://localhost:7001/news 即可看到渲染後的頁面。

編寫 service

在實際應用中,Controller 一般不會自己產出數據,也不會包含複雜的邏輯,複雜的過程應該交給業務邏輯層 Service

我們來添加一個 Service 抓取頁面的數據 ,如下:這裡只是demo寫法,根據實際情況再改變

// ./app/service/news.js
const Service = require('egg').Service;
class NewsService extends Service {
  async list(page) {//得到傳來的頁碼參數
    // 讀取配置拿到API介面
    const { serverUrl } = this.config.news;

    //使用內置的curl發出請求拿回數據
    //結構出來result,在這裡給他改名叫做idList
    const { result: idList } = await this.ctx.curl(`${serverUrl}/topstories.json`, {
      data: {//攜帶信息
        orderBy: '"$key"',//根據什麼排序
        startAt: `${page}`,//起始頁
        endAt: `${page+1}`,//結束頁
      },
      dataType: 'json',//需要返回的數據類型
    });

    // 獲取詳細信息
    const newsList = await Promise.all(
        //獲取對象的所有鍵名
      Object.keys(idList).map(key => {
        const url = `${serverUrl}/item/${idList[key]}.json`;
        return this.ctx.curl(url, { dataType: 'json' });//再去請求每一個的詳細信息
      })
    );
    return newsList.map(res => res.data);//把每一條的數據的data返回出去。
  }
}
module.exports = NewsService;

框架提供了內置的 HttpClient 來方便開發者使用 HTTP 請求。
this.ctx.curl(api地址:string,配置:json)

然後稍微修改下之前的 Controller:

// ./app/controller/news.js
const Controller = require('egg').Controller;

class NewsController extends Controller {
  async list() {
    const ctx = this.ctx;
    const page = ctx.query.page || 1;//獲取用戶url裡面的page值沒有就返回1
    const newsList = await ctx.service.news.list(page);//傳過去,聲明newsList接受return回來的數據
    await ctx.render('news/list.tpl', { list: newsList });//把list傳過去,他的數據是newsList
  }
}
module.exports = NewsController;

還需增加 app/service/news.js 中讀取到的配置:

// ./config/config.default.js
// 添加 news 的配置項
exports.news = {
  serverUrl: 'API介面地址'
};

編寫擴展

如果時間的數據是 UnixTime 格式的,我們希望顯示為便於閱讀的格式。框架提供了一種快速擴展的方式,只需在 app/extend 目錄下提供擴展腳本即可,具體參見擴展。在這裡,我們可以使用 View 插件支持的 Helper 來實現:

npm i moment --save

// ./app/extend/helper.js
const moment = require('moment');
exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();

在模板裡面使用:

<!-- ./app/view/news/list.tpl -->
<html>
  <head>
    <title>Hacker News</title>
    <link rel="stylesheet" href="/public/css/news.css" />
  </head>
  <body>
    <ul class="news-view view">
      {% for item in list %}
        <li class="item">
          <a href="{{ item.url }}">{{ item.title }}</a>
        </li>
      {% endfor %}
      {{ helper.relativeTime(item.time) }}<!--通過這樣的方法使用 -->
    </ul>
  </body>
</html>

編寫中間件

假設我們的新聞站點,禁止百度爬蟲訪問。可以通過 Middleware 判斷 User-Agent,如下:

// ./app/middleware/robot.js
// options === app.config.robot //這裡的robot是這個文件的名字
module.exports = (options, app) => {
  return async function robotMiddleware(ctx, next) {
    const source = ctx.get('user-agent') || '';
    const match = options.ua.some(ua => ua.test(source));
    //some()會讓ua數組中的每一項去執行()裡面的函數,如果有一個元素滿足條件,則表達式返回true , 剩餘的元素不會再執行檢測。
    if (match) {
      ctx.status = 403;
      ctx.message = 'Go away, robot.';//別用中文
    } else {
      await next();//放行
    }
  }
};
// ./config/config.default.js
// 添加中間件
exports.middleware = [
  'robot'
];
// 添加配置
exports.robot = {
  ua: [
    /Baiduspider/i,
  ]
};

現在可以使用 curl http://localhost:7001/news -A "Baiduspider" 看看效果。

如果你是window用戶在CMD下是無法執行此命令的,推薦你安裝Git Bash運行此命令

配置文件

寫業務的時候,不可避免的需要有配置文件,框架提供了強大的配置合併管理功能:

  • 支持按環境變數載入不同的配置文件,如 config.local.jsconfig.prod.js 等等。
  • 應用/插件/框架都可以配置自己的配置文件,框架將按順序合併載入。
  • 具體合併邏輯可參見配置文件
// ./config/config.default.js
// 這裡是預設值
exports.robot = {
  ua: [
    /curl/i,
    /Baiduspider/i,
  ],
};
// ./config/config.local.js
// 僅在開發模式下讀取,將覆蓋預設值
exports.robot = {
  ua: [
    /Baiduspider/i,
  ],
};
// ./app/service/some.js
const Service = require('egg').Service;

class SomeService extends Service {
  async list() {
    const rule = this.config.robot.ua;// /Baiduspider/i,
  }
}
module.exports = SomeService;

單元測試

單元測試非常重要,框架也提供了 egg-bin 來幫開發者無痛的編寫測試。

測試文件應該放在項目根目錄下的 test 目錄下,並以 test.js 為尾碼名,即 ./test/**/*.test.js

**表示任何文件夾
*表示任何文件

npm i egg-mock --save-dev

然後配置依賴和 npm scripts配置:

{
  "scripts": {
    "test": "egg-bin test",
  }
}
// ./test/app/middleware/robot.test.js
const { app, mock, assert } = require('egg-mock/bootstrap');
//他會去找到./app/middleware/robot.js進行測試
describe('test/app/middleware/robot.test.js', () => {
  it('阻止機器人爬蟲', () => {
    return app.httpRequest()
      .get('/')
      .set('User-Agent', "Baiduspider")
      .expect(403);
  });
});

執行測試:npm test


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 公用電話產品,插入SIM卡後要求自動打開Volte功能,即插即用,用完拔卡就走 實現 第一步 開關對應的代碼 通過列印日誌和全局查找,源碼位置 vendor/mediatek/proprietary/packages/services/Telephony/src/com/android/pho ...
  • QCA4002和QCA4004是物聯網(物聯網)的智能連接WIFI SoCs(片上系統)。 QCA400x是物聯網(物聯網)的智能Wi-Fi平臺.這個聯網平臺的目的是讓客戶以最少的開發努力和成本,將功能齊全的Wi-Fi添加到多種產品中。 QCA4002是單流(1x1)IEEE802.11n單帶SoC ...
  • 什麼是RS式呢?即Receiver+singleTask 。我們知道Activity有四種載入模式,而singleTask就是其中的一種,使用這個模式之後,當startActivity時,它先會在當前棧中查詢是否存在Activity的實例,如果存在,則將其至於棧頂,並將其之上的所有Activity移 ...
  • 一、概述 android提供了豐富的系統控制項,但在實際開發中,這些控制項依然不能完全覆蓋我們的需求。有時,我們需要自定義控制項來滿足一些個性化產品需求。自定義控制項,主要有以下三種方式: 前兩種都是對現有控制項的擴展或者組合,第三種方式是仿照 二、實例 三、關鍵API總結 (1)自定義屬性 (2)Path類 ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/91 背景: 記得很久以前,碰到一個需求場景,需要在Android Dialog中顯示Spinner,用來進行選擇操作。那個時候還很困惑,不知道是否可以這麼搞。 ...
  • Node.js 約定回調函數第一個參數必須是錯誤對象err: 問題:Node.js約定回調函數第一個參數必須是錯誤對象err,如果沒有錯誤該參數就是null 原因:非同步執行分成兩段,在兩段之間拋出異常和錯誤程式無法直接捕獲,只能當做參數傳入第二段 ...
  • 弧生成器(Arc Generator) 弧生成器(Arc Generator)可憑藉起始角度、終止角度、內半徑、外半徑等,生成弧線的路徑,因此在製作餅狀圖、弦圖等圖表時很常用。 有四個訪問器需要謹記:內半徑訪問器innerRadius()、外半徑訪問器outerRadius()、起始角度訪問器sta ...
  • 提筆又不知從何說起, 畢竟頭髮還茂盛,離大神就差一頭秀髮的距離, 所以此文不講技術, 只是單純的總結一下我是怎麼入坑的。本人前端打雜的,不喜勿噴。 17年7月我畢業了, 可我並不知道未來在何方, 我該做什麼,大學四年給我的只是一紙證書 由於家在南方, 所以就選擇了深圳這個繁華又孤獨的城市, 搜索和專 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...