[實例] 使用NodeJs,Express+EJS製作靜態網站

Sean Yeh
Web Design Zone
Published in
39 min readMar 6, 2020

--

前面幾篇我們學到NodeJs、Express與EJS樣版引擎的基本操作方式。這一篇我們想要藉由這些已知的觀念,來實際上製作一個由數個靜態頁組成的網站。並且介紹其他尚未提到的觀念。

網站規劃

在製作網站的過程中。首先必須先規劃一下,到底網站該呈現什麼樣子?那要怎麼規劃?

一般來說,就是需要一個網站的地圖(Sitemap),透過這個地圖,才有辦法逐一的進行網站的施工與製作。

或許你會問?很小的網站(五頁以內的網站)也需要地圖嗎?

其實這種狀況見仁見智,或許你天資聰穎,可以把五個頁面的架構通通存在自己的腦中。依照腦中的地圖施工。

但是你還是無法保證,網站上線營運3~5年後的某一天,當你被客戶要求保固維修而被迫不得不回想這個(舊網站)到底當時是如何規劃設計的時候,你可以記得所有的細節。

另外,如果這個網站的施工需要透過其他人一起協同合作的時候,有一張地圖作為共通討論的基礎,會比完全沒有資料來討論時來得好吧。

雖然這次我們要做的網站只有四個頁面(五頁以內的網站),符合上面討論的範疇。基於上述的兩個論點,我們產製一個簡單網站的地圖。

網站架構圖

網站架構圖
網站架構圖

網站架構圖可以用下面的圖示表示,這個圖示與一般的組織結構圖用的範本類似。

一個頁面以一個方框表示,方框與方框間上下排列並且連接在一起,表示出頁面的階層關係。

如果以一個組織結構來比喻的話,由於首頁(index)是整個網站最原始的開始頁面,可以說像是組織中的領導者一般,放在最上面的位置。其餘的單元頁面,例如聯絡(contact)、關於(about)、產品(product)等等,就像是各個不同部門一樣,一字排開放在第二層級。如果個個單元還有更多細節要放的話,可以在所屬的個個單元下面,依照目前的方式再分叉出一個層級。

請注意,在規劃架構的時候,雖然在架構邏輯上正確,但也不要走火入魔似的一路規劃超過四個以上的層級。這樣子會讓網站使用者很不容易找到他們需要的內容。

版面配置

分成三個區塊的版面配置

從單一的網頁index來看,最少我們可以把檔案分割成Header與Footer以及中間內容Main Content三個區段。

如果是內容豐富的網站,在中間內容(Main Content)區段中,可以依照頁面不同的功能,分別放入不同的版面。

檔案拆分方式

規劃好架構與版面配置後,我們要思考『在這個專案中檔案要如何放置?』在這一件事的方向上,我們可以朝『如何做可以不要重複的製造出一樣的程式碼』這個方向來思考。

在頁面中重複的複製貼上同樣的程式碼,不僅讓網站變得肥大,也會導致日後維護上的不方便。除非你確定目前規劃的網站是「拋棄式的網站」,也就是說網站上線後一段時間,就可以下線了的網站,不然多多少少都要面臨網站維護的問題。

如果檔案結構在一開始的時候就沒有先規劃好,網站運用的時間越久,在維護上會越麻煩。畢竟大家不可能記得當時開發時的方式。

檔案大致上拆分的方式如下。

以頁面來看

專案資料結構

就像是家裡的書房一樣,如果文具與書籍沒有分門別類的放好,臨時要找需要用的東西時,就會找不到。

同樣的道理,專案資料夾的結構要如何規劃也是很重要的。下面列出目前實例練習的專案資料夾規劃方式供大家參考。

  1. node_module: NodeJs 安裝後套件檔案
  2. views:放置樣版引擎ejs檔案的地方。
  3. public:放置靜態檔案的地方。裡面可以再依照靜態檔案的屬性,分為image圖片、css樣式檔、javaScript等資料夾。
  4. 其他放根目錄。由於這個網站是簡單網。只需要一個index.js檔案與package.json檔案一起放在根目錄。不需要再開資料夾放其他的js檔案。

當然,以上的規劃僅提供大家參考。大家可以依照不同專案做不同的規劃。

其原則就是把樣板、執行的程式、靜態檔案等不同功能的檔案,分開放,不要像義大利麵肉醬麵一樣的混在一起,難以分辨哪裡是肉醬、哪個番茄、哪些是麵。

