Home > Web Front-end > JS Tutorial > body text

Create a content management system: nodePress

PHPz
Release: 2023-09-03 13:53:05
Original
1395 people have browsed it
<p>You have successfully created a flat file system content management system (CMS) using Go. The next step is to take the same idea and make a web server using Node.js. I'll show you how to load the library, create the server, and run the server. </p> <p>This CMS will use the site data structure introduced in the first tutorial, "Building a CMS: Structure and Style." So, download this basic structure and install it in a new directory. </p> <h2>Get nodes and node libraries</h2> <p>The easiest way to install Node.js on Mac is to use Homebrew. If you don’t have Homebrew installed yet, the tutorial Homebrew Revealed: The Ultimate Package Manager for OS X will show you how to install it. </p> <p>To install Node.js using Homebrew, enter the following command in the terminal: </p> <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">brew install node </pre><div class="contentsignin">Copy after login</div></div> <p>When completed, Node and npm commands will be fully installed on your Mac. For all other platforms, follow the instructions on the Node.js website. </p> <p>Please note: Many package managers are currently installing Node.js version 0.10. This tutorial assumes you have version 5.3 or higher. You can check your version by typing: </p> <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">node --version </pre><div class="contentsignin">Copy after login</div></div> The <p><code class="inline">node</code> command runs the JavaScript interpreter. The <code class="inline">npm</code> command is the package manager for Node.js and is used to install new libraries, create new projects, and run project scripts. Envato Tuts has many great tutorials and courses on Node.js and NPM. </p> <p>To install the web server's libraries, you must run the following command in the Terminal.app or iTerm.app program: </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">Copy after login</div></div> <p>Express is a web application development platform. It is similar to the goWeb library in Go. Handlebars is a template engine for creating pages. Moment is a library for working with dates. Marked is a great Markdown to HTML converter in JavaScript. Jade is an HTML shorthand language that makes it easy to create HTML. Morgan is a middleware library for Express that generates Apache standard log files. </p> <p>Another way to install the library is to download the source files for this tutorial. After downloading and unzipping, enter: </p> in the home directory <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm --install </pre><div class="contentsignin">Copy after login</div></div> <p>This will install everything needed to create the project. </p> <h2>nodePress.js</h2> <p>Now you can start creating the server. In the top-level directory of your project, create a file called nodePress.js, open it in the editor of your choice, and start adding the following code. I'll explain the code I put in the file. </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">Copy after login</div></div> <p>The server code starts by initializing all libraries used to create the server. Libraries without annotations with URLs are internal Node.js libraries. </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">Copy after login</div></div> <p>Next, I set up all global variables and library configuration. Using global variables is not the best software design practice, but it does work and helps in rapid development. </p> <p><code class="inline">parts</code> The variable is a hash array containing all the parts of the web page. Each page references the contents of this variable. It starts with the contents of the server.json file at the top of the server directory. </p> <p>I then used the information in the server.json file to create the full paths to the <code class="inline">styles</code> and <code class="inline">layouts</code> directories for this site. </p> <p>Then set three variables to empty values: <code class="inline">siteCSS</code>, <code class="inline">siteScripts</code>, and <code class="inline">mainPage</code>. These global variables will contain all CSS, JavaScript, and main index page content. These three projects are the most requested on any web server. Therefore, keeping them in memory saves time. If the <code class="inline">Cache</code> variable in the server.json file is false, these items will be re-read with every request. </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">Copy after login</div></div> <p>This code block is used to configure the Marked library to generate HTML from Markdown. Mostly I turn on tables and smartLists support. </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">Copy after login</div></div> The <p><code class="inline">parts</code> variable further loads parts from the <code class="inline">styles</code> and <code class="inline">layout</code> directories. Each file in the <code class="inline">parts</code> directory within the <code class="inline">site</code> directory is also loaded into the <code class="inline">parts</code> global variable. The filename without extension is the name used to store the contents of the file. These names are expanded in the Handlebars macro. </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">Copy after login</div></div> <p>The next piece of code defines the Handlebars helpers I defined for use in the web server: <code class="inline">save</code>, <code class="inline">date</code>, and <code class="inline">cdate</code>. The save assistant allows creating variables within the page. This version supports the goPress version where the name and value of the parameter are separated together with "|". You can also specify saving using two parameters. For example:</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">Copy after login</div></div> <p>This will produce the same result. I prefer the second approach, but the Handlebars library in Go doesn't allow multiple arguments. </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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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(/\&quot\;/g,"\""); } return (result); } </pre><div class="contentsignin">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</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">Copy after login</div></div> <p>这将运行 package.json 文件内的 <code>start</code> 脚本。</p> <p><img src="https://img.php.cn/upload/article/000/000/164/169372039225694.jpg" alt="Create a content management system: nodePress"></p> <p>将您的网络浏览器指向 <code>http://localhost:8080</code>,您将看到上面的页面。您可能已经注意到我在主页上添加了更多测试代码。对页面的所有更改都包含在本教程的下载中。它们大多只是一些小的调整,以更全面地测试功能并适应使用不同库的任何差异。最显着的区别是 Jade 库不使用 <code class="inline">$</code> 来命名变量,而 Amber 则使用。</p> <h2>结论</h2> <p>现在,您在 Go 和 Node.js 中拥有完全相同的平面文件系统 CMS。这只是您可以使用此平台构建的内容的表面。尝试并尝试新事物。这是创建您自己的网络服务器的最佳部分。</p>

The above is the detailed content of Create a content management system: nodePress. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!