<p>您已使用 Go 成功建立了平面檔案系統內容管理系統 (CMS)。下一步是採用同樣的理念,使用 Node.js 製作一個 Web 伺服器。我將向您展示如何載入庫、建立伺服器和運行伺服器。 </p>
<p>此 CMS 將使用第一個教學課程「建立 CMS:結構和樣式」中介紹的網站資料結構。因此,請下載此基本結構並將其安裝在新目錄中。 </p>
<h2>取得節點和節點庫</h2>
<p>在 Mac 上安裝 Node.js 最簡單的方法是使用 Homebrew。如果您尚未安裝 Homebrew,教學 Homebrew 揭秘:OS X 的終極套件管理器將向您展示如何安裝。 </p>
<p>要使用 Homebrew 安裝 Node.js,請在終端機中輸入以下指令:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">brew install node
</pre><div class="contentsignin">登入後複製</div></div>
<p>完成後,您的 Mac 上將完全安裝了 Node 和 npm 命令。對於所有其他平台,請按照 Node.js 網站上的說明進行操作。 </p>
<p>請注意:許多套件管理器目前正在安裝 Node.js 版本 0.10。本教學假設您擁有 5.3 或更高版本。您可以鍵入以下內容來檢查您的版本:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">node --version
</pre><div class="contentsignin">登入後複製</div></div>
<p><code class="inline">node</code> 指令執行 JavaScript 解譯器。 <code class="inline">npm</code> 指令是 Node.js 的套件管理器,用於安裝新程式庫、建立新專案以及執行專案腳本。 Envato Tuts 上有許多關於 Node.js 和 NPM 的精彩教學和課程。 </p>
<p>要安裝 Web 伺服器的庫,您必須在 Terminal.app 或 iTerm.app 程式中執行以下命令:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm install express --save
npm install handlebars --save
npm install moment --save
npm install marked --save
npm install jade --save
npm install morgan --save
</pre><div class="contentsignin">登入後複製</div></div>
<p>Express 是一個 Web 應用程式開發平台。它類似Go中的goWeb函式庫。 Handlebars 是用於建立頁面的模板引擎。 Moment 是一個用於處理日期的函式庫。 Marked 是一個很棒的 JavaScript 中 Markdown 到 HTML 轉換器。 Jade 是一種 HTML 速記語言,可輕鬆建立 HTML。 Morgan 是 Express 的中間件庫,可產生 Apache 標準日誌檔案。 </p>
<p>安裝庫的另一種方法是下載本教學的來源檔案。下載並解壓縮後,在主目錄中輸入:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm --install
</pre><div class="contentsignin">登入後複製</div></div>
<p>這將安裝創建該專案所需的一切。 </p>
<h2>nodePress.js</h2>
<p>現在您可以開始建立伺服器了。在專案的頂層目錄中,建立一個名為 nodePress.js 的文件,在您選擇的編輯器中開啟它,然後開始新增以下程式碼。我將解釋放入文件中的程式碼。 </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Load the libraries used.
//
var fs = require('fs');
var path = require("path");
var child_process = require('child_process');
var process = require('process');
var express = require('express'); // http://expressjs.com/en/
var morgan = require('morgan'); // https://github.com/expressjs/morgan
var Handlebars = require("handlebars"); // http://handlebarsjs.com/
var moment = require("moment"); // http://momentjs.com/
var marked = require('marked'); // https://github.com/chjj/marked
var jade = require('jade'); // http://jade-lang.com/
</pre><div class="contentsignin">登入後複製</div></div>
<p>伺服器程式碼從初始化用於建立伺服器的所有程式庫開始。沒有帶有網址的註解的函式庫是內部 Node.js 函式庫。 </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Setup Global Variables.
//
var parts = JSON.parse(fs.readFileSync('./server.json', 'utf8'));
var styleDir = process.cwd() + '/themes/styling/' + parts['CurrentStyling'];
var layoutDir = process.cwd() + '/themes/layouts/' + parts['CurrentLayout'];
var siteCSS = null;
var siteScripts = null;
var mainPage = null;
</pre><div class="contentsignin">登入後複製</div></div>
<p>接下來,我設定所有全域變數和庫配置。使用全域變數並不是最好的軟體設計實踐,但它確實有效並有助於快速開發。 </p>
<p><code class="inline">parts</code> 變數是一個包含網頁所有部分的雜湊數組。每個頁面都引用該變數的內容。它從伺服器目錄頂部的 server.json 檔案的內容開始。 </p>
<p>然後,我使用 server.json 檔案中的資訊來建立用於此網站的 <code class="inline">styles</code> 和 <code class="inline">layouts</code> 目錄的完整路徑。 </p>
<p>接著將三個變數設定為空值:<code class="inline">siteCSS</code>、<code class="inline">siteScripts</code> 和 <code class="inline">mainPage</code>。這些全域變數將包含所有 CSS、JavaScript 和主索引頁內容。這三個項目是任何 Web 伺服器上要求最多的項目。因此,將它們保留在記憶體中可以節省時間。如果 server.json 檔案中的 <code class="inline">Cache</code> 變數為 false,則每個請求都會重新讀取這些項目。 </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
</pre><div class="contentsignin">登入後複製</div></div>
<p>此程式碼區塊用於配置 Marked 庫以從 Markdown 產生 HTML。大多數情況下,我會打開表格和 smartLists 支援。 </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">parts["layout"] = fs.readFileSync(layoutDir + '/template.html', 'utf8');
parts["404"] = fs.readFileSync(styleDir + '/404.html', 'utf8');
parts["footer"] = fs.readFileSync(styleDir + '/footer.html', 'utf8');
parts["header"] = fs.readFileSync(styleDir + '/header.html', 'utf8');
parts["sidebar"] = fs.readFileSync(styleDir + '/sidebar.html', 'utf8');
//
// Read in the page parts.
//
var partFiles = fs.readdirSync(parts['Sitebase'] + "parts/");
partFiles.forEach(function(ele, index, array) {
parts[path.basename(ele, path.extname(ele))] = figurePage(parts['Sitebase'] + "parts/" + path.basename(ele, path.extname(ele)));
});
</pre><div class="contentsignin">登入後複製</div></div>
<p><code class="inline">parts</code> 變數進一步載入 <code class="inline">styles</code> 和 <code class="inline">layout</code> 目錄中的部分。 <code class="inline">site</code> 目錄內的 <code class="inline">parts</code> 目錄中的每個檔案也被載入到 <code class="inline">parts</code> 全域變數中。不含副檔名的檔案名稱是用來儲存檔案內容的名稱。這些名稱在 Handlebars 巨集中會擴展。 </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Setup Handlebar's Helpers.
//
//
// HandleBars Helper: save
//
// Description: This helper expects a
// "<name>" "<value>" where the name
// is saved with the value for future
// expansions. It also returns the
// value directly.
//
Handlebars.registerHelper("save", function(name, text) {
//
// Local Variables.
//
var newName = "", newText = "";
//
// See if the name and text is in the first argument
// with a |. If so, extract them properly. Otherwise,
// use the name and text arguments as given.
//
if(name.indexOf("|") > 0) {
var parts = name.split("|");
newName = parts[0];
newText = parts[1];
} else {
newName = name;
newText = text;
}
//
// Register the new helper.
//
Handlebars.registerHelper(newName, function() {
return newText;
});
//
// Return the text.
//
return newText;
});
//
// HandleBars Helper: date
//
// Description: This helper returns the date
// based on the format given.
//
Handlebars.registerHelper("date", function(dFormat) {
return moment().format(dFormat);
});
//
// HandleBars Helper: cdate
//
// Description: This helper returns the date given
// in to a format based on the format
// given.
//
Handlebars.registerHelper("cdate", function(cTime, dFormat) {
return moment(cTime).format(dFormat);
});
</pre><div class="contentsignin">登入後複製</div></div>
<p>下一段程式碼定義了我定義的在 Web 伺服器中使用的 Handlebars 幫助程式:<code class="inline">save</code>、<code class="inline">date</code> 和 <code class="inline">cdate</code>。儲存助手允許在頁面內建立變數。此版本支援 goPress 版本,其中參數的名稱和值一起以「|」分隔。您也可以使用兩個參數指定保存。例如:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:plaintext;toolbal:false;">{{save "name|Richard Guay"}}
{{save "newName" "Richard Guay"}}
Name is: {{name}}
newName is: {{newName}}
</pre><div class="contentsignin">登入後複製</div></div>
<p>這將產生相同的結果。我更喜歡第二種方法,但 Go 中的 Handlebars 庫不允許使用多個參數。 </p>
<p><code class="inline">date</code> 和 <code class="inline">cdate</code> 帮助程序格式化当前日期 (<code class="inline">date</code>) 或给定日期 (<code class="inline">cdate</code>)根据 <strong>moment.js</strong> 库格式化规则。 <code class="inline">cdate</code> 帮助程序期望渲染的日期是第一个参数并且具有 ISO 8601 格式。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Create and configure the server.
//
var nodePress = express();
//
// Configure middleware.
//
nodePress.use(morgan('combined'))
</pre><div class="contentsignin">登入後複製</div></div>
<p>现在,代码创建一个 Express 实例来配置实际的服务器引擎。 <code>nodePress.use()</code> 函数设置中间件软件。中间件是在每次调用服务器时提供服务的任何代码。在这里,我设置了 Morgan.js 库来创建正确的服务器日志输出。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Define the routes.
//
nodePress.get('/', function(request, response) {
setBasicHeader(response);
if((parts["Cache"] == true) && (mainPage != null)) {
response.send(mainPage);
} else {
mainPage = page("main");
response.send(mainPage);
}
});
nodePress.get('/favicon.ico', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/ico");
setBasicHeader(response);
response.sendFile('favicon.ico', options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Favicon was sent:', 'favicon.ico');
}
});
});
nodePress.get('/stylesheets.css', function(request, response) {
response.set("Content-Type", "text/css");
setBasicHeader(response);
response.type("css");
if((parts["Cache"] == true) && (siteCSS != null)) {
response.send(siteCSS);
} else {
siteCSS = fs.readFileSync(parts['Sitebase'] + 'css/final/final.css');
response.send(siteCSS);
}
});
nodePress.get('/scripts.js', function(request, response) {
response.set("Content-Type", "text/javascript");
setBasicHeader(response);
if((parts["Cache"] == true) && (siteScripts != null)) {
response.send(siteScripts);
} else {
siteScripts = fs.readFileSync(parts['Sitebase'] + 'js/final/final.js', 'utf8');
response.send(siteScripts);
}
});
nodePress.get('/images/:image', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/" + path.extname(request.params.image).substr(1));
setBasicHeader(response);
response.sendFile(request.params.image, options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Image was sent:', request.params.image);
}
});
});
nodePress.get('/posts/blogs/:blog', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, "index"));
});
nodePress.get('/posts/blogs/:blog/:post', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, request.params.post));
});
nodePress.get('/posts/news/:news', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, "index"));
});
nodePress.get('/posts/news/:news/:post', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, request.params.post));
});
nodePress.get('/:page', function(request, response) {
setBasicHeader(response);
response.send(page(request.params.page));
});
</pre><div class="contentsignin">登入後複製</div></div>
<p>这部分代码定义了实现 Web 服务器所需的所有路由。所有路由都运行 <code>setBasicHeader()</code> 函数来设置正确的标头值。所有针对页面类型的请求都会调用 <code>page()</code> 函数,而所有针对 post 类型页面的请求都会调用 <code>posts()</code> 函数。</p>
<p><code class="inline">Content-Type</code> 的默认值为 HTML。因此,对于 CSS、JavaScript 和图像,<code class="inline">Content-Type</code> 显式设置为其适当的值。</p>
<p>您还可以使用 <code>put</code>、<code>delete</code> 和 <code>post</code> REST 动词定义路由。这个简单的服务器仅使用 <code>get</code> 动词。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Start the server.
//
var addressItems = parts['ServerAddress'].split(':');
var server = nodePress.listen(addressItems[2], function() {
var host = server.address().address;
var port = server.address().port;
console.log('nodePress is listening at http://%s:%s', host, port);
});
</pre><div class="contentsignin">登入後複製</div></div>
<p>在定义所使用的不同函数之前要做的最后一件事是启动服务器。 server.json 文件包含 DNS 名称(此处为 <code>localhost</code>)和服务器的端口。解析后,服务器的 <code>listen()</code> 函数使用端口号来启动服务器。服务器端口打开后,脚本会记录服务器的地址和端口。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: setBasicHeader
//
// Description: This function will set the basic header information
// needed.
//
// Inputs:
// response The response object
//
function setBasicHeader(response) {
response.append("Cache-Control", "max-age=2592000, cache");
response.append("Server", "nodePress - a CMS written in node from Custom Computer Tools: http://customct.com.");
}
</pre><div class="contentsignin">登入後複製</div></div>
<p>定义的第一个函数是 <code>setBasicHeader()</code> 函数。该函数设置响应头,告诉浏览器将页面缓存一个月。它还告诉浏览器该服务器是nodePress服务器。如果您需要任何其他标准标头值,您可以使用 <code>response.append()</code> 函数在此处添加它们。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: page
//
// Description: This function processes a page request
//
// Inputs:
// page The requested page
//
function page(page) {
//
// Process the given page using the standard layout.
//
return (processPage(parts["layout"], parts['Sitebase'] + "pages/" + page));
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>page()</code> 函数将页面的布局模板以及页面在服务器上的位置发送到 <code>processPage()</code> 函数。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: post
//
// Description: This function processes a post request
//
// Inputs:
// type The type of post.
// cat The category of the post.
// post The requested post
//
function post(type, cat, post) {
//
// Process the post given the type and the post name.
//
return (processPage(parts["layout"], parts['Sitebase'] + "posts/" + type + "/" + cat + "/" + post));
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>post()</code> 函数就像 <code>page()</code> 函数,不同之处在于帖子有更多项目来定义每个帖子。在这个系列的服务器中,一个post包含一个<code>type</code>、category,以及实际的<code>post</code>。类型为 <code>blogs</code> 或 <code>news</code>。类别是 <code>flatcms</code>。由于这些代表目录名称,因此您可以将它们设为您想要的任何名称。只需将命名与文件系统中的名称相匹配即可。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: processPage
//
// Description: This function processes a page for the CMS.
//
// Inputs:
// layout The layout to use for the page.
// page Path to the page to render.
//
function processPage(layout, page) {
//
// Get the pages contents and add to the layout.
//
var context = {};
context = MergeRecursive(context, parts);
context['content'] = figurePage(page);
context['PageName'] = path.basename(page, path.extname(page));
//
// Load page data.
//
if(fileExists(page + ".json")) {
//
// Load the page's data file and add it to the data structure.
//
context = MergeRecursive(context, JSON.parse(fs.readFileSync(page + '.json', 'utf8')));
}
//
// Process Handlebars codes.
//
var template = Handlebars.compile(layout);
var html = template(context);
//
// Process all shortcodes.
//
html = processShortCodes(html);
//
// Run through Handlebars again.
//
template = Handlebars.compile(html);
html = template(context);
//
// Return results.
//
return (html);
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>processPage()</code> 函数获取要呈现的页面内容的布局和路径。该函数首先创建 <code>parts</code> 全局变量的本地副本,并添加“contents”主题标签以及调用 <code>figurePage()</code> 函数的结果。然后,它将 <code>PageName</code> 哈希值设置为页面名称。</p>
<p>然后,该函数使用 Handlebars 将页面内容编译到布局模板。之后, <code>processShortCodes()</code> 函数将展开页面上定义的所有短代码。然后,Handlebars 模板引擎再次检查代码。然后浏览器接收结果。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: processShortCodes
//
// Description: This function takes a string and
// processes all of the shortcodes in
// the string.
//
// Inputs:
// content String to process
//
function processShortCodes(content) {
//
// Create the results variable.
//
var results = "";
//
// Find the first match.
//
var scregFind = /\-\[([^\]]*)\]\-/i;
var match = scregFind.exec(content);
if (match != null) {
results += content.substr(0,match.index);
var scregNameArg = /(\w+)(.*)*/i;
var parts = scregNameArg.exec(match[1]);
if (parts != null) {
//
// Find the closing tag.
//
var scregClose = new RegExp("\\-\\[\\/" + parts[1] + "\\]\\-");
var left = content.substr(match.index + 4 + parts[1].length);
var match2 = scregClose.exec(left);
if (match2 != null) {
//
// Process the enclosed shortcode text.
//
var enclosed = processShortCodes(content.substr(match.index + 4 + parts[1].length, match2.index));
//
// Figure out if there were any arguments.
//
var args = "";
if (parts.length == 2) {
args = parts[2];
}
//
// Execute the shortcode.
//
results += shortcodes[parts[1]](args, enclosed);
//
// Process the rest of the code for shortcodes.
//
results += processShortCodes(left.substr(match2.index + 5 + parts[1].length));
} else {
//
// Invalid shortcode. Return full string.
//
results = content;
}
} else {
//
// Invalid shortcode. Return full string.
//
results = content;
}
} else {
//
// No shortcodes found. Return the string.
//
results = content;
}
return (results);
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>processShortCodes()</code> 函数将网页内容作为字符串并搜索所有短代码。短代码是类似于 HTML 标签的代码块。一个例子是:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbal:false;">-[box]-
<p>This is inside a box</p>
-[/box]-
</pre><div class="contentsignin">登入後複製</div></div>
<p>此代码在 HTML 段落周围有一个 <code>box</code> 的简码。其中 HTML 使用 <code><</code> 和 </code>>></code>,短代码使用 <code>-[</code> 和 </code>>]-</code>。在名称后面,可以包含或不可以包含包含短代码参数的字符串。</p>
<p><code>processShortCodes()</code> 函数查找短代码,获取其名称和参数,找到末尾以获取内容,处理短代码的内容,使用参数和内容执行短代码,将结果添加到完成中页面,并在页面的其余部分搜索下一个短代码。循环是通过递归调用函数来执行的。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Define the shortcodes function array.
//
var shortcodes = {
'box': function(args, inside) {
return ("<div class='box'>" + inside + "</div>");
},
'Column1': function(args, inside) {
return ("<div class='col1'>" + inside + "</div>");
},
'Column2': function(args, inside) {
return ("<div class='col2'>" + inside + "</div>");
},
'Column1of3': function(args, inside) {
return ("<div class='col1of3'>" + inside + "</div>");
},
'Column2of3': function(args, inside) {
return ("<div class='col2of3'>" + inside + "</div>");
},
'Column3of3': function(args, inside) {
return ("<div class='col3of3'>" + inside + "</div>");
},
'php': function(args, inside) {
return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: php'>" + inside + "</pre><div class="contentsignin">登入後複製</div></div></div>");
},
'js': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: javascript'>" + inside + "</pre><div class="contentsignin">登入後複製</div></div></div>");
},
'html': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: html'>" + inside + "</pre><div class="contentsignin">登入後複製</div></div></div>");
},
'css': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: css'>" + inside + "</pre><div class="contentsignin">登入後複製</div></div></div>");
}
};
</pre>
<p>下一节定义 <code>shortcodes</code> json 结构,该结构定义与其函数关联的短代码的名称。所有短代码函数都接受两个参数:<code>args</code> 和 <code>inside</code>。 <code>args</code> 是名称和空格之后、标签结束之前的所有内容。 <code>inside</code> 是开始和结束短代码标记包含的所有内容。这些功能是基本功能,但您可以创建一个短代码来执行您能在 JavaScript 中想到的任何功能。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: figurePage
//
// Description: This function figures the page type
// and loads the contents appropriately
// returning the HTML contents for the page.
//
// Inputs:
// page The page to load contents.
//
function figurePage(page) {
var result = "";
if (fileExists(page + ".html")) {
//
// It's an HTML file. Read it in and send it on.
//
result = fs.readFileSync(page + ".html");
} else if (fileExists(page + ".amber")) {
//
// It's a jade file. Convert to HTML and send it on. I
// am still using the amber extension for compatibility
// to goPress.
//
var jadeFun = jade.compileFile(page + ".amber", {});
// Render the function
var result = jadeFun({});
} else if (fileExists(page + ".md")) {
//
// It's a markdown file. Convert to HTML and send
// it on.
//
result = marked(fs.readFileSync(page + ".md").toString());
//
// This undo marked's URI encoding of quote marks.
//
result = result.replace(/\"\;/g,"\"");
}
return (result);
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>figurePage()</code> 函数接收服务器上页面的完整路径。然后,此函数根据扩展名测试它是否为 HTML、Markdown 或 Jade 页面。我仍然在 Jade 中使用 .amber,因为那是我在 goPress 服务器上使用的库。所有 Markdown 和 Jade 内容都会先转换为 HTML,然后再传递给调用例程。由于 Markdown 处理器将所有引号翻译为 <code>"</code>,因此我在传回之前将它们翻译回来。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: fileExists
//
// Description: This function returns a boolean true if
// the file exists. Otherwise, false.
//
// Inputs:
// filePath Path to a file in a string.
//
function fileExists(filePath) {
try {
return fs.statSync(filePath).isFile();
} catch (err) {
return false;
}
}
</pre><div class="contentsignin">登入後複製</div></div>
<p><code>fileExists()</code> 函数是 <code>fs.exists()</code> 函数的替代品,该函数曾经是 Node.js 的 <code>fs</code> 库的一部分。它使用 <code>fs.statSync()</code> 函数来尝试获取文件的状态。如果发生错误,则会返回 <code>false</code>。否则,返回 <code>true</code>。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: MergeRecursive
//
// Description: Recursively merge properties of two objects
//
// Inputs:
// obj1 The first object to merge
// obj2 The second object to merge
//
function MergeRecursive(obj1, obj2) {
for (var p in obj2) {
try {
// Property in destination object set; update its value.
if (obj2[p].constructor == Object) {
obj1[p] = MergeRecursive(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch (e) {
// Property in destination object not set; create it and set its value.
obj1[p] = obj2[p];
}
}
return obj1;
}
</pre><div class="contentsignin">登入後複製</div></div>
<p>最后一个函数是 <code>MergeRecursive()</code> 函数。它将第二个传递对象复制到第一个传递对象中。在添加特定于页面的部分之前,我利用它将主 <code>parts</code> 全局变量复制到本地副本中。</p>
<h3>本地运行</h3>
<p>保存文件后,您可以使用以下命令运行服务器:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">node nodePress.js
</pre><div class="contentsignin">登入後複製</div></div>
<p>或者,您可以使用 package.json 文件中的 <code class="inline">npm</code> 脚本。您可以像这样运行 npm 脚本:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm start
</pre><div class="contentsignin">登入後複製</div></div>
<p>这将运行 package.json 文件内的 <code>start</code> 脚本。</p>
<p><img src="https://img.php.cn/upload/article/000/000/164/169372039225694.jpg" alt="建立內容管理系統:nodePress"></p>
<p>将您的网络浏览器指向 <code>http://localhost:8080</code>,您将看到上面的页面。您可能已经注意到我在主页上添加了更多测试代码。对页面的所有更改都包含在本教程的下载中。它们大多只是一些小的调整,以更全面地测试功能并适应使用不同库的任何差异。最显着的区别是 Jade 库不使用 <code class="inline">$</code> 来命名变量,而 Amber 则使用。</p>
<h2>结论</h2>
<p>现在,您在 Go 和 Node.js 中拥有完全相同的平面文件系统 CMS。这只是您可以使用此平台构建的内容的表面。尝试并尝试新事物。这是创建您自己的网络服务器的最佳部分。</p>
以上是建立內容管理系統:nodePress的詳細內容。更多資訊請關注PHP中文網其他相關文章!