[實例] 使用NodeJs,Express+EJS製作靜態網站
前面幾篇我們學到NodeJs、Express與EJS樣版引擎的基本操作方式。這一篇我們想要藉由這些已知的觀念,來實際上製作一個由數個靜態頁組成的網站。並且介紹其他尚未提到的觀念。
網站規劃
在製作網站的過程中。首先必須先規劃一下,到底網站該呈現什麼樣子?那要怎麼規劃?
一般來說,就是需要一個網站的地圖(Sitemap),透過這個地圖,才有辦法逐一的進行網站的施工與製作。
或許你會問?很小的網站(五頁以內的網站)也需要地圖嗎?
其實這種狀況見仁見智,或許你天資聰穎,可以把五個頁面的架構通通存在自己的腦中。依照腦中的地圖施工。
但是你還是無法保證,網站上線營運3~5年後的某一天,當你被客戶要求保固維修而被迫不得不回想這個(舊網站)到底當時是如何規劃設計的時候,你可以記得所有的細節。
另外,如果這個網站的施工需要透過其他人一起協同合作的時候,有一張地圖作為共通討論的基礎,會比完全沒有資料來討論時來得好吧。
雖然這次我們要做的網站只有四個頁面(五頁以內的網站),符合上面討論的範疇。基於上述的兩個論點,我們產製一個簡單網站的地圖。
網站架構圖
網站架構圖可以用下面的圖示表示,這個圖示與一般的組織結構圖用的範本類似。
一個頁面以一個方框表示,方框與方框間上下排列並且連接在一起,表示出頁面的階層關係。
如果以一個組織結構來比喻的話,由於首頁(index)是整個網站最原始的開始頁面,可以說像是組織中的領導者一般,放在最上面的位置。其餘的單元頁面,例如聯絡(contact)、關於(about)、產品(product)等等,就像是各個不同部門一樣,一字排開放在第二層級。如果個個單元還有更多細節要放的話,可以在所屬的個個單元下面,依照目前的方式再分叉出一個層級。
請注意,在規劃架構的時候,雖然在架構邏輯上正確,但也不要走火入魔似的一路規劃超過四個以上的層級。這樣子會讓網站使用者很不容易找到他們需要的內容。
版面配置
從單一的網頁index來看,最少我們可以把檔案分割成Header與Footer以及中間內容Main Content三個區段。
如果是內容豐富的網站,在中間內容(Main Content)區段中,可以依照頁面不同的功能,分別放入不同的版面。
檔案拆分方式
規劃好架構與版面配置後,我們要思考『在這個專案中檔案要如何放置?』在這一件事的方向上,我們可以朝『如何做可以不要重複的製造出一樣的程式碼』這個方向來思考。
在頁面中重複的複製貼上同樣的程式碼,不僅讓網站變得肥大,也會導致日後維護上的不方便。除非你確定目前規劃的網站是「拋棄式的網站」,也就是說網站上線後一段時間,就可以下線了的網站,不然多多少少都要面臨網站維護的問題。
如果檔案結構在一開始的時候就沒有先規劃好,網站運用的時間越久,在維護上會越麻煩。畢竟大家不可能記得當時開發時的方式。
檔案大致上拆分的方式如下。
專案資料結構
就像是家裡的書房一樣,如果文具與書籍沒有分門別類的放好,臨時要找需要用的東西時,就會找不到。
同樣的道理,專案資料夾的結構要如何規劃也是很重要的。下面列出目前實例練習的專案資料夾規劃方式供大家參考。
- node_module: NodeJs 安裝後套件檔案
- views:放置樣版引擎ejs檔案的地方。
- public:放置靜態檔案的地方。裡面可以再依照靜態檔案的屬性,分為image圖片、css樣式檔、javaScript等資料夾。
- 其他放根目錄。由於這個網站是簡單網。只需要一個index.js檔案與package.json檔案一起放在根目錄。不需要再開資料夾放其他的js檔案。
當然,以上的規劃僅提供大家參考。大家可以依照不同專案做不同的規劃。
其原則就是把樣板、執行的程式、靜態檔案等不同功能的檔案,分開放,不要像義大利麵肉醬麵一樣的混在一起,難以分辨哪裡是肉醬、哪個番茄、哪些是麵。
如果你的專案部署後放到AWS或者是GCP等雲端空間上面的話,可能會把這個資料夾裡面的檔案放在storage之類的儲存空間上(例如AWS 上面的S3儲存空間)。這個public資料夾就不需要了。
規劃完畢之後,我們開始透過NodeJs與Express.js製作網站。
建立的步驟之前已經提到過,在此就不再贅述。大家有興趣的話,可以到下面幾篇參看。
下面是我們在這裡的假設
- 大家已經完成透過Express建立伺服器,並且:
- 可以進入首頁http://localhost:3000/ 並且看到首頁的內容。
- 已經安裝EJS樣版引擎
- 伺服器可以自動重新整理
- 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。如果建立完成後,整個資料夾的結構應該向下面圖示:
整個結構規劃成上面這樣子的原因是,我們想要將網站中每一個頁面都會利用到的位置拆出來,分別獨立為一個檔案(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, corruptirepudiandae 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 © 版權所有 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資料夾裡面的子資料夾進行分類。