5分鐘上手Egg.js+nunjucks模板引擎快速開發SEO友好的官網項目

在平常的項目中,有時候仍是不可避免的會維護一些jq官網項目等。面對此類需求,不少仍是之前的老套路,前端寫頁面交給後端去套數據。很煩有木有~~而改動以後還得交給後端再次修改,時間和溝通都是個麻煩。同時開發中,寫靜態頁面也很麻煩,不能複用,不能使用如今的工具,心累有木有~~固然了,解決辦法不少javascript

  • 本身寫個webpack腳本維護起來,把工程化的那一套東西搬過來。
  • 使用現成的nust\next等服務端渲染框架
  • 藉助於node+模板引擎等
  • ...

而本文介紹一下node的egg.js框架配合模板引擎來快速開發項目的模式。上手簡單快速,一我的搞定先後端。PS:又能夠學習新知識來,我好(草)開(泥)心(馬),奧利給~~~css

老羅扇臉圖.gif

初始化項目

# 初始化
cnpm init egg --type=simple

# 安裝依賴
cnpm i

# 啓動服務
npm run dev
複製代碼

簡單看下生成的文件目錄(ps:個別文件是沒有的,後期本身添加的)html

目錄.jpg

基本介紹

先介紹一下egg中app下的這些文件的基本做用,有個大概的概念:前端

  • app/router.js中編寫路由
  • app/controller/下編寫對應的控制器
  • app/service/下編寫service
  • app/view/下編寫對應的模板

一臉懵逼圖

路由編寫

  • 路由,相似前端項目中的路由。是用來定義請求的URL。
  • 當用戶訪問該路由地址時,將映射到對應的controller進行進一步的處理。
  • 因而可知,路由router就是定義和controller之間的一種映射關係。

定義路由,首先要打開app/router.js文件,在裏面進行定義,例如:vue

'use strict';

module.exports = app => {
  const { router, controller } = app;
  // 定義首頁的路由
  // 即當直接訪問域名的時候,將請求映射到controller.home.home中進行進一步的處理
  router.get('/', controller.home.home);
  // 定義關於咱們的路由
  router.get('/about', controller.home.about);
  // 定義新聞詳情的路由
  router.get('/details/:id', controller.home.details);
};
複製代碼
  • 此處的路由能夠理解爲就是咱們訪問的域名後面的具體的路徑地址。例如xxx.com/about中的/aboutjava

  • controller.xx.xx是指當咱們訪問了這個路由的時候,服務將當前路由映射到這個控制器中。具體的控制器做用,下面會詳細介紹。node

控制器Controller

  • 控制器,主要的做用就是處理用戶請求的參數,而後能夠調用service服務獲得咱們須要的數據,最後返回數據或者直接渲染出一個模板。
  • 而咱們這裏則是根據router(router參數非必須)渲染出一個頁面,也就是渲染一個html頁面返回,這樣瀏覽器打開的就直接是頁面了。
'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async home() {
    const { ctx } = this;
    await ctx.render('index.njk')
  }
}

module.exports = HomeController

// 或者你也能夠簡化一下寫法
module.exports = class extends require('egg').Controller {
    // ...
}
複製代碼
  • 經過調用ctx.render('index.njk')方法,咱們將渲染一個模板並返回(能夠理解爲返回給瀏覽器)
  • 在渲染模板的時候咱們也能夠給模板傳遞一個數據對象,ctx.render('index.njk', {}),而後模板內就能夠經過模板引擎的語法渲染咱們的數據了。
  • 通常這個數據是經過service讀取的數據庫或者調用其餘接口獲得的數據。這個會在下面的service中講到。

下面演示一個調用service的例子:jquery

// app/controller/home.js

'use strict';
module.exports = class extends require('egg').Controller {
    async details() {
    const { ctx } = this;
    try {
        // 調用service的home模塊中的details服務
        // 獲得數據後,塞給靜態模板使用
        const data = await ctx.service.home.details(ctx.params.id)
        // 渲染一個模板
        await ctx.render('details.njk', data)
    } catch (error) {
      ctx.body = '新聞獲取出錯'
    }
  }
}

複製代碼
  • 關於模板相關的內會在下面詳細講解
  • 關於service的定義和使用接下來立刻講解

Service服務

service服務主要是用來作什麼的呢?上面在controller中也提到了,主要用來獲取數據,拿到數據以後也能夠格式化再返回給controller使用。webpack

下面演示一個service調用某些接口服務獲得數據並返回給controller使用:ios

// 首先須要在app/下新建service文件夾
// 而後在service下新建home.js,最終爲app/service/home.js

'use strict';