如果你的專案部署後放到AWS或者是GCP等雲端空間上面的話,可能會把這個資料夾裡面的檔案放在storage之類的儲存空間上(例如AWS 上面的S3儲存空間)。這個public資料夾就不需要了。

規劃完畢之後,我們開始透過NodeJs與Express.js製作網站。

建立的步驟之前已經提到過,在此就不再贅述。大家有興趣的話,可以到下面幾篇參看。

下面是我們在這裡的假設

  1. 大家已經完成透過Express建立伺服器,並且:
  2. 可以進入首頁http://localhost:3000/ 並且看到首頁的內容。
  3. 已經安裝EJS樣版引擎
  4. 伺服器可以自動重新整理
  5. package.json如下所列內容:
{    "name": "express_exejs",    "version": "1.0.0",    "description": "",    "main": "index.js",    "scripts": {        "test": "echo \"Error: no test specified\" && exit 1"    },    "author": "",    "license": "ISC",    "dependencies": {    "ejs": "^3.0.1",    "ejs-locals": "^1.0.2",    "express": "^4.17.1",    "nodemon": "^2.0.2"    }}

index.js已經有下面的內容:

let express = require('express');  //載入express模組let engine = require('ejs-locals');  //載入ejs-locals 模組let app = express();  // 使用expressapp.engine('ejs', engine);app.set('views', './views');app.set('view engine', 'ejs');app.get('/', function (req, res) {    res.render('index', {'title': '首頁',});})let port = 3000;  //設定port位置app.listen(port);  // 監聽 port

載入靜態資料

要讓Express抓到靜態檔案的資料必須使用express.static來取得,在Express的網站上有相關的說明。

  • To serve static files such as images, CSS files, and JavaScript files, use the express.static built-in middleware function in Express.
  • The function signature is:
  • express.static(root, [options])

將含有靜態檔案的資料夾名稱傳遞給 express.static 中介軟體,就能開始提供檔案。依照先前的規劃,我們將public的資料夾規劃為放置圖片檔案、CSS與JavaScript檔案的目錄。

因此,我們可以用下面的程式碼將public資料夾的名稱傳遞給 express.static 中介軟體。

app.use(express.static('public'))

此外,未來的專案中如果要使用多個靜態檔案的資料夾,可以多次呼叫 express.static 中介軟體函數,方法如下:

app.use(express.static('public'))
app.use(express.static('files'))

執得注意的是,靜態資料夾的名稱並不是 URL 的一部分,因為Express 會查閱靜態資料夾的相對檔案。

例如:如果在public裡面放了一個img資料夾,並且裡面放了一個logo.jpg檔。對於這個檔案來說它的URL是:

http://localhost:3000/img/logo.png

而不是:http://localhost:3000/public/img/logo.png 如果輸入這個 URL,網頁會出現Cannot GET /public/img/logo.png的錯誤。

虛擬路徑

如果你想要隱藏真實的路徑,為靜態檔案提供一個事實上不存在的資料夾,可以使用下面的方式來增加一個虛擬的路徑:

app.use('/static', express.static('public'));

如果將原來的程式碼換成上面的程式碼,http://localhost:3000/img/logo.png 路徑下,就無法找到檔案了。

新的路徑將增加一個static資料夾路徑,變成下面:

http://localhost:3000/static/img/logo.png

不過,我們提供給 express.static 的路徑,是相對於我們啟動 node 的目錄,就目前專案來說,就是從根目錄起算。但是,未來網站規模越來越大時,可能會有所錯亂。比較保險的做法是提供一個絕對路徑給express.static。

app.use('/static', express.static(__dirname + '/public'));

註:__dirname可以找到專案的絕對路徑。

views資料夾

再來是依照規劃建立views資料夾與partial子資料夾。由於我們在 index.js 裡面設定使用 ejs為本專案的樣板引擎,因此在views資料夾裡面新增五個檔案,分別是 index.ejs、about.ejs、product.ejs、contact.ejs與layout.ejs。接著在views/partial資料夾裡面新增兩個檔案,分別是 header.ejs, footer.ejs。如果建立完成後,整個資料夾的結構應該向下面圖示:

views資料夾內的檔案結構

整個結構規劃成上面這樣子的原因是,我們想要將網站中每一個頁面都會利用到的位置拆出來,分別獨立為一個檔案(1).頁首(header.ejs)頁尾( footer.ejs)以及關係到整體的(2).框架(layout.ejs)。

實際上,也有人把頁首與頁尾(1)的內容直接放在layout.ejs(2)中。如果你的專案不是很大,頁首裡面的menu選單不是很複雜,頁尾也不是業界常用的胖頁尾(fat footer)的話,合併在一個layout.ejs檔案是比較簡潔的;反之,我會建議拆開來比較好。

在此,我們的規劃如下:(1)頁首(header.ejs)頁尾( footer.ejs)載入到(2)框架(layout.ejs)中,再讓(3)index.ejs來使用。透過index.js中的routing讓伺服器對來到首頁(/)的使用者render給使用者index.ejs的內容。以上就是整的流程。

製作版型

首先,我們要製作上述的各個版型。先從第一個Template,就是layout.ejs開始。由於ejs使用的格式,大部分與一般的HTML語法近似,在製作一開始的時候,可以把它當作平常撰寫HTML語法一樣。

由於這篇文章主要是說明樣板引擎如何組合等問題,在版型的製作上我們儘量簡化並且使用了Bootstrap4的CDN讓畫面好看一點點,當然實際上業界使用的版型必定比目前的複雜多了。

起手式:空白HTML

先開啟一個空白的HTML當作layout.ejs。不過,我們可以先使用layout.html來製作。製作完畢之後,再把附檔名改為ejs。

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body></body></html>

加入調味料:BS4

我們加入Bootstrap 4 CSS與JavaScript的相關檔案路徑。如下程式碼。並且加入一個自定義的CSS在Bootstrap 4 CSS後面</head>前面(如粗體字)。

自定義的CSS放在靜態資料夾public/css裡面,我們指定了一個虛擬路徑static,這在前面曾經說明過。

<link rel=”stylesheet” href=”static/css/main.css”>

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="static/css/main.css">
</head><body><script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script></body></html>

加上Header與Footer的內容

再加上Header與Footer的版型與main content的部分。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
<!-- header -->
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#"><img src="../public/img/MyLogo.png" style="height:40px" alt="My logo">EJS測試網站</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="./">首頁 <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">關於我們</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">產品資訊</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">聯絡我們</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="搜尋..." aria-label="Search">
<button class="btn btn-secondary my-2 my-sm-0" type="submit">搜尋</button>
</form>
</div>
</nav>
</header>
<!-- // header -->
<!-- main content -->
<main role="main">
<section class="jumbotron text-center">
<div class="container py-5">
<h1>Title</h1>
<p class="lead text-muted">Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi quod, corrupti
repudiandae omnis voluptates adipisci temporibus aut rem accusamus eum magnam, rerum sit eveniet?Laboriosam quisquam vero magni eum magnam.</p>
</div>
</section>
<div class="album py-5 bg-light">
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div><div class="col-md-4"><div class="card mb-4 shadow-sm"><svg class="bd-placeholder-img card-img-top" width="100%" height="225"xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body"><p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam,rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beataeanimi odio tempora quae. Porro, non!</p></div></div></div>
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225"
xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text></svg><div class="card-body">
<p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam, rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beatae animi odio tempora quae. Porro, non!</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225"
xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"focusable="false" role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"dy=".3em">Thumbnail</text>
</svg>
<div class="card-body">
<p class="card-text">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro neque soluta ullam, rerum deserunt in possimus similique quam, dicta alias eligendi dolores quas beatae animi odio tempora quae. Porro, non!</p>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- // main content --><!-- footer -->
<footer class="text-muted">
<div class="container py-3">
<p class="float-right">
<a href="/">回首頁</a>
</p>
<p>2020 &copy; 版權所有 Sean Yeh 出品</p>
<p>剛來的嗎?要不要先去逛逛<a href="/about">關於我們</a> 頁面,裡面可以知道我們是誰或者是去逛逛<a href="/product">產品資訊</a>,暸解我們在賣什麼。</p>
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous">
</script>
<!-- // footer -->
</body>
</html>

目前為止,就像是一般在製作HTML頁面一樣。

拆分為EJS

就上面的HTML頁面,將header到header部分的程式碼拷貝到header.ejs裡面。(要記得修改靜態檔案的路徑)

<!-- header -->
....
<!-- // header -->

將footer到footer部分的程式碼拷貝到footer.ejs裡面。

<!-- footer -->
...
<!-- // footer -->

第一個部分

我們要把頁首(header.ejs)頁尾( footer.ejs)載入到框架(layout.ejs)中。這時候要用到partial() 來將頁首與頁尾載入到layout中。括弧中要放置檔案的路徑。