module.exports = class extends require('egg').Service {
    // 根據文章id獲取文章數據
    // 此處的id是controll調用service服務時傳遞的id
    async details(id) {
        try {
            const url = `https:xxxx.com/api/${id}`
            const { data } = await this.ctx.curl(url, { dataType: 'json' })
            if (data.data && data.code === 200) {
                // 此處也能夠對數據進行處理後再返回
                
                // 返回數據
                return data.data
            }
            throw '數據獲取失敗'
        } catch (error) {
          throw error
        }
    }

}
複製代碼
  • 之因此叫home.js,是由於咱們controller中使用的是ctx.service.home
  • service.home中的home就是service文件的文件名稱
  • 此處進行了最簡單的數據獲取,經過this.ctx.curl方法請求其餘接口的數據。相似於使用axios獲取數據的操做。
  • 固然了,也能夠進行數據庫數據的增刪改查操做,本次就不作演示了,後面會考慮新開一篇egg作項目開發的文章再詳細介紹吧。
  • 此處對於接口的域名配置能夠放在配置文件中
  • 對於curl方法也能夠進一步的封裝在helper中
  • 對於數據返回的攔截也能夠封裝在中間件進行攔截
  • 對於上面這些內容有興趣的能夠翻看egg文件中相應的說明,仍是蠻詳細的。

模板渲染

nunjucks.jpg

定義好了基本的路由、控制器和service以後,就剩下模板了。首先模板,多是前端小夥伴寫的靜態頁面給到咱們的(ps:這個前端充氣小夥伴或許就是咱們本身!哦呸,不是充氣的)

好了,言歸正傳!Egg中有關於模板渲染的文檔,能夠看一下。Egg自己內置了egg-view做爲模板的解決方案,其中View支持插件egg-view-nunjucks,本文也是經過該插件進行的模板開發。

# 首先安裝
cnpm i egg-view-nunjucks --save

# 而後在根目錄下的config/plugin.js中使用插件
'use strict';

module.exports = {
  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',
  }
};

# 完成了插件的安裝和引入,咱們還須要配置插件的參數
# 根目錄下的config/config.default.js中
module.exports = appInfo => {
    // 其餘代碼
    // ...
    
    // 配置咱們的插件參數
    config.view = {
        // 定義映射的文件後綴名
        // 此處咱們定義爲.njk,那麼咱們的模板都須要以.njk結束,
        // 這樣該類型的文件纔會被咱們的模板插件進行處理
        mapping: {
          '.njk': 'nunjucks',
        }
    }

    // 其餘代碼
    // ...
}
複製代碼
  • 注意,若是不叫.njk也是能夠的,這個是自定義的,只要知足你的模板文件的後綴名和你定義的同樣就行,這樣纔會被插件處理。
  • egg-view-nunjucks封裝的是nunjucks,nunjucks推薦叫.njk

egg-view-nunjucks文檔    nunjucks文檔

有了模板引擎,咱們嵌套數據等就會方便不少了。下面來簡單看下一個模板的文件夾:

模板文件夾.jpg

  • 如上圖所示,須要新建app/view文件夾,全部的模板放在該文件夾下。這個也是能夠經過配置修改的(若是你有需求的話);
  • 上面的模板文件,根據你的項目來,這裏僅做爲演示。一般的,咱們可能會把頁面一些通用的頭部啊、底部、菜單等單獨抽離處理複用,具體的仍是看你的項目。

下面咱們簡單看下base.njk這個模板,作了什麼事情:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{% block title %}title默認內容{% endblock %}</title>
    <meta name="keywords" content="{% block keywords %}keywords默認內容{% endblock %}">
    <meta name="description" content="{% block description %}description默認內容{% endblock %}">
    

    <link rel="stylesheet" type="text/css" href="../../public/css/base.css">
    {% block head %}{% endblock %}
  </head>

  <body>
    <!-- 主體內容部分 -->
    {% block content %}{% endblock %}

    <script src="../../public/js/jquery.js"></script>
    <!-- script部分 -->
    {% block script %}{% endblock %}
  </body>
</html>
複製代碼
  • 不少時候,靜態頁面基本的結構都是同樣的,或者說相似的。因此咱們能夠定義一個基本的模板,而後全部的頁面繼承咱們這個基礎的模板就好。如何繼承,後面會講。
  • 好比,這裏咱們定義的基本模板中有頭部,可是頭部中的title等內容可能各個頁面不同,因此咱們自定義一個block塊,這樣就能夠在頁面中替換掉這塊的內容。有些相似於vue的slot插槽。
// 定義block塊
{% block title %}默認內容{% endblock %}
複製代碼
  • 簡單說一下這裏我的定義的幾個基本的block的做用吧:
名稱 說明
head塊 用於在head內最底部插入一些代碼。例如,公共模板引入了公共css,可是每一個頁面還有可能單獨引入css,全局只有一個css文件的除外
content塊 用於放置每一個頁面的主體部分
script塊 用於放置每一個頁面須要單獨引入的js文件

定義好了基本的模板和塊,下面咱們看下頁面中如何使用:

{% extends "./base/base.njk" %}

{% block title %}這是一個新的title{% endblock %}
{% block description %}這是一個新的description{% endblock %}
{% block keywords %}壹沓科技,加入壹沓,聯繫咱們{% endblock %}
{% set navActive = "about" %}

{% block head %}
  <link rel="stylesheet" href="../public/css/swiper.min.css">
{% endblock %}

{% block content %}
  <div class="page-wrapper">
    <!-- 導入公共的nav模板 -->
    {% include './base/nav.njk' %}

    <!-- 背景圖 -->
    <section class="banner-wrapper">
      <img src="../public/img/icon/about-banner.jpg" alt="背景LOGO">
      <span>{{ userName }}</span>
    </section>
    
    <!-- 渲染html模板演示 -->
    <div>{{content | safe}}</div>

    <!-- 導入公共的底部模板 -->
    {% include './base/foot.njk' %}
  </div>
{% endblock %}

{% block script %}
  <script src="../public/js/swiper.min.js"></script>
  <script src="../public/js/about.js"></script>
{% endblock %}
複製代碼
  • 經過上面一個簡單的模板,卻包含了最經常使用和最關鍵的不少特性。首先第一行,咱們要讓當前頁面繼承咱們定義的基礎模板:
// 相對路徑便可
{% extends "./base/base.njk" %}
複製代碼
  • 覆蓋咱們在基礎模板中自定義的塊。也能夠理解爲給vue的slot傳入新的內容:
// 例如,設置該頁面的title
// 若是不設置,就會使用基礎模板中默認的
{% block title %}這是一個新的title{% endblock %}
複製代碼
  • 設置變量,在當前模板中均可以使用。包括引入的其餘模板,只要是出如今了當前模板中,均可以取到該值進行使用。
// 定義了一個navActive變量
// 後面會講解一個常見的場景
{% set navActive = "about" %}
複製代碼
  • 使用變量、渲染內容/html
// 變量的使用,和vue的{{}}插值同樣
// 好比這個userName是咱們從controller中調用service獲取的數據對象中的一個屬性,
// 而後把對象傳遞給了模板,
// 那麼在模板中能夠直接取對象中的屬性進行渲染
// 注意,傳遞給模板的是一個對象,例如{userName: 'jack'},可是咱們使用的時候直接取userName
{{userName}}


// 一樣的,咱們在模板內定義的變量也能夠直接使用的

// 渲染html,好比很常見的富文本
// 相似vue的過濾器,這個safe是內置的,
// 過濾器也能夠自定義,具體的查看文檔吧
{{content | safe}}
複製代碼
  • 引入其餘模板。好比上面能夠看到,咱們繼承了基礎的模板,可是咱們還能夠會提取一些公共的像nav模板進行復用。
// 導入咱們的nav模板
{% include './base/nav.njk' %}
複製代碼
  • nav模板通常很常見的會有這麼一個場景,好比就是當前頁面選項須要高亮。來,先看一下nav模板:
<!-- 導航頭部 -->
<nav class="nav-wrapper">
  <ul class="menu-content">
    <li>
      <a href="/news" class="{{ 'active' if navActive == 'news' else '' }}">動態資訊</a>
    </li>
    <li>
      <a href="/about" class="{{ 'active' if navActive == 'about' else '' }}">關於咱們</a>
    </li>
  </ul>
</nav>
複製代碼

能夠看到,咱們對class名進行了一個if判斷,判斷當變量navActive是某些值的時候給他增長一個active類名,不然就是各空的class名。這裏仔細注意一下寫法便可。 那麼咱們怎麼定義變量呢?此時再回到上面定義變量的部分介紹,咱們已經在頁面中定義了一個變量navActive,因此咱們只須要每一個頁面定義一個navActive且值爲咱們須要的便可了。

  • 最後再補充說明一下,模板內的調整路徑,就直接寫成咱們在router.js中定義的路由便可,而不是寫的模板地址。
// a標籤跳轉
<a href="/news"></a>

// js調整也是同樣,使用router.js定義的路由
window.location.href="/news"
複製代碼

王境特真香警告

百尺竿頭、日進一步 我是大家的老朋友愣錘~ 喜歡的小夥伴歡迎關注點贊,一塊兒分享交流更多的前端好玩技術!

相關文章
相關標籤/搜索