由於header與footer都放在partial資料夾裡面,所以我們要這樣子呼叫:

<%-partial('partial/header')%><%-partial('partial/footer')%>

最後,layout.ejs會呈現下面的樣子:

<!doctype html>
<html lang="zh-TW">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="<%= description %>">
<title><%= title %></title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="static/css/main.css">
</head>
<body>
<%-partial('partial/header')%>
<%-body %>
<!-- /.container -->
<%-partial('partial/footer')%>
</body>
</html>

第二個部分

首先,要放入 <%-body %>在index.ejs裡面,在這個案子裡面,我們把它放在header與footer的中間。

接下來,可以使用layout()將共用樣式layout.ejs框架與個別頁面產生連動。因此,在實際上路由(routing)對應的檔案中(index、product、about、contact)我們要加入:

這時候可以使用layout()將共用樣式layout.ejs框架與個別頁面產生連動。因此,在實際上路由(routing)對應的檔案中(index、product、about、contact)我們要加入:

<% layout('layout') %>

如此,就完成了ejs檔案之間的連動了。

建立頁面的路由

原來的index.js裡面我們只有一個路由。就是連到首頁的部分。

app.get('/', function (req, res) {res.render('index', {'title': '首頁',});})

現在要在index.js加入product、about、contact等路由。

app.get('/product', function (req, res) {    res.render('product', {'title': '產品頁',});})app.get('/about', function (req, res) {    res.render('about', {'title': '關於我們',});})app.get('/contact', function (req, res) {   res.render('contact', {'title': '聯絡我們',});})

在header.ejs修改Menu選單路徑

/about,/product/contact 加入<a>連結裡面。

<li class="nav-item">    <a class="nav-link" href="/about">關於我們</a></li><li class="nav-item">    <a class="nav-link" href="/product">產品資訊</a></li><li class="nav-item">    <a class="nav-link" href="/contact">聯絡我們</a></li>

這樣子,點選任何連結時,就會到達指定的頁面。

接下來,我們在優化一下。我們以「聯絡我們」頁面跟「產品介紹」為例說明。

之前文章提過,我們可以增加參數到路由上面。因此,這次我們加上三個參數。這三個參數,提供給不同頁面使用。

  • title: 中文標題,
  • subtitle:英文標題,
  • description: 頁面說明簡介。

下面是聯絡我們:

app.get('/contact', function (req, res) {res.render('contact', {    'title': 'Contact Us',    'subtitle': '聯絡我們',    'description': `Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi quod, corrupti repudiandae omnis voluptates adipisci temporibus aut rem accusamus eum magnam, rerum sit eveniet? Laboriosam quisquam vero magni eum magnam.`,});
})

下面是產品介紹:

app.get('/product', function (req, res) {res.render('product', {    'title': 'Our Products',    'subtitle': '產品介紹',    'description': `Something short and leading about the collection below—its contents, the creator, etc.Make it short and sweet, but not too short so folks don’ t simply skip over it entirely.`,
});
})

在關於我們的ejs檔案中,我們需要加入<%= title %>與<%= subtitle %>還有<%= description %>。這三個會將上面的參數帶入。

<% layout('layout') %>   <section class="jumbotron text-center">        <div class="container">         <h1><%= title %></h1>         <h2><%= subtitle %></h2>         <p class="lead text-muted"><%= description %></p>        </div>    </section>

完成結果

完成後就可以隨便點擊上面的按鈕 到達指定的頁面。而每個頁面都會有一個主要標題與次要標題(一個是中文標題另一個是英文標題),以及頁面的說明文字。圖片則屬於靜態檔案,一律放在public資料夾裡面。隨著專案的規模不同,建議將public資料夾裡面的子資料夾進行分類。

延伸閱讀1:Node.js — EJS樣板引擎的使用方式

延伸閱讀2:Node.js — 讓您在Server環境下用JavaScript進行操作

延伸閱讀3:Node.jsWeb應用框架 — Express建立Server服務

延伸閱讀4:Node.js — 從一個實例看Express 的運作方式

延伸閱讀5:Express GeneratorーWeb應用程式產生器的使用方式

延伸閱讀6:Node.js+Express — 製作CRUD簡易待辦清單(上)

延伸閱讀7:Node.js+Express — 製作CRUD簡易待辦清單(下)

--

--

Sean Yeh
Web Design Zone

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。