aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/learn/server-side/express_nodejs
diff options
context:
space:
mode:
Diffstat (limited to 'files/zh-cn/learn/server-side/express_nodejs')
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/deployment/index.html526
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/development_environment/index.html390
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html89
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html89
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html112
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html70
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html91
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html69
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html58
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html139
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html120
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/home_page/index.html134
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/index.html87
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html69
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/displaying_data/template_primer/index.html149
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/create_author_form/index.html155
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/create_book_form/index.html212
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/create_bookinstance_form/index.html150
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/create_genre_form/index.html298
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/delete_author_form/index.html165
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/index.html286
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/forms/update_book_form/index.html189
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/index.html72
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/installing_on_pws_cloud_foundry/index.html242
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/introduction/index.html525
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/mongoose/index.html831
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/routes/index.html425
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/skeleton_website/index.html476
-rw-r--r--files/zh-cn/learn/server-side/express_nodejs/tutorial_local_library_website/index.html93
29 files changed, 6311 insertions, 0 deletions
diff --git a/files/zh-cn/learn/server-side/express_nodejs/deployment/index.html b/files/zh-cn/learn/server-side/express_nodejs/deployment/index.html
new file mode 100644
index 0000000000..b4802ca3c5
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/deployment/index.html
@@ -0,0 +1,526 @@
+---
+title: 'Express 教程 7: 部署到生产环境'
+slug: learn/Server-side/Express_Nodejs/deployment
+tags:
+ - Express
+ - Learn
+ - Node
+ - 初学者
+ - 部署
+translation_of: Learn/Server-side/Express_Nodejs/deployment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</div>
+
+<p class="summary">现在你已经创建(并测试)了一个不错的 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">本地图书馆</a> 网站了,你打算把它发布到一个公共网络服务器,这样图书馆职工和网络上的其他成员就可以访问它了。这篇文章总结了你可以怎样找到一台主机部署你的网站,以及你需要为站点准备到生产环境做什么。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>完成前面所有的指南主题,包括 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>学习你可以怎样以及在哪里部署一个Express 应用到生产环境。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>一旦您的站点完成(或完成“足够”以开始公共测试),您将需要将其托管在比您的个人开发计算机,更公开和可访问的地方。</p>
+
+<p>到目前为止,您一直在<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/development_environment">开发环境</a>中工作,使用 Express / Node 作为Web服务器,将您的站点共享到本地浏览器/网络,并使用(不安全的)开发设置运行您的网站,以显示调试和其他私人信息。在您可以在外部托管网站之前,您首先必须:</p>
+
+<ul>
+ <li>选择托管Express 应用程序的环境。</li>
+ <li>对项目设置进行一些更改。</li>
+ <li>设置生产级别的基础架构,以服务您的网站。</li>
+</ul>
+
+<p>本教程提供了,有关选择托管站点的选项的一些指导,简要概述了为使您的Express 应用程序准备好生产,所需执行的操作,以及如何将LocalLibrary 网站安装到 <a href="https://www.heroku.com/">Heroku</a>云托管上的工作示例服务。</p>
+
+<p>请记住,您不必使用Heroku  - 还有其他托管服务可用。我们还提供了一个单独的教程,以展示如何在 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry">PWS/Cloud Foundry </a>上安装LocalLibrary。</p>
+
+<h2 id="什么是生产环境?">什么是生产环境?</h2>
+
+<p>生产环境是服务器计算机提供的环境,您可以在其中运行网站,以供外部使用。环境包括:</p>
+
+<ul>
+ <li>网站运行的计算机硬件。</li>
+ <li>操作系统(例如Linux或Windows)。</li>
+ <li>编程语言运行库和框架库,在其上编写您的网站。</li>
+ <li>Web 服务器基础结构,可能包含Web服务器,反向代理,负载平衡器等。</li>
+ <li>您的网站所依赖的数据库。</li>
+</ul>
+
+<p>服务器计算机,可以位于您的场所,并通过快速链接,连接到 Internet,但使用 “托管在云上” 的计算机更为常见。这实际上意味着,您的代码运行在托管公司的数据中心的某台远程计算机(或可能是“虚拟”计算机)。远程服务器,通常会以特定价格提供互联网连接,和一些保证级别的计算资源(例如CPU,RAM,存储器等)。</p>
+
+<p>这种可远程访问的计算/网络硬件,称为基础架构即服务(IaaS)。许多IaaS 供应商,提供预安装特定操作系统的选项,您必须在其上,安装生产环境的其他组件。其他供应商,允许您选择功能更全面的环境,可能包括完整的 node 设置。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 预构建环境,可以使您的网站设置变得非常简单,因为它们会减少配置,但可用选项可能会限制您使用不熟悉的服务器(或其他组件),并且可能基于较旧版本的操作系统。通常最好自己安装组件,以便获得所需的组件,并且当您需要升级系统的某些部分时,您可以知道从哪里开始!</p>
+</div>
+
+<p>其他托管服务提供商,支持 Express 作为平台即服务(PaaS)产品的一部分。使用此类托管时,您无需担心大多数生产环境(服务器,负载平衡器等),因为主机平台会为您处理这些问题。这使得部署非常简单,因为您只需要专注于 Web 应用程序,而不是任何其他服务器基础结构。</p>
+
+<p>一些开发人员选择 IaaS ,相对于 PaaS ,IaaS 提供更高灵活性,而其他开发人员偏好 PaaS 的降低维护开销,和更轻松的扩展性。当您在一开始使用时,在 PaaS 系统上设置您的网站,要容易得多,因此我们将在本教程中使用 PaaS。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 如果您选择Node/Express友好的托管服务提供商,他们应该提供,有关如何使用Web服务器,应用程序服务器,反向代理等不同配置,来设置 Express 网站的说明。例如,在<a href="https://www.digitalocean.com/community/tutorials?q=node">数字海洋node社区文档</a>中,有许多各种配置的手把手指南。</p>
+</div>
+
+<h2 id="选择一个主机供应商">选择一个主机供应商</h2>
+
+<p>众所周知,众多托管服务提供商,都积极支持或与Node(和Express)合作。这些供应商提供不同类型的环境(IaaS,PaaS),以及不同价格的不同级别的计算和网络资源。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 有很多托管解决方案,他们的服务和定价,可能会随着时间而改变。虽然我们在下面介绍几个选项,但在选择托管服务提供商之前,有必要自己进行互联网搜索。</p>
+</div>
+
+<p>选择主机时需要考虑的一些事项:</p>
+
+<ul>
+ <li>您的网站可能有多忙,以及满足该需求所需的数据,和计算资源的成本。</li>
+ <li>水平扩展(添加更多机器)和垂直扩展(升级到更强大的机器)的支持级别,以及这样做的成本。</li>
+ <li>供应商有数据中心的地方,因此访问可能是最快的。</li>
+ <li>主机正常运行时间和停机时间的历史表现。</li>
+ <li>用于管理站点的工具 - 易于使用且安全(例如 SFTP 与 FTP)。</li>
+ <li>用于监控服务器的内置框架。</li>
+ <li>已知限制。有些主机会故意阻止某些服务(例如电子邮件)。其他在某些价格层中,仅提供一定数小时的 “实时时间”,或者仅提供少量存储空间。</li>
+ <li>额外的好处。一些提供商将提供免费域名和SSL证书支持,否则您将不得不为此另外支付费用。</li>
+ <li>您所依赖的“免费”等级,是否会随着时间的推移而过期,以及迁移到更昂贵等级的成本,是否意味着您最好在一开始就使用其他服务!</li>
+</ul>
+
+<p>当你刚开始时,好消息是有很多网站提供“免费”的计算环境,尽管有一些条件。例如, <a href="https://www.heroku.com/">Heroku </a>“永远” 提供免费但资源有限的PaaS 环境,而 <a href="http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-free-tier.html">Amazon Web Services</a>, <a href="https://azure.microsoft.com/en-us/pricing/details/app-service/">Microsoft Azure </a>和开源选项 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry">PWS/Cloud Foundry </a>在您第一次加入时,提供免费信用额度。</p>
+
+<p>许多提供商还拥有“基本”层,可提供更多有用的计算能力,和更少的限制。举例来说, <a href="https://www.digitalocean.com/">Digital Ocean</a> 是一个流行的托管服务提供商,它提供了一个相对便宜的基本计算层(在本教程写作时,是每月5美元的较低范围)。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 请记住,价格不是唯一的选择标准。如果您的网站成功,可能会发现可扩展性是最重要的考虑因素。</p>
+</div>
+
+<h2 id="准备好发布你的网站">准备好发布你的网站</h2>
+
+<p>发布网站时,要考虑的主要问题是网络安全性和性能。至少,您需要删除开发期间,错误页面上包含的堆栈跟踪,整理日志记录,并设置适当的标头,以避免许多常见的安全威胁。</p>
+
+<p>在以下小节中,我们概述了您应该对应用进行的、最重要的更改。</p>
+
+<div class="note">
+<p><strong>提示:</strong> Express文档中还有其他有用的提示 - 请参阅“<a href="https://expressjs.com/en/advanced/best-practice-performance.html">生产最佳实践:性能和可靠性</a>”,以及“<a href="https://expressjs.com/en/advanced/best-practice-security.html">生产最佳实践:安全性</a>”。</p>
+</div>
+
+<h3 id="设置_NODE_ENV_为_'production'">设置 NODE_ENV 为 'production'</h3>
+
+<p>我们可以通过将 <code>NODE_ENV</code> 环境变量,设置为 production ,来删除错误页面中的堆栈跟踪(默认设置为 “development” )。除了生成较为不详细的错误消息之外,还要将变量设置为生产缓存视图模板,和从CSS扩展生成的CSS文件。测试表明,将<code>NODE_ENV</code>设置为生产,可以将应用程序性能提高三倍!</p>
+
+<p>可以使用导出或环境文件,或使用OS初始化系统,以进行此更改。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 这实际上是在环境设置,而不是应用中所做的更改,但重要的是,要注意这里!我们将在下面,展示如何为我们的托管示例设置。</p>
+</div>
+
+<h3 id="Log_appropriately">Log appropriately</h3>
+
+<p>记录呼叫会对高流量网站产生影响。在生产环境中,您可能需要记录网站活动(例如,跟踪流量,或记录API调用),但您应尝试最小化为调试目的而添加的日志记录量。</p>
+
+<p>在生产环境中,最小化“调试”日志记录的一种方法,是使用类似<a href="https://www.npmjs.com/package/debug">调试debug </a>的模块,允许您通过设置环境变量,来控制执行的日志记录。例如,下面的代码片段,显示了如何设置“author”日志记录。调试变量使用名称“author”声明,并且将自动显示,来自此对象的所有日志的前缀“author”。</p>
+
+<pre class="brush: js"><strong>var debug = require('debug')('author');</strong>
+
+// Display Author update form on GET
+exports.author_update_get = function(req, res, next) {
+
+ req.sanitize('id').escape().trim();
+ Author.findById(req.params.id, function(err, author) {
+ if (err) {
+<strong> debug('update error:' + err);</strong>
+ return next(err);
+  }
+ //On success
+ res.render('author_form', { title: 'Update Author', author: author });
+ });
+
+};</pre>
+
+<p>然后,您可以通过在<code>DEBUG</code>环境变量中,将它们指定为逗号分隔列表,来启用特定日志集。您可以设置显示作者和书籍日志的变量,如图所示(也支持通配符)。</p>
+
+<pre class="brush: bash">#Windows
+set DEBUG=author,book
+
+#Linux
+export DEBUG="author,book"
+</pre>
+
+<div class="note">
+<p><strong>挑战:</strong> 调用<code>debug</code>可以替换您以前使用<code>console.log()</code>或<code>console.error()</code>执行的日志记录。通过调试模块<a href="https://www.npmjs.com/package/debug">debug</a>进行日志记录,替换代码中的所有<code>console.log()</code>调用。通过设置 DEBUG 变量,并在其中记录对日志记录的影响,在开发环境中,打开和关闭日志记录。</p>
+</div>
+
+<p>如果您需要记录网站活动,可以使用 Winston 或 Bunyan 等日志库。有关此主题的更多信息,请参阅:<a href="https://expressjs.com/en/advanced/best-practice-performance.html">生产最佳实践:性能和可靠性</a>。</p>
+
+<h3 id="使用_gzipdeflate_压缩响应文件">使用 gzip/deflate 压缩响应文件</h3>
+
+<p>Web服务器,通常可以压缩发送回客户端的 HTTP 响应,从而显着减少客户端获取和加载页面所需的时间。使用的压缩方法,取决于客户端在请求中支持的解压缩方法(如果不支持压缩方法,则响应将以未压缩的方式发送)。</p>
+
+<p>您可以使用压缩中间件 <a href="https://www.npmjs.com/package/compression">compression</a>,将其添加到您的站点。通过在项目的根目录下,运行以下命令,将其安装到项目中。</p>
+
+<pre class="brush: bash">npm install compression</pre>
+
+<p>打开<strong>./app.js</strong>,并导入压缩库,如图所示。使用<code>use()</code>方法,将压缩库添加到中间件链(这应该出现在您想要压缩的任何路由之前 - 在本教程这种情况下,全部都是!)</p>
+
+<pre class="brush: js">var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site
+<strong>var compression = require('compression');</strong>
+
+// Create the Express application object
+var app = express();
+
+...
+
+<strong>app.use(compression()); //Compress all routes</strong>
+
+app.use(express.static(path.join(__dirname, 'public')));
+
+app.use('/', indexRouter);
+app.use('/users', usersRouter);
+app.use('/catalog', catalogRouter);  // Add catalog routes to middleware chain.
+
+...
+</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 对于生产中流量较大的网站,您不会使用此中间件。相反,你会使用像 Nginx 这样的反向代理。</p>
+</div>
+
+<h3 id="使用_Helmet_避免被常见漏洞侵袭">使用 Helmet 避免被常见漏洞侵袭</h3>
+
+<p><a href="https://www.npmjs.com/package/helmet">Helmet</a> 是一个中间件包,可以通过设置适当的 HTTP 标头,来帮助保护您的应用,免受一些众所周知的 Web 漏洞的影响(有关它设置的标头/防护漏洞的详细信息,请参阅<a href="https://helmetjs.github.io/docs/">文档</a>)。</p>
+
+<p>通过在项目的根目录下,运行以下命令,将其安装到项目中。</p>
+
+<pre class="brush: bash">npm install helmet
+</pre>
+
+<p>打开<strong>./app.js</strong>,并导入如图所示的<em> helmet</em> 库。然后使用<code>use()</code>方法将模块添加到中间件链。</p>
+
+<pre class="brush: js">var compression = require('compression');
+<strong>var helmet = require('helmet');
+</strong>
+// Create the Express application object
+var app = express();
+
+<strong>app.use(helmet())</strong>;
+...</pre>
+
+<div class="note">
+<p id="production-best-practices-performance-and-reliability"><strong>注意:</strong> 上面的命令,添加了对大多数站点有意义的可用标头子集。您可以按照<a href="https://www.npmjs.com/package/helmet">npm</a>上的说明,根据需要添加/禁用特定标头。</p>
+</div>
+
+<h2 id="例子:在_Heroku_上安装一个本地图书馆">例子:在 Heroku 上安装一个本地图书馆</h2>
+
+<p>本节提供了如何在<a href="http://heroku.com">Heroku PaaS cloud</a>云上安装LocalLibrary的实际演示。</p>
+
+<h3 id="为什么选择_Heroku">为什么选择 Heroku?</h3>
+
+<p>Heroku 是运行时间最长,且最受欢迎的基于云的 PaaS 服务之一。它最初只支持 Ruby 应用程序,但现在可用于托管来自许多编程环境的应用程序,包括Node(以及Express)!</p>
+
+<p>我们选择使用 Heroku 有以下几个原因:   </p>
+
+<ul>
+ <li>Heroku 有一个<a href="https://www.heroku.com/pricing">免费套餐</a>(尽管有一些限制)。</li>
+ <li>作为PaaS,Heroku为我们提供了大量的 Web 基础架构。这使得入门更加容易,因为您不必担心服务器,负载平衡器,反向代理,崩溃时重新启动网站,或者 Heroku 为我们提供的任何其他 Web 基础结构。</li>
+ <li>虽然它确实有一些限制,但这些不会影响这个特定的应用程序。例如:
+ <ul>
+ <li>Heroku只提供短期存储,因此用户上传的文件无法安全地存储在Heroku本身。</li>
+ <li>如果半小时内没有请求,免费套餐将使不活动的网络应用程序进入睡眠。然后,该网站可能需要几秒钟才能被唤醒。</li>
+ <li>免费套餐将您网站运行的时间,限制为每月一定的小时数(不包括网站“睡着”的时间)。这对于低使用/演示站点来说很好,但如果需要100%的正常运行时间,则不适用。</li>
+ <li><a href="https://devcenter.heroku.com/articles/limits">Heroku 官方文档</a>中列出的其他限制。</li>
+ </ul>
+ </li>
+ <li>大多数情况下它只是可以工作,如果你最终喜欢它,并希望升级,那么扩展你的应用程序非常容易。</li>
+</ul>
+
+<p>虽然 Heroku 非常适合举办此演示,但它可能并不适合您的真实网站。 Heroku可以轻松设置和扩展,但代价是灵活性较低,而且一旦退​​出免费套餐,可能会花费更多。</p>
+
+<h3 id="Heroku_如何工作?"> Heroku 如何工作?</h3>
+
+<p>Heroku在一个或多个“<a href="https://devcenter.heroku.com/articles/dynos">Dynos</a>”中运行网站,这些“Dynos”是独立的虚拟化Unix容器,提供运行应用程序所需的环境。 Dynos 是完全隔离的,并且有一个短暂的文件系统(一个短暂的文件系统,每次dyno重新启动时都会清理/清空)。 dynos 默认共享的唯一内容,是应用程序<a href="https://devcenter.heroku.com/articles/config-vars">配置变量</a>。 Heroku内部使用负载均衡器,将Web流量分配给所有“web”dynos。由于它们之间没有任何共享,Heroku可以通过添加更多dynos,来水平扩展应用程序(当然,您可能还需要扩展数据库,以接受其他连接)。</p>
+
+<p>由于文件系统是短暂的,因此无法直接安装应用程序所需的服务(例如数据库,队列,缓存系统,存储,电子邮件服务等)。相反,Heroku Web应用程序使用 Heroku 或第三方作为独立“附加组件”提供的支持服务。连接到Web应用程序后,可以通过环境变量,在Web应用程序中访问附加服务。</p>
+
+<p>为了执行您的应用程序,Heroku需要能够设置适当的环境和依赖关系,并了解它是如何启动的。对于Node应用程序,它所需的所有信息都是从<strong>package.json</strong>文件中获取的。</p>
+
+<p>开发人员使用特殊的客户端应用程序/终端,与Heroku交互,这很像Unix bash脚本。这允许您上传存储在git存储库中的代码,检查正在运行的进程,查看日志,设置配置变量等等!</p>
+
+<p>为了让我们的应用程序在Heroku上工作,我们需要将我们的Express Web应用程序放入git存储库,并对 package.json 进行一些小的更改。完成后,我们可以设置Heroku帐户,获取Heroku客户端,并使用它来安装我们的网站。</p>
+
+<p>这是您开始教程所需的全部概述(有关更全面的指南,请参阅<a href="https://devcenter.heroku.com/articles/getting-started-with-nodejs">带有Node.js的Heroku入门</a>)。</p>
+
+<h3 id="在_Github_上创建一个应用仓库">在 Github 上创建一个应用仓库</h3>
+
+<p>Heroku 与 <strong>git </strong>源代码版本控制系统紧密集成,使用它来上传/同步您对实时运行系统所做的任何更改。它通过添加一个名为 heroku 的新 Heroku“远程”存储库,来指向您在Heroku云上的源存储库。在开发期间,您使用 git 在“主”存储库 master 中存储更改。如果要部署站点,请将更改同步到 Heroku 存储库。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 如果您习惯于遵循良好的软件开发实践,那么您可能已经在使用git或其他一些SCM系统。如果您已有git存储库,则可以跳过此步骤。</p>
+</div>
+
+<p>有很多方法可以使用git,但最简单的方法之一,是首先在<a href="https://github.com/">GitHub</a>上建立一个帐户,在那里创建存储库,然后在本地同步它:</p>
+
+<ol>
+ <li>访问 <a href="https://github.com/">https://github.com/</a> 并创建一个帐户。</li>
+ <li>登录后,单击顶部工具栏中的<strong> +</strong> 号链接,然后选择新建存储库<strong>New repository</strong>。</li>
+ <li>填写此表单上的所有字段。虽然这些不是强制性的,但强烈建议使用它们。
+ <ul>
+ <li>输入新的存储库名称(例如,express-locallibrary-tutorial)和描述(例如 “以Express(node)编写的本地图书馆网站”)。</li>
+ <li>在 Add .gitignore 选择列表中选择 <strong>Node</strong>。</li>
+ <li>在添加许可证 <em>Add license</em> 选择列表中,选择您偏好的许可证。</li>
+ <li>点选 <strong>使用自述文件初始化此存储库 </strong>“<strong>Initialize this repository with a README</strong>”</li>
+ </ul>
+ </li>
+ <li>按 <strong>Create repository</strong>.</li>
+ <li>单击新仓库页面上的绿色“克隆或下载”按钮 "<strong>Clone or download</strong>"。</li>
+ <li>从显示的对话框的文本字段,复制URL值(它应该类似于:<strong>https://github.com/<em>&lt;your_git_user_id&gt;</em>/express-locallibrary-tutorial.git</strong>)。</li>
+</ol>
+
+<p>现在创建了存储库(“repo”),我们将要在本地计算机上克隆它:</p>
+
+<ol>
+ <li>为您的本地计算机安装git(您可以在<a href="https://git-scm.com/downloads">此处</a>找到不同平台的版本)。</li>
+ <li>打开命令提示符/终端,并使用您在上面复制的 URL ,克隆clone存储库:
+ <pre class="brush: bash">git clone https://github.com/<strong><em>&lt;your_git_user_id&gt;</em></strong>/express-locallibrary-tutorial.git
+</pre>
+ 这将在当前时间点之后,创建存储库。</li>
+ <li>到新的仓库。
+ <pre class="brush: bash">cd express-locallibrary-tutorial</pre>
+ </li>
+</ol>
+
+<p>最后一步,是复制你的应用程序,然后使用 git ,将文件添加到你的仓库:</p>
+
+<ol>
+ <li>将Express应用程序,复制到此文件夹中(不包括<strong>/node_modules</strong>,其中包含您应根据需要,从NPM获取的依赖项文件)。</li>
+ <li>打开命令提示符/终端,并使用<code>add</code>命令,将所有文件添加到 git。</li>
+ <li>
+ <pre class="brush: bash">git add -A
+</pre>
+ </li>
+ <li>使用 status 命令,检查要添加的所有文件是否正确(您希望包含源文件,而不是二进制文件,临时文件等)。它应该看起来有点像下面的列表。
+ <pre>&gt; git status
+On branch master
+Your branch is up-to-date with 'origin/master'.
+Changes to be committed:
+  (use "git reset HEAD &lt;file&gt;..." to unstage)
+
+        new file:   ...</pre>
+ </li>
+ <li>如果您满意,请将文件提交到本地存储库:
+ <pre class="brush: bash">git commit -m "First version of application moved into github"</pre>
+ </li>
+ <li>然后使用以下内容,将本地存储库同步到Github网站:
+ <pre>git push origin master</pre>
+ </li>
+</ol>
+
+<p>完成此操作后,您应该可以返回创建存储库的Github上的页面,刷新页面,并查看您的整个应用程序现已上传。使用此添加/提交/推送循环,您可以在文件更改时,继续更新存储库。</p>
+
+<div class="note">
+<p><strong>提示:</strong> 这是备份你的“vanilla”项目的好时机 - 虽然我们将在以下部分中进行的一些更改,可能对任何平台(或开发)上的部署有用,而一些其他的更改可能没有用。</p>
+
+<p>执行此操作的最佳方法,是使用git来管理您的修订。使用git,您不仅可以回到特定的旧版本,而且可以在生产变更的单独“分支”中进行维护,并选择在生产和开发分支之间移动的任何更改。<a href="https://help.github.com/articles/good-resources-for-learning-git-and-github/">学习Git</a>非常值得,但超出了本主题的范围。</p>
+
+<p>最简单的方法,是将文件复制到另一个位置。使用最符合您对 git 了解的方法!</p>
+</div>
+
+<h3 id="更新Heroku的应用程序">更新Heroku的应用程序</h3>
+
+<p>本节介绍了您需要对 LocalLibrary 应用程序进行的更改,以使其在Heroku上运行。</p>
+
+<h4 id="设置_node_版本">设置 node 版本</h4>
+
+<p><strong>package.json</strong>包含解决应用程序依赖项所需的所有内容,以及启动站点时,应启动的文件。 Heroku检测到此文件的存在,并将使用它来配置您的应用程序环境。</p>
+
+<p>我们当前的<strong>package.json</strong>中,缺少的唯一有用信息,是 node 的版本。我们可以通过输入命令,找到我们用于开发的 node 版本:</p>
+
+<pre class="brush: bash">&gt;node --version
+v8.9.1</pre>
+
+<p>打开<strong>package.json</strong>,并将此信息添加为<strong>engines &gt; node</strong> 部分,如图所示(使用系统的版本号)。</p>
+
+<pre class="brush: json">{
+ "name": "express-locallibrary-tutorial",
+ "version": "0.0.0",
+<strong> "engines": {
+ "node": "8.9.1"
+ },</strong>
+ "private": true,
+  ...
+</pre>
+
+<h4 id="数据库配置">数据库配置</h4>
+
+<p>到目前为止,在本教程中,我们使用了一个硬编码到<strong>app.js</strong>的单个数据库。通常我们希望,能够为生产和开发创建不同的数据库,接下来我们将修改 LocalLibrary 网站,以从OS环境获取数据库URI(如果已定义),否则使用我们的开发数据库。</p>
+
+<p>打开<strong>app.js</strong>,并找到设置mongoDB连接变量的行。它看起来像这样:</p>
+
+<pre class="brush: js">var mongoDB = 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';</pre>
+
+<p>使用以下代码替换该行,该代码使用<code>process.env.MONGODB_URI</code>从名为<code>MONGODB_URI</code>的环境变量中,获取连接字符串(如果已设置)(使用您自己的数据库URL,而不是下面的占位符。)</p>
+
+<pre class="brush: js">var mongoDB = <strong>process.env.MONGODB_URI</strong> || 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';
+</pre>
+
+<h4 id="安装依赖并重新测试">安装依赖并重新测试</h4>
+
+<p>在我们继续之前,让我们再次测试该网站,并确保它不受我们的任何更改的影响。</p>
+
+<p>首先,我们需要获取我们的依赖项(你会记得,我们没有将 <strong>node_modules</strong>文件夹,复制到我们的 git 树中)。您可以通过在项目根目录的终端中,运行以下命令来执行此操作:</p>
+
+<pre class="brush: bash">npm install
+</pre>
+
+<p>现在运行该站点(请参阅<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/routes#Testing_the_routes">测试路由</a>的相关命令),并检查该站点,是否仍按预期运行。</p>
+
+<h4 id="将更改保存到_Github">将更改保存到 Github</h4>
+
+<p>接下来,让我们将所有更改保存到 Github。在终端中(在我们的存储库中),输入以下命令:</p>
+
+<pre class="brush: bash">git add -A
+git commit -m "Added files and changes required for deployment to heroku"
+git push origin master</pre>
+
+<p>我们现在应该准备开始在 Heroku 上,部署 LocalLibrary。</p>
+
+<h3 id="获取一个_Heroku_账户">获取一个 Heroku 账户</h3>
+
+<p>要开始使用Heroku,您首先需要创建一个帐户(如果您已经拥有一个帐户,并安装了Heroku客户端,请跳过创建并上传网站):</p>
+
+<ul>
+ <li>访问 <a href="https://www.heroku.com/">www.heroku.com</a> ,并单击免费注册按钮 <strong>SIGN UP FOR FREE</strong> 。</li>
+ <li>输入您的详细信息,然后按<strong>CREATE FREE ACCOUNT</strong>。系统会要求您,检查帐户中是否有注册电子邮件。</li>
+ <li>单击注册电子邮件中的帐户激活链接。您将在网络浏览器上收回您的帐户。</li>
+ <li>输入您的密码,然后单击 <strong>SET PASSWORD AND LOGIN</strong>.</li>
+ <li>然后,您将登录并进入Heroku仪表板:<a href="https://dashboard.heroku.com/apps">https://dashboard.heroku.com/apps</a>.</li>
+</ul>
+
+<h3 id="安装客户端">安装客户端</h3>
+
+<p>按照 <a href="https://devcenter.heroku.com/articles/getting-started-with-python#set-up">Heroku上的说明</a>,下载并安装Heroku客户端。</p>
+
+<p>安装客户端后,您将能够运行命令。例如,要获得客户端的帮助说明:</p>
+
+<pre class="brush: bash">heroku help
+</pre>
+
+<h3 id="创建并上传网站">创建并上传网站</h3>
+
+<p>要创建应用程序,我们在存储库的根目录中,运行“create”命令。这将在我们的本地git环境中,创建一个名为 heroku 的 git remote(“指向远程存储库的指针”)。</p>
+
+<pre class="brush: bash">heroku create</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 如果您愿意,可以在“创建”create 之后指定远程存储库的命名。如果你不这样做,你会得到一个随机的名字。该名称用于默认URL。</p>
+</div>
+
+<p>然后,我们可以将我们的应用程序,推送到Heroku存储库,如下所示。这将上传应用程序,获取所有依赖项,将其打包到dyno中,然后启动该站点。</p>
+
+<pre class="brush: bash">git push heroku master</pre>
+
+<p>如果我们很幸运,该应用程序现在正在网站上“运行”。要打开浏览器并运行新网站,请使用以下命令:</p>
+
+<pre class="brush: bash">heroku open</pre>
+
+<div class="note">
+<p><strong>注意</strong>: 该站点将使用我们的开发数据库运行。创建一些书本和其他对象,并检查该网站是否按预期运行。在下一节中,我们将其设置为使用我们的新数据库。</p>
+</div>
+
+<h3 id="设定配置变量">设定配置变量</h3>
+
+<p>您将从前一节回忆起,我们需要将NODE_ENV设置为'production',以便提高性能,并生成更简洁的错误消息。我们通过输入以下命令,来完成此操作:</p>
+
+<pre class="brush: bash">&gt;heroku config:set NODE_ENV='production'
+Setting NODE_ENV and restarting limitless-tor-18923... done, v13
+NODE_ENV: production
+</pre>
+
+<p>我们还应该使用单独的数据库进行生产,在<strong>MONGODB_URI</strong>环境变量中,设置其URI。您可以完全按照<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/mongoose#Setting_up_the_MongoDB_database">我们原来的方式</a>,设置新数据库和数据库用户,并获取其URI。您可以如下图所示设置URI(显然,要使用您自己的URI!)</p>
+
+<pre class="brush: bash">&gt;heroku config:set <strong>MONGODB_URI</strong>='mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production'
+Setting MONGODB_URI and restarting limitless-tor-18923... done, v13
+MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
+</pre>
+
+<p>您可以使用<code>heroku config</code>命令,随时检查配置变量 - 立即尝试:</p>
+
+<pre class="brush: bash">&gt;heroku config
+=== limitless-tor-18923 Config Vars
+MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
+NODE_ENV:    production
+</pre>
+
+<p>Heroku会在更新变量时,重新启动应用程序。如果您现在检查主页,它应该显示对象计数的零值,因为上面的更改,意味着我们现在正在使用新的(空)数据库。</p>
+
+<h3 id="管理附加组件">管理附加组件</h3>
+
+<p>Heroku 使用独立的附加组件,为应用程序提供支持服务 - 例如电子邮件或数据库服务。我们不在本网站中使用任何插件,但它们是使用Heroku的重要部分,因此您可能需要查看主题<a href="https://devcenter.heroku.com/articles/managing-add-ons">管理插件</a>(Heroku docs)。</p>
+
+<h3 id="调试">调试</h3>
+
+<p>Heroku客户端提供了一些调试工具:</p>
+
+<pre class="brush: bash">heroku logs # Show current logs
+heroku logs --tail # Show current logs and keep updating with any new results
+heroku ps #Display dyno status
+</pre>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>本教程介绍在生产环境中,如何配置Express 应用。是Express系列教程的最后一个。我们希望你觉得这些教程有用。你可以在<a href="https://github.com/mdn/express-locallibrary-tutorial">Github上取得完整的源码</a>。</p>
+
+<h2 id="相关链接">相关链接</h2>
+
+<ul>
+ <li id="production-best-practices-performance-and-reliability"><a href="https://expressjs.com/en/advanced/best-practice-performance.html">Production best practices: performance and reliability</a> (Express docs)</li>
+ <li><a href="https://expressjs.com/en/advanced/best-practice-security.html">Production Best Practices: Security</a> (Express docs)</li>
+ <li>Heroku
+ <ul>
+ <li><a href="https://devcenter.heroku.com/articles/getting-started-with-nodejs">Getting Started on Heroku with Node.js</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/deploying-nodejs">Deploying Node.js Applications on Heroku</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/nodejs-support">Heroku Node.js Support</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/node-concurrency">Optimizing Node.js Application Concurrency</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/how-heroku-works">How Heroku works</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/dynos">Dynos and the Dyno Manager</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/config-vars">Configuration and Config Vars</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/limits">Limits</a> (Heroku docs)</li>
+ </ul>
+ </li>
+ <li>Digital Ocean
+ <ul>
+ <li><a href="https://www.digitalocean.com/community/tutorials?q=express">Express</a> tutorials</li>
+ <li><a href="https://www.digitalocean.com/community/tutorials?q=node.js">Node.js</a> tutorials </li>
+ </ul>
+ </li>
+</ul>
+
+<p>{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</p>
+
+<p> </p>
+
+<h2 id="本教程链接">本教程链接</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node 介绍</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">架设 Node (Express) 开发环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express 教程: 本地图书馆网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express 教程 2: 创建骨架网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express 教程 3: 使用数据库 (Mongoose)</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express 教程 4: 路由与控制器</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express 教程 6: 使用表单</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express 教程 7: 部署到生产环境</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/development_environment/index.html b/files/zh-cn/learn/server-side/express_nodejs/development_environment/index.html
new file mode 100644
index 0000000000..9a5e9ac7de
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/development_environment/index.html
@@ -0,0 +1,390 @@
+---
+title: 设置 Node 开发环境
+slug: learn/Server-side/Express_Nodejs/development_environment
+tags:
+ - Express
+ - Node
+ - node.js
+ - npm
+ - 初学者
+ - 学习
+ - 开发环境
+ - 服务器端
+translation_of: Learn/Server-side/Express_Nodejs/development_environment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}</div>
+
+<p class="summary">你已经了解了 Express 的用途,接下来将在 Windows、Linux(Ubuntu)和 Mac OS X 下搭建 Node/Express 开发环境。本节将介绍主流操作系统下开发 Express 程序的必备知识。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>会打开终端 / 命令行。会为开发用操作系统安装软件包。</td>
+ </tr>
+ <tr>
+ <th scope="row">学习目标:</th>
+ <td>在电脑上搭建 Express 开发环境。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Express_开发环境概述">Express 开发环境概述</h2>
+
+<p>使用 Node 和 Express 搭建 web 应用程序开发环境非常简便。这一章节将简述所需的工具,在主流操作系统(Ubuntu、macOS 和 Windows)上安装 Node 的步骤,以及测试安装是否成功的方法。</p>
+
+<h3 id="什么是_Express_开发环境?">什么是 Express 开发环境?</h3>
+
+<p>完整的 Express 本地开发环境包括 Nodejs、NPM 包管理器和 <strong>Express 应用生成器</strong>(可选)。</p>
+
+<p>Node 和 NPM 包管理器可以用二进制包、安装程序或系统包管理器一并安装(下文将介绍)。然后在开发每个 Express web 应用时,由 NPM 针对当前应用将 Express(以及模板引擎、数据库驱动程序、身份验证中间件、静态文件托管中间件等其它库)作为依赖项进行安装。</p>
+
+<p>NPM 也可以安装(全局的)<strong>Express 应用生成器</strong>,可用于创建遵循 <a href="/zh-CN/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">MVC模式</a> 的 Express 应用框架。它不是必备的,因为无需这个工具就可以创建 Express 应用(或相同架构布局或依赖的 Express 应用)。但我们还是会使用它,因为它更容易上手,还有助于应用结构的模块化管理。</p>
+
+<div class="note">
+<p><strong>注:</strong> 与某些其他Web框架不同,开发环境不包含单独的开发Web服务器。在Node / Express中,Web应用程序将创建并运行自己的Web服务器!</p>
+</div>
+
+<p>典型的开发环境中还需要一些外围工具,包括用于编写代码的 <a href="zh-CN/docs/Learn/Common_questions/实用文本编辑器">文本编辑器</a> 或 IDE ,用于代码控制管理的工具(比如代码版本控制工具 <a href="https://git-scm.com/">Git</a>)。这里假定你已经安装了这些工具(尤其是文本编辑器)。</p>
+
+<h3 id="支持哪些操作系统?">支持哪些操作系统?</h3>
+
+<p>Node 可以在 Windows、macOS、Linux 的诸多发行版本或 Docker 等环境运行(完整列表见 Node <a href="https://nodejs.org/zh-cn/download/">下载页面</a>)。几乎所有的个人电脑都具备 Node 开发所需性能。Express 运行在 Node 环境中,因此可运行 Node 的平台均可运行 Express。</p>
+
+<p>本文将介绍 Windows、macOS 和 Ubuntu Linux 上的安装步骤。</p>
+
+<h3 id="应该选择_NodeExpress_的哪个版本?">应该选择 Node/Express 的哪个版本?</h3>
+
+<p>Node 有许多 <a href="https://nodejs.org/zh-cn/blog/release/">发行版本</a>,新版包含 bug 修复、对最新版本 ECMAScript 标准的支持,以及 API 的改进。</p>
+
+<p>通常应该选择最新的 LTS(Long-term supported,长期支持版)发行版,因为它比当前发布版(current)更稳定。当前发布版包含最新的特性(维护中),如果需要 LTS 版本中没有提供的特征,那么可以选择它。</p>
+
+<p>Express 应选用最新版本。</p>
+
+<h3 id="数据库和其它依赖该如何选择?">数据库和其它依赖该如何选择?</h3>
+
+<p>其它依赖(例如数据库驱动程序、模板引擎、身份认证引擎等)是应用的一部分,使用 NPM 将它们引入到应用环境中。稍后进行讨论。</p>
+
+<h2 id="安装_Node">安装 Node</h2>
+
+<p>先在操作系统上安装 Node.js 和 NPM 后才可使用 Express。接下来将介绍如何最简便地在 Ubuntu 18.04、macOS Mojave 以及 Windows 10 上安装 Node.js 最新的 LTS 版本。.</p>
+
+<div class="note">
+<p><strong>提示:</strong>以下内容将介绍在上述三种 OS 上安装 Node 和 NPM 的最简便方法。对于其它操作系统,以及更多的安装方法,可以参考 <a href="https://nodejs.org/zh-cn/download/package-manager/">通过包管理器方式安装 Node.js</a> (nodejs.org).</p>
+</div>
+
+<h3 id="Windows_和_macOS">Windows 和 macOS</h3>
+
+<p>在 Windows 和 macOS 上安装 Node 和 NPM 非常简单明了,使用现成的安装包就行了:</p>
+
+<ol>
+ <li>下载安装包:
+ <ol>
+ <li>访问 <a href="https://nodejs.org/zh-cn/">https://nodejs.org/zh-cn/</a></li>
+ <li>左侧按钮上写着“推荐多数用户使用(LTS)”,点击下载。</li>
+ </ol>
+ </li>
+ <li>双击下载的安装包,按照提示即可安装。</li>
+</ol>
+
+<h3 id="Ubuntu_18.04">Ubuntu 18.04</h3>
+
+<p>安装 Node 最新的 LTS 版本的最简便方法就是使用 <a href="https://nodejs.org/zh-cn/download/package-manager/#debian-and-ubuntu-based-linux-distributions-enterprise-linux-fedora-and-snap-packages">包管理器</a>,可以直接从 Ubuntu 二进制发行仓库中下载。通过在终端运行以下两行简单的命令就可以做到:</p>
+
+<pre class="brush: bash"><code>curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
+sudo apt-get install -y nodejs</code>
+</pre>
+
+<div class="warning">
+<p><strong>警告:</strong>直接从 Ubuntu 默认仓库中下载的 Node 是 8.x 版本的。</p>
+</div>
+
+<ol>
+</ol>
+
+<h3 id="测试_Node.js_和_NPM_是否安装成功">测试 Node.js 和 NPM 是否安装成功</h3>
+
+<p>检查 Node 是否成功安装的最简单方法就是在终端(或命令行)中运行 "<code>version</code>" 命令,看是否返回版本号字符串:</p>
+
+<pre class="brush: bash">$ node -v
+v10.15.0</pre>
+
+<p>NPM 应该与 Node.js 一同成功安装,可以使用同样的方法来测试一下:</p>
+
+<pre class="brush: bash">$ npm -v
+6.7.0</pre>
+
+<p>下面的测试也许会带来小小激动:创建一个非常基础的“纯 Node”服务器,在浏览器中访问正确的 URL 地址时将直接打印 "Hello world":</p>
+
+<ol>
+ <li>以下代码使用了纯 Node 的特性(与 Express 无关)和一些 ES6 的语法,把它复制到 <strong>hellonode.js</strong> 文件中:
+
+ <pre class="brush: js">// 加载 HTTP 模块
+const http = require("http");
+const hostname = '127.0.0.1';
+const port = 3000;
+
+// 创建 HTTP 服务器
+const server = http.createServer((req, res) =&gt; {
+
+ // 用 HTTP 状态码和内容类型(Content-Type)设置 HTTP 响应头
+ res.statusCode = 200;
+ res.setHeader('Content-Type', 'text/plain');
+
+ // 发送响应体
+ res.end('Hello World\n');
+});
+
+// 监听 3000 端口的请求,注册一个回调函数记录监听开始
+server.listen(port, hostname, () =&gt; {
+ console.log(`服务器运行于 http://${hostname}:${port}/`);
+});
+</pre>
+
+ <p>代码导入了 <code>"http"</code> 模块,并用它(<code>createServer()</code>)创建了一个服务器来监听 3000 端口的 HTTP 请求。随后在控制台打印一条信息,提示测试服务器的正确 URL。<code>createServer()</code> 函数接受一个回调函数作为参数,并在接收 HTTP 请求后进行回调。直接返回了 HTTP 状态码 200 ("<code>OK</code>"),以及纯文本信息 "Hello World"。</p>
+
+ <div class="note">
+ <p><strong>注:</strong>现在看不懂这些代码请不要担心,开始使用 Express 后候会进行更加详细的解释。</p>
+ </div>
+ </li>
+ <li>在命令行工具中进入 hellonode.js 文件所在的目录,输入“node + 文件名”并运行,服务器就启动了:
+ <pre class="brush: bash">$ node hellonode.js
+服务器运行于 http://127.0.0.1:3000/
+</pre>
+ </li>
+ <li>在浏览器中访问这个 URL(<a href="http://127.0.0.1:8000/">http://127.0.0.1:3000/</a>),如果一切正常,浏览器会直接显示出 "Hello world" 字符串。</li>
+</ol>
+
+<h2 id="使用_NPM">使用 NPM</h2>
+
+<p>构建 Node 应用过程中,<a href="https://docs.npmjs.com/">NPM</a> 是除了 Node 本身之外最重要的工具。可用于获取应用开发、测试以及生产所需的所有包(JavaScript 库)。也可运行开发过程中使用的测试单元和工具。</p>
+
+<div class="note">
+<p><strong>注:</strong>以 Node 的角度来看,Express 只是一个用 NPM 安装、供人使用的包而已。</p>
+</div>
+
+<p>可以用 NPM 手动逐个安装所需包。但通常可用 <a href="https://docs.npmjs.com/files/package.json">package.json</a> 文件来管理依赖。把每个<font><font>依赖以一个</font></font> JavaScript “包”的形式(其中<font><font>包括名称、版本、描述,初始执行文件、生产依赖,开发依赖、支持的 </font></font><em><font><font>Node </font></font></em><font><font>版本,等等</font></font>)罗<font><font>列在这个文件中。package.json 文件包含 NPM 获取和运行应用程序所需的所有内容(在编写可重用的库时,可以用它把包上传到 NPM 仓库中供其他用户使用)。</font></font></p>
+
+<h3 id="添加依赖项">添加依赖项</h3>
+
+<p>下面介绍用 NPM 下载包、将包保存进工程依赖树,以及在 Node 应用中调用包的方法和步骤。</p>
+
+<div class="note">
+<p><strong>注:</strong>现在来讲解获取和安装 Express 包的步骤。稍后解释为什么可以直接对 Express 包(乃至其它包)使用 <strong>Express 应用生成器</strong>。这段对理解 NPM 的工作原理和应用生成器的工作机制有一定的帮助。</p>
+</div>
+
+<ol>
+ <li>首先为新应用创建一个目录,并进入它:
+ <pre class="brush: bash">$ mkdir myapp
+$ cd myapp</pre>
+ </li>
+ <li>然后,使用 NPM 的 init 命令为应用创建一个 <strong>package.json</strong> 文件。这个命令将请求一系列的信息,包括应用的名称和版本,程序初始进入点的文件名(默认为 <strong>index.js</strong>)。现在先接受默认信息即可:
+ <pre class="brush: bash">$ npm init</pre>
+
+ <p><strong>package.json</strong> 文件中保存了所接受的默认信息,最后一条是许可证信息:</p>
+
+ <pre class="brush: json">{
+ "name": "myapp",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
+ },
+ "author": "",
+ "license": "ISC"
+}
+</pre>
+ </li>
+ <li>接下来在 <strong>myapp</strong> 目录中安装 Express,用下面的命令将 Express 保存在 <strong>package.json</strong> 文件中的依赖表里:
+ <pre class="brush: bash">$ npm install express</pre>
+
+ <p>此时 <strong>package.json</strong> 文件的底部会出现依赖列表("dependencies"),其中包含 Express:</p>
+
+ <pre class="brush: json">{
+ "name": "myapp",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+<strong> "dependencies": {
+ "express": "^4.16.4"
+ }</strong>
+}
+</pre>
+ </li>
+ <li>可以调用 <code>require()</code> 函数来使用库:
+ <pre><code><strong>const express = require('express');
+</strong>const app = express();
+
+app.get('/', (req, res) =&gt; {
+ res.send('Hello World!')
+});
+
+app.listen(8000, () =&gt; {
+ console.log('示例程序正在监听 8000 端口!')
+});</code>
+</pre>
+
+ <p>以上代码展示了一个最简单的 "HelloWorld" Express 应用。它导入了 "express" 模块并用它创建了一个服务器(app)来监听 8000 端口,并且在控制台打印了一条信息以提示测试服务器的正确 URL。<code>app.get()</code> 函数只响应对特定路径(<code>'/'</code>)的 HTTP <code>GET</code> 请求,此处的响应就是发送 "Hello World!"。<br>
+ <br>
+ 在 myapp 应用的根目录下新建一个 <strong>index.js</strong> 文件,将上述代码粘贴进来并保存。</p>
+ </li>
+ <li>在命令行输入 node + 文件名 即可启动服务器:
+ <pre class="brush: bash">$ node index.js
+<code>示例程序正在监听 8000 端口!</code>
+</pre>
+ </li>
+ <li>在浏览器中访问这个 URL(<a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>),如果一切正常,浏览器会直接显示出 "Hello world!" 字符串。</li>
+</ol>
+
+<h3 id="开发依赖(Development_dependencies)">开发依赖(Development dependencies)</h3>
+
+<p>如果一个依赖只在开发过程中用到,应该将其保存为“开发依赖”(这样,包的用户便无需在生产环境中安装它们)。比如,如果要使用 <a href="http://eslint.org/">eslint</a>(一款流行的 JavaScript lint 工具)可以这样调用 NPM:</p>
+
+<pre class="brush: bash"><code>$ npm install eslint --save-dev</code></pre>
+
+<p>当前应用的 <strong>package.json </strong>文件中将自动添加以下项目:</p>
+
+<pre class="brush: js"> "devDependencies": {
+ "eslint": "^5.12.0"
+ }
+</pre>
+
+<div class="note">
+<p><strong>注:</strong>"<a href="https://zh.wikipedia.org/wiki/Lint">lint</a>" 是用于对软件进行静态分析的工具,可以发现并报告软件是否遵循某些最佳编程惯例。</p>
+</div>
+
+<h3 id="运行任务">运行任务</h3>
+
+<p>在 <strong>package.json</strong> 中,除了定义和获取依赖,还可以定义脚本,然后通过 NPM 的 <a href="https://docs.npmjs.com/cli/run-script">run-script</a> 命令来运行。这个用法普遍用于自动运行测试单元或部分应用,也可用于构建工具链(比如运行工具来压缩 JavaScript 文件或图片,lint 或分析代码,等等)。</p>
+
+<div class="note">
+<p><strong>注:</strong><a href="http://gulpjs.com/">Gulp</a> 和 <a href="http://gruntjs.com/">Grunt</a> 等任务运行器可用于运行测试单元或其它外部工具。</p>
+</div>
+
+<p>比如,可以在 <strong>package.json</strong> 文件中添加以下内容来定义一个脚本,从而对上文的代码运行 eslint(假设应用代码在 /src/js 文件夹下):</p>
+
+<pre class="brush: js">"scripts": {
+ ...
+ "lint": "eslint src/js"
+ ...
+}
+</pre>
+
+<p>深入解释一下,eslint src/js 命令可以在终端/命令行对应用目录下的 src/js 目录中的 JavaScript 文件运行 eslint。把上面一段脚本添加进应用的 package.json 中还可以为此命令提供一个快捷方式—— lint。</p>
+
+<p>然后就可以用 NPM 这样运行 eslint 了:</p>
+
+<pre class="brush: bash"><code>$ npm run-script lint</code></pre>
+
+<p>或使用别名:</p>
+
+<pre class="brush: bash"><code>$ npm run lint</code></pre>
+
+<p>这个示例看上去并没有让原始命令简洁多少,但在 NPM 脚本中可以加入更长的命令,甚至是多命令链。比如可以让单一的 NPM 脚本来一次运行所有的测试单元。</p>
+
+<h2 id="安装_Express_应用生成器">安装 Express 应用生成器</h2>
+
+<p><a href="https://expressjs.com/en/starter/generator.html">Express 应用生成器</a> 工具可以生成一个 Express 应用的“框架”。可以用 NPM 这样安装它(-g 参数可以把该工具全局安装,那样就可以在任意应用中使用了):</p>
+
+<pre class="brush: bash"><code>$ npm install express-generator -g</code></pre>
+
+<p>进入应用目录,运行以下命令,即可创建一个名为 "helloworld" 的 Express 应用:</p>
+
+<pre class="brush: bash">$ express helloworld</pre>
+
+<div class="note">
+<p><strong>注:</strong>也可以指定模板库来使用其它丰富的设置。可通过 help 命令来查看所有选项:</p>
+
+<pre class="brush: bash">$ express --help
+</pre>
+</div>
+
+<p>NPM 将在当前位置的子目录中创建新的 Express 应用,可以在控制台看到构建的过程。在完成时,NPM 会提示你需要安装哪些 Node 依赖,以及如何开启应用。</p>
+
+<div class="note">
+<p>新应用的根目录有一个 <strong>package.json</strong> 文件。可以打开它看看都安装了哪些依赖,其中可以看到 Express 和 Jade 模板库:</p>
+
+<pre class="brush: js">{
+ "name": "helloworld",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "start": "node ./bin/www"
+ },
+ "dependencies": {
+ "cookie-parser": "~1.4.3",
+ "debug": "~2.6.9",
+ "express": "~4.16.0",
+ "http-errors": "~1.6.2",
+ "jade": "~1.11.0",
+ "morgan": "~1.9.0"
+ }
+}</pre>
+</div>
+
+<p>用下列命令可为 helloworld 应用安装所有依赖:</p>
+
+<pre class="brush: bash">$ cd helloworld
+$ npm install
+</pre>
+
+<p>然后运行这个应用(Windows 环境):</p>
+
+<pre class="brush: bash">&gt; SET DEBUG=helloworld:* &amp; npm start
+</pre>
+
+<p>(Linux/macOS 环境):</p>
+
+<pre class="brush: bash">$ DEBUG=helloworld:* npm start</pre>
+
+<p>DEBUG 命令可以展示应用运行时返回的有用的日志信息,如下所示:</p>
+
+<p><img alt="设置 DEBUG 命令显示的日志信息" src="https://mdn.mozillademos.org/files/16404/debug.png"></p>
+
+<p>打开浏览器并访问 <a href="http://127.0.0.1:3000/">http://127.0.0.1:3000/</a> 将看到 Express 的默认欢迎页面。</p>
+
+<p><img alt="生成应用的默认主页" src="https://mdn.mozillademos.org/files/16405/express.png"></p>
+
+<p>稍后在创建应用框架一节中将讨论生成应用的具体细节。</p>
+
+<ul>
+</ul>
+
+<h2 id="小结">小结</h2>
+
+<p>现在 Node 开发环境已经配置好了,可以用于创建 Express 应用了。你还了解了用 NPM 导入 Express 的步骤,以及如何创建(使用 Express 应用生成器)和运行 web 应用。</p>
+
+<p>下一节将开始用上述的环境和工具通过实战逐步搭建一个完整的 web 应用。</p>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<ul>
+ <li><a href="https://nodejs.org/zh-cn/download/">Node.js 下载页面</a> (nodejs.org 官方中文页面)</li>
+ <li><a href="https://nodejs.org/zh-cn/download/package-manager/">通过包管理器方式安装 Node.js</a> (nodejs.org 官方中文页面)</li>
+ <li><a href="http://www.expressjs.com.cn/starter/installing.html">安装 Express</a> (expressjs.com.cn 中文镜像页面)</li>
+ <li><a href="http://www.expressjs.com.cn/starter/generator.html">Express 应用程序生成器</a> (expressjs.com.cn 中文镜像页面)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node 入门</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/development_environment">设置 Node(Express)开发环境</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express 教程:本地图书馆网站</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express 教程 2:创建站点框架</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/mongoose">Express 教程 3:使用数据库(Mongoose)</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/routes">Express 教程 4:路由和控制器</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5:显示图书馆数据</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/forms">Express 教程 6:使用表单</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/deployment">Express 教程 7:部署至生产环境</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html
new file mode 100644
index 0000000000..c62c5fbbec
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html
@@ -0,0 +1,89 @@
+---
+title: 作者细节页面
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page
+---
+<p>作者细节页面需要呈现指定作者<code>Author</code>的信息,使用 <code>_id</code> 字段的值(自动产生)识别,接着是这个作者的所有书本物件<code>Book</code>的列表。</p>
+
+<h2 id="控制器">控制器</h2>
+
+<p>打开 <strong>/controllers/authorController.js</strong>。</p>
+
+<p>在档案最上方,加入底下几行,引入 async 和 Book 模组(作者细节页面需要它们)。</p>
+
+<pre class="brush: js">var async = require('async');
+var Book = require('../models/book');</pre>
+
+<p>找到 exported <code>author_detail()</code> 控制器方法,并用底下代码置换。</p>
+
+<pre class="brush: js">// Display detail page for a specific Author.
+exports.author_detail = function(req, res, next) {
+
+<strong>    async.parallel({
+        author: function(callback) {
+            Author.findById(req.params.id)
+              .exec(callback)
+        },
+        authors_books: function(callback) {
+          Book.find({ 'author': req.params.id },'title summary')
+          .exec(callback)
+        },
+    }, function(err, results) {
+        if (err) { return next(err); } // Error in API usage.
+        if (results.author==null) { // No results.
+            var err = new Error('Author not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render.
+        res.render('author_detail', { title: 'Author Detail', author: results.author, author_books: results.authors_books } );
+    });</strong>
+
+};
+</pre>
+
+<p>此处的控制器方法使用 <code>async.parallel()</code>,用平行的方式,查询作者 <code>Author</code>和相应的书本实例,并附加上绘制本页面的回调,如果 2 个要求都成功完成,就运行回调。这个方式,就跟前面的种类细节页面所说明的完全相同。</p>
+
+<h2 id="视图">视图</h2>
+
+<p>创建 <strong>/views/author_detail.pug</strong> ,並複制貼上底下的文字。</p>
+
+<pre class="brush: js">extends layout
+
+block content
+
+<strong>  h1 Author: #{author.name}</strong>
+  p #{author.date_of_birth} - #{author.date_of_death}
+
+  div(style='margin-left:20px;margin-top:20px')
+
+    h4 Books
+
+    dl
+    each book in author_books
+      dt
+        a(href=book.url) #{book.title}
+      dd #{book.summary}
+
+    else
+      p This author has no books.
+</pre>
+
+<p>本模板里的所有事物,都在先前的章节演示过了。</p>
+
+<h2 id="它看起來像是">它看起來像是?</h2>
+
+<p>运行本应用,并打开浏览器访问 <a href="http://localhost:3000/">http://localhost:3000/</a>。选择All Authors 连结,然后选择一个作者。如果每个东西都设定正确了,你的网站看起来应该会像底下的截图。</p>
+
+<p><img alt="Author Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14466/LocalLibary_Express_Author_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 422px; margin: 0px auto; width: 1000px;"></p>
+
+<div class="note">
+<p><strong>注意:</strong> 作者的出生与死亡日期的外观很丑!我们将在本文最后的自我挑战处理它。</p>
+</div>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 的下一个部分 : <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge"> 书本实例细节页面和自我挑战 </a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html
new file mode 100644
index 0000000000..055ae91e1c
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html
@@ -0,0 +1,89 @@
+---
+title: 作者清单面页、分类清单页面、与自我挑战
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page
+---
+<p>作者列表页面,需要呈现数据库中所有作者的列表,有每位作者的名字,并连结到作者详细内容页面。出生与死亡日期应该在名字后面,并且在同一列。</p>
+
+<h2 class="highlight-spanned" id="控制器"><span class="highlight-span">控制器</span></h2>
+
+<p>作者列表控制器函数,需要获取所有作者实例的列表,然后将这些实例传递给模板进行渲染。</p>
+
+<p>打开<strong>/controllers/authorController.js</strong>。在文件顶部附近,找到导出的<code>author_list()</code> 控制器方法,并将其替换为以下代码(更改后的代码以粗体显示)。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all Authors.</span>
+exports<span class="punctuation token">.</span>author_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+
+ Author<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">sort</span><span class="punctuation token">(</span><span class="punctuation token">[</span><span class="punctuation token">[</span><span class="string token">'family_name'</span><span class="punctuation token">,</span> <span class="string token">'ascending'</span><span class="punctuation token">]</span><span class="punctuation token">]</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_authors<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span>
+ <span class="comment token">//Successful, so render</span>
+ res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'author_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Author List'</span><span class="punctuation token">,</span> author_list<span class="punctuation token">:</span> list_authors <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre>
+
+<p>该方法使用模型的 <code>find()</code>, <code>sort()</code> 和 <code>exec()</code> 函数,以返回所有<code>Author</code>对象,并按<code>family_name</code>的字母顺排列。传递给<code>exec()</code>方法的回调被调用,并将传入任何错误(或<code>null</code>)作为第一个参数,或者成功时,传入所有作者列表。如果出现错误,则调用带有错误值的下一个中间件函数,如果没有错误,则呈现<strong> author_list</strong>(.pug)模板,传递页面标题<code>title,</code>和作者列表(<code>author_list</code>)。</p>
+
+<h2 class="highlight-spanned" id="视图">视图</h2>
+
+<p>打开 <strong>/views/author_list.pug </strong>,用底下文字取代它的内容。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span>
+
+block content
+ h1<span class="operator token">=</span> title
+
+ ul
+ each author <span class="keyword token">in</span> author_list
+ li
+ <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>author<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>author<span class="punctuation token">.</span>name<span class="punctuation token">}</span>
+ <span class="operator token">|</span> <span class="punctuation token">(</span>#<span class="punctuation token">{</span>author<span class="punctuation token">.</span>date_of_birth<span class="punctuation token">}</span> <span class="operator token">-</span> #<span class="punctuation token">{</span>author<span class="punctuation token">.</span>date_of_death<span class="punctuation token">}</span><span class="punctuation token">)</span>
+
+ <span class="keyword token">else</span>
+ li There are no authors<span class="punctuation token">.</span></code></pre>
+
+<p>如同我们其它的模板,上面视图也依照着同样的模式。</p>
+
+<h2 class="highlight-spanned" id="它看起來像是"><span class="highlight-span">它看起來像是?</span></h2>
+
+<p>运行本应用,并打开浏览器访问 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a> 。然后选择所有作者 All authors 连结。如果每个东西都设定正确了,页面看起来应该像底下的截图。</p>
+
+<p><img alt="Author List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14468/LocalLibary_Express_Author_List.png" style="display: block; height: 453px; margin: 0px auto; width: 1200px;"></p>
+
+<div class="note">
+<p><strong>注意:</strong> 作者生命日期的外观是丑陋的!您可以使用我们用于<code>BookInstance</code> 列表的<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data#date_formatting">相同方法</a>(将生命周期的虚拟属性,添加到 <code>Author</code> 模型),来改进此方法。</p>
+
+<p>但是,这次缺少日期,除非严格模式生效,否则将忽略对不存在的属性的引用。<code>moment()</code>返回当前时间,并且您不希望将缺少的日期格式化为就像今天一样。</p>
+
+<p>解决此问题的一种方法,是定义返回格式化日期的函数内容,以便返回空字符串,除非日期实际存在。例如:</p>
+
+<p><code>return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';</code></p>
+</div>
+
+<h2 id="种类列表页面—自我挑战!Edit">种类列表页面—自我挑战!<a class="button section-edit only-icon" href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data$edit#Genre_list_page—challenge!" rel="nofollow, noindex"><span>Edit</span></a></h2>
+
+<p>在这个部分,你应该实作你自己的种类列表页面。该页面应显示数据库中所有种类的列表,每个种类都链接到其关联的详细信息页面。预期结果的屏幕截图如下所示。</p>
+
+<p><img alt="Genre List - Express Local Library site" src="https://mdn.mozillademos.org/files/14460/LocalLibary_Express_Genre_List.png" style="border-style: solid; border-width: 1px; display: block; height: 346px; margin: 0px auto; width: 600px;"></p>
+
+<p>种类列表控制器功能,需要获取所有种类实例的列表,然后将这些实例传递给模板进行渲染。</p>
+
+<ol>
+ <li>您需要在 <strong>/controllers/genreController.js</strong> 中编辑<code>genre_list()</code>。</li>
+ <li>实现方式几乎与<code>author_list()</code>控制器完全相同。
+ <ul>
+ <li>按名称以上升顺序,对结果进行排序。</li>
+ </ul>
+ </li>
+ <li>要呈现的模板,应命名为 <strong>genre_list.pug</strong>。</li>
+ <li>要呈现的模板应该传递变量<code>title</code>('Genre List')和种类列表<code>genre_list</code>(从<code>Genre.find()</code>回调返回)。</li>
+ <li>该视图应与上面的屏幕截图/要求相匹配(这应该与作者列表视图具有非常相似的结构/格式,除了种类没有日期)。</li>
+</ol>
+
+<h2 id="下一步">下一步</h2>
+
+<p>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></p>
+
+<p>继续教程 5 下一個部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page">种类细节页面</a></p>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html
new file mode 100644
index 0000000000..775bcd387f
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html
@@ -0,0 +1,112 @@
+---
+title: 书本详细信息页面
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page
+---
+<p>书本细节页面需要呈现一本指定书本(<code>Book</code>)的信息, 使用它的 <code>_id</code> 字段值(自动产生)做为识别,接着是图书馆中书本实例(<code>BookInstance</code>)的信息。无论我们在哪里呈现一个作者、种类、或书本实例,都应该连结到它的细节页面。</p>
+
+<h2 id="控制器">控制器</h2>
+
+<p>打开 <strong>/controllers/bookController.js</strong>. ,找到 exported <code>book_detail()</code> 控制器方法,用底下的代码置换。</p>
+
+<pre class="brush: js">// Display detail page for a specific book.
+exports.book_detail = function(req, res, next) {
+
+<strong>    async.parallel({
+        book: function(callback) {
+
+            Book.findById(req.params.id)
+              .populate('author')
+              .populate('genre')
+              .exec(callback);
+        },
+        book_instance: function(callback) {
+
+          BookInstance.find({ 'book': req.params.id })
+          .exec(callback);
+        },
+    }, function(err, results) {
+        if (err) { return next(err); }
+        if (results.book==null) { // No results.
+            var err = new Error('Book not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render.
+        res.render('book_detail', { title: 'Title', book:  results.book, book_instances: results.book_instance } );
+    });</strong>
+
+};
+
+</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 我们不需要用 require 导入 async 和 BookInstance,当我们实作主页面控制器的时候,我们就已经引入这些模组。</p>
+</div>
+
+<p>此处的控制器方法使用 <code>async.parallel()</code>,用平行的方式找到 <code>Book</code> 以及它的相应复本 (<code>BookInstances</code>) 。这样的处理方式,就跟上面的 种类细节页面 所说明的完全相同。</p>
+
+<h2 id="视图">视图</h2>
+
+<p>创建 <strong>/views/book_detail.pug</strong> 并加入底下文字。</p>
+
+<pre class="brush: js">extends layout
+
+block content
+  h1 #{title}: #{book.title}
+
+  p #[strong Author:]
+    a(href=book.author.url) #{book.author.name}
+  p #[strong Summary:] #{book.summary}
+  p #[strong ISBN:] #{book.isbn}
+  p #[strong Genre:]&amp;nbsp;
+    each val, index in book.genre
+      a(href=val.url) #{val.name}
+  if index &lt; book.genre.length - 1
+      |,
+
+  div(style='margin-left:20px;margin-top:20px')
+    h4 Copies
+
+    each val in book_instances
+      hr
+      if val.status=='Available'
+        <strong>p.text-success</strong> #{val.status}
+      else if val.status=='Maintenance'
+        p.text-danger #{val.status}
+      else
+        p.text-warning #{val.status}
+      p #[strong Imprint:] #{val.imprint}
+      if val.status!='Available'
+        p #[strong Due back:] #{val.due_back}
+      p #[strong Id:]&amp;nbsp;
+        a(href=val.url) #{val._id}
+
+    else
+      p There are no copies of this book in the library.
+</pre>
+
+<p>在这个模板里,几乎每个东西都在先前的章节演示过了。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 与该书相关的種類列表,在模板中的实作,如以下代碼。除了最后一本书之外,在与本书相关的每个种類之后,都会添加一个逗号。</p>
+
+<pre> p #[strong Genre:]
+  each val, index in book.genre
+  a(href=val.url) #{val.name}
+  if index &lt; book.genre.length - 1
+  |, </pre>
+</div>
+
+<h2 id="它看起來像是">它看起來像是?</h2>
+
+<p>运行本应用,并打开浏览器访问 <a href="http://localhost:3000/">http://localhost:3000/</a>。选择 All books 连结,然后选择其中一本书。如果每个东西都设定正确了,你的页面看起来应该像是底下的截图。</p>
+
+<p><img alt="Book Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14470/LocalLibary_Express_Book_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 616px; margin: 0px auto; width: 1200px;"></p>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 的下一个部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page">作者细节页面</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html
new file mode 100644
index 0000000000..bb76f61d50
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html
@@ -0,0 +1,70 @@
+---
+title: 书本列表页面
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page
+---
+<p>接下做我们将实作书本列表页面。这个页面需要呈现数据库中所有书本的列表,包含每本书的作者、标题,标题将成为一个超连结,连到书本详细内容页面。</p>
+
+<h2 class="highlight-spanned" id="控制器"><span class="highlight-span">控制器</span></h2>
+
+<p>书本列表控制器函数,需要获取数据库中所有<code>Book</code>对象的列表,然后将这些对象传给模板进行呈现。</p>
+
+<p>打开 <strong>/controllers/bookController.js</strong>. 找到导出的 <code>book_list()</code> 控制器方法,并替换為下面的代码。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all Books.</span>
+exports<span class="punctuation token">.</span>book_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+
+ Book<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">{</span><span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="string token">'title author'</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">populate</span><span class="punctuation token">(</span><span class="string token">'author'</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_books<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span>
+ <span class="comment token">//Successful, so render</span>
+ res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'book_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Book List'</span><span class="punctuation token">,</span> book_list<span class="punctuation token">:</span> list_books <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre>
+
+<p>该方法使用模型的<code>find()</code>函数返回所有<code>Book</code>对象,选择仅返回标题<code>title</code>和作者<code>author</code>,因为我们不需要其他字段(它也会返回<code>_id</code>和虚拟字段)。这里我们还调用<code>Book</code>上的<code>populate()</code> ,指定作者<code>author</code>字段 — 这将用完整的作者信息,替换存储的书本作者 id。</p>
+
+<p>成功时,传递给查询的回调,将呈现<strong> book_list</strong>(.pug) 模板,将标题<code>title</code>和<code>book_list</code>(包含作者的書本列表)作为变量传递。</p>
+
+<h2 class="highlight-spanned" id="视图">视图</h2>
+
+<p>创建 <strong>/views/book_list.pug </strong>并复制底下的文字。</p>
+
+<p> </p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span>
+
+block content
+ h1<span class="operator token">=</span> title
+
+ ul
+ each book <span class="keyword token">in</span> book_list
+ li
+ <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>book<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>book<span class="punctuation token">.</span>title<span class="punctuation token">}</span>
+ <span class="operator token">|</span> <span class="punctuation token">(</span>#<span class="punctuation token">{</span>book<span class="punctuation token">.</span>author<span class="punctuation token">.</span>name<span class="punctuation token">}</span><span class="punctuation token">)</span>
+
+ <span class="keyword token">else</span>
+ li There are no books<span class="punctuation token">.</span></code></pre>
+
+<p>這个视图扩展了 <strong>layout.pug</strong> 基本模板,并覆盖了名为 '<strong>content</strong>' 的區块 <code>block</code> 。它显示我们从控制器传入的标题<code>title</code>(通过<code>render()</code>方法),然后使用<code>each</code>-<code>in</code>-<code>else</code>语法,遍历<code>book_list</code>变量。为每本图书创建一个列表项,以显示书名,并作为书的详细信息页面的链接,后面跟着作者姓名。如果<code>book_list</code>中没有书,则执行<code>else</code>子句,并显示文字 “没有书” 'There are no books.'。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 我们使用 <code>book.url</code> ,为每本书提供详细记录链接(我们已经实现了此路由,但尚未实现此页面)。这是 <code>Book </code>模型的一个虚拟属性,它使用模型实例的 <code>_id </code>字段,生成唯一的URL路径。</p>
+</div>
+
+<p>在这里,我們感兴趣的是,每本书被定义为两行,第二行使用管道(上面高亮显示)。这种方法是必要的,因为如果作者姓名位于上一行,那么它将成为超链接的一部分。</p>
+
+<h2 class="highlight-spanned" id="它看起來像是"><span class="highlight-span">它看起來像是?</span></h2>
+
+<p>运行本应用 (参见 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes#Testing_the_routes">测试路由</a> 有相关的命令) ,并打开你的浏览器,访问 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>。然后选择 所有书本 连结。如果每样东西都设定正确了,你的网站看起来应该像底下的截图。</p>
+
+<p><img alt="Book List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14464/LocalLibary_Express_Book_List.png" style="border-style: solid; border-width: 1px; display: block; height: 387px; margin: 0px auto; width: 918px;"></p>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 下个部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page">书本实例列表页面</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html
new file mode 100644
index 0000000000..cba25ab30d
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html
@@ -0,0 +1,91 @@
+---
+title: 书本实例细节页面、与自我挑战
+slug: >-
+ learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge
+translation_of: >-
+ Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge
+---
+<h2 id="书本实例细节页面">书本实例细节页面</h2>
+
+<p><code>BookInstance</code>细节页面,需要呈现每一个<code>BookInstance</code>的信息,用 <code>_id</code> 字段值(自动产生)做识别。它包含了 <code>Book</code> 名称 (也是一个连结,连到 书本细节页面),接着是纪录中的其它的信息。</p>
+
+<h3 id="控制器">控制器</h3>
+
+<p>打开 <strong>/controllers/bookinstanceController.js</strong>. ,找到exported <code>bookinstance_detail()</code> 控制器方法,并替换以下代码。</p>
+
+<pre class="brush: js">// Display detail page for a specific BookInstance.
+exports.bookinstance_detail = function(req, res, next) {
+
+<strong>    BookInstance.findById(req.params.id)
+    .populate('book')
+    .exec(function (err, bookinstance) {
+      if (err) { return next(err); }
+      if (bookinstance==null) { // No results.
+          var err = new Error('Book copy not found');
+          err.status = 404;
+          return next(err);
+        }
+      // Successful, so render.
+      res.render('bookinstance_detail', { title: 'Book:', bookinstance:  bookinstance});
+    })</strong>
+
+};
+</pre>
+
+<p>该方法使用从URL(使用路由)中提取的特定书本实例的ID,调用<code>BookInstance.findById()</code>,并通过请求参数(<code style="font-style: normal; font-weight: normal;">req.params.id</code>),在控制器中访问。然后调用<code>populate()</code>来获取相关<code>Book</code>的详细信息。</p>
+
+<h3 id="视图">视图</h3>
+
+<p>创建 <strong>/views/bookinstance_detail.pug</strong>,并复制到下面的内容中。</p>
+
+<pre class="brush: js">extends layout
+
+block content
+
+<strong>  h1 ID: #{bookinstance._id}</strong>
+
+  p #[strong Title:]
+    a(href=bookinstance.book.url) #{bookinstance.book.title}
+  p #[strong Imprint:] #{bookinstance.imprint}
+
+  p #[strong Status:]
+    if bookinstance.status=='Available'
+      span.text-success #{bookinstance.status}
+    else if bookinstance.status=='Maintenance'
+      span.text-danger #{bookinstance.status}
+    else
+      span.text-warning #{bookinstance.status}
+
+  if bookinstance.status!='Available'
+    p #[strong Due back:] #{bookinstance.due_back}
+</pre>
+
+<p>本模组中的所有东西,都在先前的章节演示过了。</p>
+
+<h3 id="它看起來像是">它看起來像是?</h3>
+
+<p>运行本应用,并打开浏览器访问 <a href="http://localhost:3000/">http://localhost:3000/</a>。选择 All book-instances 连结,然后选择其中一本。如果每个东西都设定正确了,你的网站看起来应该像是底下的截图。</p>
+
+<p><img alt="BookInstance Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14472/LocalLibary_Express_BookInstance_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 362px; margin: 0px auto; width: 1000px;"></p>
+
+<h2 id="自我挑战">自我挑战</h2>
+
+<p>目前,我们网站上显示的大多数日期,都使用默认的 JavaScript 格式(例如 <em>Tue Dec 06 2016 15:49:58 GMT+1100</em>(AUS东部夏令时间)。本文的挑战,是改善作者<code>Author</code>生命周期日期显示的外观信息(死亡/出生日期)和BookInstance详细信息页面,使用格式:December 6th, 2016。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 您可以使用与我们用于 Book Instance List 的相同方法(将生命周期的虚拟属性,添加到<code>Author</code>模型,并使用<a href="https://www.npmjs.com/package/moment">moment</a>来设置日期字符串的格式)。</p>
+</div>
+
+<p>这一挑战的要求:</p>
+
+<ol>
+ <li>用 BookInstance 详细信息页面中的 <code>due_back_formatted</code> 替换 <code>due_back</code>。</li>
+ <li>更新作者模块以添加寿命虚拟属性。寿命应該有两个值: <em>date_of_birth - date_of_death,這</em>两个值的格式与 <code>BookInstance.due_back_formatted</code>的日期格式相同。</li>
+ <li>在当前使用<code>date_of_birth</code> 和 <code>date_of_death</code>的所有视图中,使用 <code>Author.lifespan</code> 。</li>
+</ol>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html
new file mode 100644
index 0000000000..1e8403f0a5
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html
@@ -0,0 +1,69 @@
+---
+title: 书本实例列表页面
+slug: learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page
+---
+<p>接下来,我们将实作图书馆中所有书本实例 (<code>BookInstance</code>) 的列表页面。这个页面需要包含与每个 <code>BookInstance</code> (链接到其详细信息页面) 关联的书本 <code>Book</code> 标题,以及<code>BookInstance</code>模型中的其他信息,包含每个副本的状态,印记和唯一ID。唯一ID的文字,应该链接到 <code>BookInstance</code> 详细信息页面。</p>
+
+<h2 class="highlight-spanned" id="控制器"><span class="highlight-span">控制器</span></h2>
+
+<p><code>BookInstance</code>列表控制器函数,需要获取所有书本实例的列表,填充关联的书本信息,然后将列表传递给模板以进行呈现。</p>
+
+<p>打开 <strong>/controllers/bookinstanceController.js</strong>。找到导出的 <code>bookinstance_list()</code> 控制器方法,并用以下代码替换它(更改后的代码以粗体显示)。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all BookInstances.</span>
+exports<span class="punctuation token">.</span>bookinstance_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+
+ BookInstance<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">populate</span><span class="punctuation token">(</span><span class="string token">'book'</span><span class="punctuation token">)</span>
+ <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_bookinstances<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span>
+ <span class="comment token">// Successful, so render</span>
+ res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'bookinstance_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Book Instance List'</span><span class="punctuation token">,</span> bookinstance_list<span class="punctuation token">:</span> list_bookinstances <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre>
+
+<p>此方法使用模型的<code>find()</code>函数,返回所有<code>BookInstance</code>对象。然后它将一个调用,以菊花链方式连接到<code>populate()</code>,附加书本<code>book</code>字段,这将使用完整的<code>Book</code>文档,替换每个<code>BookInstance</code>存储的书本ID。</p>
+
+<p>成功时,传递给查询的回调,会呈现 <strong>bookinstance_list</strong> (.pug)模板,并将标题<code>title</code>和书籍实例列表<code>bookinstance_list</code>作为变量传递。</p>
+
+<h2 class="highlight-spanned" id="视图"><span class="highlight-span">视图</span></h2>
+
+<p>创建 <strong>/views/bookinstance_list.pug</strong> ,並複制貼上底下的文字。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span>
+
+block content
+ h1<span class="operator token">=</span> title
+
+ ul
+ each val <span class="keyword token">in</span> bookinstance_list
+ li
+ <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>val<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>book<span class="punctuation token">.</span>title<span class="punctuation token">}</span> <span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>imprint<span class="punctuation token">}</span> <span class="operator token">-</span>
+ <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">==</span><span class="string token">'Available'</span>
+ span<span class="punctuation token">.</span>text<span class="operator token">-</span>success #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span>
+ <span class="keyword token">else</span> <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">==</span><span class="string token">'Maintenance'</span>
+ span<span class="punctuation token">.</span>text<span class="operator token">-</span>danger #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span>
+ <span class="keyword token">else</span>
+ span<span class="punctuation token">.</span>text<span class="operator token">-</span>warning #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span>
+ <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">!=</span><span class="string token">'Available'</span>
+ span <span class="function token"> </span><span class="punctuation token">(</span>Due<span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>due_back<span class="punctuation token">}</span> <span class="punctuation token">)</span>
+
+ <span class="keyword token">else</span>
+ li There are no book copies <span class="keyword token">in</span> <span class="keyword token">this</span> library<span class="punctuation token">.</span></code></pre>
+
+<p>这个視图与其他視图非常相似。它扩展了布局,替换内容區块,显示从控制器传入的标题<code>title</code>,并遍历<code>bookinstance_list</code> 中的所有书籍副本。对于每个副本,我们都会显示它的状态(用颜色编码),如果书本不可用,则显示其预期返回日期。這裡引入了一个新功能 — 我们可以在标签之后使用点符号表示法,來指定一個类別。因此,<code>span.text-success</code> 将被编译为 <code>&lt;span class="text-success"&gt;</code> (也可以用 Pug 编写为 <code>span(class="text-success")</code>.</p>
+
+<h2 class="highlight-spanned" id="它看起來像是"><span class="highlight-span">它看起來像是?</span></h2>
+
+<p>运行本应用,打开浏览器访问 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>,然后选择 All book-instances 连结。假如每个东西都设定正确了,你的网站看起来应该像是底下的截图。</p>
+
+<p><img alt="BookInstance List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14474/LocalLibary_Express_BookInstance_List.png" style="border-style: solid; border-width: 1px; display: block; height: 322px; margin: 0px auto; width: 1200px;"></p>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 下个部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment">日期格式化与使用 moment</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html
new file mode 100644
index 0000000000..8abbf8d290
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html
@@ -0,0 +1,58 @@
+---
+title: 使用 moment 做日期格式化
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment
+---
+<p>我们模型的日期预设呈现很难看: <em>Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time)</em>。在本节中,我们将展示如何更新上一节中的 書本實例 BookInstance 列表页面,以更友好的格式显示<code>due_date</code>字段:December 6th, 2016。</p>
+
+<p>我们将使用的方法,是在我们的<code>BookInstance</code>模型中,创建一个返回格式化日期的虚拟屬性。我们将使用<a class="external external-icon" href="https://www.npmjs.com/package/moment" rel="noopener">moment</a> 来做实际的格式化,这是一个轻量级JavaScript日期库,用于解析,验证,操作和格式化日期。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 我们可以直接在 Pug 模板中,使用 <em>moment </em>格式化字符串,或者可以在许多其它地方格式化字符串。使用虚拟属性,可以使我们获得格式化的日期,這与我们当前获取 <code>due_date</code> 的方式完全相同。</p>
+</div>
+
+<h2 class="highlight-spanned" id="安装_moment">安装<span class="highlight-span"> moment</span></h2>
+
+<p>在项目的根目录,输入下列命令</p>
+
+<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install moment</code></pre>
+
+<h2 class="highlight-spanned" id="创建虚拟属性">创建虚拟属性</h2>
+
+<ol>
+ <li>打开<strong> ./models/bookinstance.js</strong>.</li>
+ <li>在此页面最上方,引用 moment
+ <pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">var</span> moment <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'moment'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+ </li>
+</ol>
+
+<p>在 url 属性后面,加入虚拟属性 <code>due_back_formatted</code> 。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js">BookInstanceSchema
+<span class="punctuation token">.</span><span class="function token">virtual</span><span class="punctuation token">(</span><span class="string token">'due_back_formatted'</span><span class="punctuation token">)</span>
+<span class="punctuation token">.</span><span class="keyword token">get</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="keyword token">return</span> <span class="function token">moment</span><span class="punctuation token">(</span><span class="keyword token">this</span><span class="punctuation token">.</span>due_back<span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">format</span><span class="punctuation token">(</span><span class="string token">'MMMM Do, YYYY'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<div class="note">
+<p><strong>注意:</strong> 格式化方法可以使用几乎任何模式显示日期。<a class="external external-icon" href="http://momentjs.com/docs/#/displaying/" rel="noopener">moment文档</a>中,可以找到表示不同日期组件的语法。</p>
+</div>
+
+<h2 class="highlight-spanned" id="更新视图"><span class="highlight-span">更新视图</span></h2>
+
+<p>打开 <strong>/views/bookinstance_list.pug</strong> ,然后用  <code>due_back_formatted</code> 取代 <code>due_back</code> 。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"> <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">!=</span><span class="string token">'Available'</span>
+ <span class="comment token">//span (Due: #{val.due_back} )</span>
+ span <span class="function token"> </span><span class="punctuation token">(</span>Due<span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>due_back_formatted<span class="punctuation token">}</span> <span class="punctuation token">)</span> </code></pre>
+
+<p>这就是本章节的全部了。如果你访问侧边栏的 All book-instances ,你应该会看到所有的归还日期都更吸引人了!</p>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 下一個部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page">作者列表页面、种类列表页面、与自我挑战</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html
new file mode 100644
index 0000000000..f720812c50
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html
@@ -0,0 +1,139 @@
+---
+title: 使用 async 进行非同步流控制
+slug: learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async
+---
+<p>有些本地图书馆网页的控制器代码,会依赖多重非同步要求的结果,可能会需要以某种特定次序运行,或者以平行方式运行。为了管理流控制,并在我们所有需要用到的信息,都已经可以取用的时候,再绘制网页,我们将使用许多人采用的 node <a class="external external-icon" href="https://www.npmjs.com/package/async" rel="noopener">async</a> 模组。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 在 JavaScript 中有许多其他方法,可以管理异步行为和流控制,包括相对较新的 JavaScript 语言功能,如 <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Techniques/Promises">Promises</a>。</p>
+</div>
+
+<p>Async 有很多有用的方法(请查看<a href="http://caolan.github.io/async/docs.html">文档</a>)。一些最重要的功能是:</p>
+
+<ul>
+ <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#parallel" rel="noopener">async.parallel()</a></code> 执行必须并行执行的任何操作。</li>
+ <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#series" rel="noopener">async.series()</a></code> 用于当需要确保异步操作是序列执行的。</li>
+ <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#waterfall" rel="noopener">async.waterfall()</a></code> 用于必须序列运行的操作,每个操作取决于前面操作的结果。</li>
+</ul>
+
+<h2 class="highlight-spanned" id="为什么需要这么做">为什么需要这么做?</h2>
+
+<p>我们在 Express 中使用的大多数方法,都是异步的 - 您指定要执行的操作,传递回调。该方法立即返回,并在请求的操作完成时,调用回调。按照 Express 中的惯例,回调函数将错误值作为第一个参数传递(或成功时为 <code>null</code>),并将函数的结果(如果有的话)作为第二个参数传递。</p>
+
+<p>如果控制器只需要执行<strong>一个</strong>异步操作,来获取呈现页面所需的信息,那么实现很简单 - 我们只需在回调中呈现模板。下面的代码片段,显示了一个函数,该函数呈现模型 <code>SomeModel</code> 的计数(使用Mongoose <code><a class="external external-icon" href="http://mongoosejs.com/docs/api.html#model_Model.count" rel="noopener">count()</a></code>方法):</p>
+
+<pre class="brush: js"><code>exports.some_model_count = function(req, res, next) {
+
+</code> SomeModel.count({ a_model_field: 'match_value' }, function (err, count) {
+ // ... do something if there is an err
+
+  // On success, render the result by passing count into the render function (here, as the variable 'data').
+  res.render('the_template', { data: count } );
+  });
+<code>}</code>
+</pre>
+
+<p>但是,如果您需要进行<strong>多个</strong>异步查询,并且在完成所有操作之前,无法呈现页面,该怎么办?一个单纯的实现可以用 “菊花链” 连接请求,在先前请求的回调中,启动后续请求,并在最终回调中呈现响应。这种方法的问题,是我们的请求必须串行运行,即使并行运行它们可能更有效。这也可能导致复杂的嵌套代码,通常称为<a href="http://callbackhell.com/">回调地狱</a>。</p>
+
+<p>一个更好的解决方案,是并行执行所有请求,然后在所有查询完成后执行单个回调。这是 Async 模块简化的流操作!</p>
+
+<h2 class="highlight-spanned" id="平行的非同步操作"><span class="highlight-span">平行的非同步操作</span></h2>
+
+<p>方法<code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#parallel" rel="noopener">async.parallel()</a></code>用于并行运行多个异步操作。</p>
+
+<p><code>async.parallel()</code> 的第一个参数,是要运行的异步函数的集合(数组,对象或其他可迭代的)。每个函数都传递一个回调函数<code>callback(err, result)</code> ,它必须在完成时调用错误<code>err</code>(可以为<code>null</code>)和可选的结果值。</p>
+
+<p><code>async.parallel()</code>的可选第二个参数是一个回调,它将在第一个参数中的所有函数完成时运行。回调的调用,是使用错误参数和包含各个异步操作结果的结果集合。结果集合与第一个参数的类型相同(即,如果传递异步函数数组,则将使用结果数组,调用最终回调)。如果任何并行函数报告错误,则提前调用回调(具有错误值)。</p>
+
+<p>下面的示例,显示了当我们将对象作为第一个参数传递时它是如何工作的。如您所见,结果将返回到一个对象中,该对象具有与传入的原始函数相同的属性名称。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">parallel</span><span class="punctuation token">(</span><span class="punctuation token">{</span>
+ one<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ two<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span>
+ something_else<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="comment token">// optional callback</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}</span>
+ <span class="punctuation token">}</span>
+<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<p>如果您将一组函数,作为第一个参数传递,则结果将是一个数组(数组顺序结果,将与声明函数的原始顺序匹配 - 而不是它们完成的顺序)。</p>
+
+<h2 class="highlight-spanned" id="序列的非同步操作"><span class="highlight-span">序列的非同步操作</span></h2>
+
+<p><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#series" rel="noopener">async.series()</a></code>方法用于按顺序运行多个异步操作,后续函数不依赖于先前函数的输出。它本质上是声明的,并且行为与<code>async.parallel()</code>.相同。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">series</span><span class="punctuation token">(</span><span class="punctuation token">{</span>
+ one<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ two<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span>
+ something_else<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="comment token">// optional callback after the last asynchronous function completes.</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} </span>
+ <span class="punctuation token">}</span>
+<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<div class="note">
+<p><strong>注意:</strong> ECMAScript(JavaScript)语言规范指出,对象的枚举顺序是未定义的,因此可能不会按照在所有平台上指定它们的顺序,调用这些函数。如果顺序真的很重要,那么你应该传递一个数组而不是一个对象,如下所示。</p>
+</div>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">series</span><span class="punctuation token">(</span><span class="punctuation token">[</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// do some stuff ...</span>
+ <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'one'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// do some more stuff ... </span>
+ <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'two'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span>
+ <span class="punctuation token">]</span><span class="punctuation token">,</span>
+ <span class="comment token">// optional callback</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// results is now equal to ['one', 'two'] </span>
+ <span class="punctuation token">}</span>
+<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<h2 class="highlight-spanned" id="依赖序列的非同步操作">依赖序列的非同步操作</h2>
+
+<p>方法<code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#waterfall" rel="noopener">async.waterfall()</a></code>用于在每个操作依赖于前一个操作的结果时,依次运行多个异步操作。</p>
+
+<p>每个异步函数调用的回调,包含第一个参数的<code>null</code>,与后续参数里的结果。该序列中的每个函数,都将前一个回调的结果参数,作为第一个参数,然后是回调函数。</p>
+
+<p>完成所有操作后,将使用上一操作的结果,调用最终回调。当您参考下面的代码片段时,这种工作方式会更加明确(此示例来自 <em>async</em> 文档):</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">waterfall</span><span class="punctuation token">(</span><span class="punctuation token">[</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'one'</span><span class="punctuation token">,</span> <span class="string token">'two'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>arg1<span class="punctuation token">,</span> arg2<span class="punctuation token">,</span> callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// arg1 now equals 'one' and arg2 now equals 'two' </span>
+ <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'three'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="keyword token">function</span><span class="punctuation token">(</span>arg1<span class="punctuation token">,</span> callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// arg1 now equals 'three'</span>
+ <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'done'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span>
+<span class="punctuation token">]</span><span class="punctuation token">,</span> <span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> result<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// result now equals 'done'</span>
+<span class="punctuation token">}</span>
+<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<h2 class="highlight-spanned" id="安装_async">安装<span class="highlight-span"> async</span></h2>
+
+<p>使用 NPM 包管理器安装 async 模块,以便我们可以在代码中使用它。您可以常规方式执行此操作,在 LocalLibrary 项目的根目录中,打开命令提示并输入以下命令:</p>
+
+<p> </p>
+
+<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install async</code></pre>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5下一个部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer">模板入门</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html
new file mode 100644
index 0000000000..825474b9ab
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html
@@ -0,0 +1,120 @@
+---
+title: 种类细节页面
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page
+---
+<p>种类细节页面,需要利用<code>_id</code> 字段值 (自动生成) ,以呈现特定种类实例的信息。此页面应该呈现种类名称,各个种类的所有书本列表(每本书都连结到书本的细节页面)。</p>
+
+<h2 id="控制器">控制器</h2>
+
+<p>打开 <strong>/controllers/genreController.js</strong> ,并在档案最上方引用 async 和 Book 模组。</p>
+
+<pre class="brush: js">var Book = require('../models/book');
+var async = require('async');
+</pre>
+
+<p>找到导出的<code>genre_detail</code><code>()</code>控制器方法,并将其替换为以下代码。</p>
+
+<pre class="brush: js">// Display detail page for a specific Genre.
+exports.genre_detail = function(req, res, next) {
+
+<strong>    async.parallel({
+        genre: function(callback) {
+            Genre.findById(req.params.id)
+              .exec(callback);
+        },
+
+        genre_books: function(callback) {
+          Book.find({ 'genre': req.params.id })
+          .exec(callback);
+        },
+
+    }, function(err, results) {
+        if (err) { return next(err); }
+        if (results.genre==null) { // No results.
+            var err = new Error('Genre not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render
+        res.render('genre_detail', { title: 'Genre Detail', genre: results.genre, genre_books: results.genre_books } );
+    });</strong>
+
+};
+</pre>
+
+<p>该方法使用<code>async.parallel()</code>,并行查询类型名称及其相关联的书本,并在(如果)两个请求成功完成时,回调呈现页面。</p>
+
+<p>所需种类记录的 ID ,在 URL 的末尾编码,并根据路由定义(<strong>/genre/:id</strong>)自动提取。通过请求参数(<code style="font-style: normal; font-weight: normal;">req.params.id</code><code style="font-style: normal; font-weight: normal;">)</code>在控制器内访问 ID。它在<code style="font-style: normal; font-weight: normal;">Genre.findById()</code>中用于获取当前种类。它还用于获取符合当前种类的所有<code>Book</code>对象,就是在种类字段中具有种类ID的那些 <code>Book.find({ 'genre': req.params.id })</code>。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 如果数据库中不存在该类型(即它可能已被删除),则<code>findById()</code>将成功返回,但没有结果。在这种情况下,我们想要显示一个“未找到”页面,因此我们创建一个<code>Error</code>对象,并将其传递给链中的下一个中间件函数<code>next</code>。</p>
+
+<pre class="brush: js"><strong>if (results.genre==null) { // No results.
+ var err = new Error('Genre not found');
+ err.status = 404;
+ return next(err);
+}</strong>
+</pre>
+
+<p>然后,此消息将传播给我们的错误处理代码(这是在我们<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/skeleton_website#error_handling">生成应用程序框架</a>时设置的 - 有关更多信息,请参阅<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Introduction#Handling_errors">处理错误</a>)。</p>
+</div>
+
+<p>渲染的视图是 <strong>genre_detail</strong>,它传递了该类型的标题<code>title</code>,种类<code>genre</code>和书本列表的变量(<code>genre_books</code>)。</p>
+
+<h2 id="视图">视图</h2>
+
+<p>创建 <strong>/views/genre_detail.pug</strong> ,并填写底下文字:</p>
+
+<pre class="brush: js">extends layout
+
+block content
+
+  <strong>h1 Genre: #{genre.name}</strong>
+
+  div(style='margin-left:20px;margin-top:20px')
+
+    h4 Books
+
+    dl
+    each book in genre_books
+      dt
+        a(href=book.url) #{book.title}
+      dd #{book.summary}
+
+    else
+      p This genre has no books
+</pre>
+
+<p>这个视图跟我们其它的模板非常相似。主要的差别在于,我们不使用 <code>title</code> 传送第一个标题 (虽然它还是用在底层的 <strong>layout.pug</strong> 模板,设定页面的标题)。</p>
+
+<h2 id="它看起來像是">它看起來像是?</h2>
+
+<p>运行本应用,并打开浏览器访问 <a href="http://localhost:3000/">http://localhost:3000/</a>。选择 All genres 连结,然后选择其中一个种类 (例如,"Fantasy")。如果每样东西都设定正确了,你的页面看起来应该像底下的截图。</p>
+
+<p><img alt="Genre Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14462/LocalLibary_Express_Genre_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 523px; margin: 0px auto; width: 1000px;"></p>
+
+<div class="note">
+<p>您可能会收到与此类似的错误:</p>
+
+<pre class="brush: bash">Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre"
+</pre>
+
+<p>这是来自 <strong>req.params.id</strong> 的 mongoose 错误。要解决这个问题,首先需要在<strong> genreController.js</strong> 页面上要求mongoose,如下所示:</p>
+
+<pre class="brush: js"> var mongoose = require('mongoose');
+</pre>
+然后使用 <strong>mongoose.Types.ObjectId()</strong>将 id 转换为可以使用的。例如:
+
+<pre class="brush: js">exports.genre_detail = function(req, res, next) {
+ var id = mongoose.Types.ObjectId(req.params.id);
+ ...
+</pre>
+</div>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 下一个部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page">书本细节页面</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/home_page/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/home_page/index.html
new file mode 100644
index 0000000000..27dbdb5788
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/home_page/index.html
@@ -0,0 +1,134 @@
+---
+title: 主页
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Home_page
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page
+---
+<p>我们创建的第一个页面,是网站的主页面,可以从网站的根目录 (<code>'/'</code>) ,或者 catalog 的根目录 (<code>catalog/</code>) 访问。这将呈现一些网站的静态文字描述,以及动态计算数据库中不同记录类型的“计数”。</p>
+
+<p>我们已经为主页创建了一个路由。为了完成页面,我们需要更新控制器函数,以从数据库中提取记录的“计数”,并创建一个可用于呈现页面的视图(模板)。</p>
+
+<h2 class="highlight-spanned" id="路由"><span class="highlight-span">路由</span></h2>
+
+<p>在 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes">前面的教程</a>,我们创建 index 页面路由。此处要提醒的是,所有的路由函式,都定义在 <strong>/routes/catalog.js</strong>:</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// GET catalog home page.</span>
+router<span class="punctuation token">.</span><span class="keyword token">get</span><span class="punctuation token">(</span><span class="string token">'/'</span><span class="punctuation token">,</span> book_controller<span class="punctuation token">.</span>index<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="comment token">//This actually maps to /catalog/ because we import the route with a /catalog prefix</span></code></pre>
+
+<p>在 <strong>/controllers/bookController.js</strong> 中,定义回调函数参数(<code>book_controller.index</code>) :</p>
+
+<pre class="brush: js"><code>exports.index = function(req, res, next) {
+ res.send('NOT IMPLEMENTED: Site Home Page');
+}</code>
+</pre>
+
+<p>我们扩展这个控制器函数,以从我们的模型获取信息,然后使用模板(视图)渲染它。</p>
+
+<h2 class="highlight-spanned" id="控制器"><span class="highlight-span">控制器</span></h2>
+
+<p>索引控制器函数需要获取以下有关信息,即数据库中有多少<code>Book</code>,<code>BookInstance</code>,可用的<code>BookInstance</code>,<code>Author</code>和<code>Genre</code>记录,将这些数据渲染到模板中,以创建HTML页面,然后将其返回到HTTP响应中。</p>
+
+<div class="note">
+<p><strong>Note:</strong> 我们使用<code><a class="external external-icon" href="http://mongoosejs.com/docs/api.html#model_Model.count" rel="noopener">count()</a></code> 方法来获取每个模型的实例数量。这在具有一组可选条件的模型上进行调用,以匹配第一个参数,而回调放在第二个参数(如<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">使用数据库</a><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">(Mongoose)</a>)中讨论的那样,并且还可以返回 <code>Query</code> ,然后稍后以回调执行它。当数据库返回计数时,将返回该回调,并将错误值(或空值<code>null</code>)作为第一个参数,并将记录计数(如果存在错误,则返回null)作为第二个参数。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js">SomeModel<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">(</span><span class="punctuation token">{</span> a_model_field<span class="punctuation token">:</span> <span class="string token">'match_value'</span> <span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> count<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ <span class="comment token">// ... do something if there is an err</span>
+ <span class="comment token">// ... do something with the count if there was no error</span>
+ <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+</div>
+
+<p>打开 <strong>/controllers/bookController.js</strong>. 在文件顶部附近,您应该看到导出的 <code>index()</code> 函数。</p>
+
+<pre class="brush: python line-numbers language-python"><code class="language-python">var Book = require('../models/book')
+
+exports.index = function(req, res, next) {
+ res.send('NOT IMPLEMENTED: Site Home Page');
+}</code></pre>
+
+<p>用以下代码片段替换上面的所有代码。这要做的第一件事,是导入(<code>require()</code>)所有模型(以粗体突出高亮显示)。我们需要这样做,是因为我们将使用它们来获取记录的计数。然后它会导入异步模块<em> async</em> 。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">var</span> Book <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'../models/book'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="keyword token">var</span> Author <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'../models/author'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="keyword token">var</span> Genre <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'../models/genre'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="keyword token">var</span> BookInstance <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'../models/bookinstance'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+<span class="keyword token">var</span> <span class="keyword token">async</span> <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'async'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+exports<span class="punctuation token">.</span>index <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+
+ <span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">parallel</span><span class="punctuation token">(</span><span class="punctuation token">{</span>
+ book_count<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ Book<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">({}, </span>callback<span class="punctuation token">); // Pass an empty object as match condition to find all documents of this collection</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ book_instance_count<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ BookInstance<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">({}, </span>callback<span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ book_instance_available_count<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ BookInstance<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">(</span><span class="punctuation token">{</span>status<span class="punctuation token">:</span><span class="string token">'Available'</span><span class="punctuation token">}</span><span class="punctuation token">,</span> callback<span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ author_count<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ Author<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">({}, </span>callback<span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ genre_count<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ Genre<span class="punctuation token">.</span><span class="function token">count</span><span class="punctuation token">({}, </span>callback<span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span>
+ <span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'index'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Local Library Home'</span><span class="punctuation token">,</span> error<span class="punctuation token">:</span> err<span class="punctuation token">,</span> data<span class="punctuation token">:</span> results <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre>
+
+<p><code>async.parallel()</code> 方法传递一个对象,其中包含用于获取每个模型计数的函数。这些函数都是在同一时间开始的。当这些函数全部完成时,最终回调将与结果参数中的计数(或错误)一起被调用。</p>
+
+<p>成功时,回调函数调用 <code><a class="external external-icon" href="http://expressjs.com/en/4x/api.html#res.render" rel="noopener">res.render()</a></code>,指定名为 '<strong>index</strong>' 的视图(模板),以及一个对象包含了要插入其中的数据 (这包括我们模型计数的结果对象)。数据以键值对的形式提供,可以使用键在模板中访问。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 上面的<code>async.parallel()</code>裡的回调函数有点不寻常,因为不管是否出现错误,我们都会渲染页面(通常您可能使用单独的执行路径来处理错误的显示)。</p>
+</div>
+
+<h2 class="highlight-spanned" id="视图">视图</h2>
+
+<p>打开 <strong>/views/index.pug</strong> ,并用底下文字取代它的内容。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span>
+
+block content
+ h1<span class="operator token">=</span> title
+ p Welcome to #<span class="punctuation token">[</span>em LocalLibrary<span class="punctuation token">]</span><span class="punctuation token">,</span> a very basic Express website developed <span class="keyword token">as</span> a tutorial example on the Mozilla Developer Network<span class="punctuation token">.</span>
+
+ h1 Dynamic content
+
+ <span class="keyword token">if</span> error
+ p Error getting dynamic content<span class="punctuation token">.</span>
+ <span class="keyword token">else</span>
+ p The library has the following record counts<span class="punctuation token">:</span>
+
+ ul
+ li #<span class="punctuation token">[</span>strong Books<span class="punctuation token">:</span><span class="punctuation token">]</span> <span class="operator token">!</span><span class="punctuation token">{</span>data<span class="punctuation token">.</span>book_count<span class="punctuation token">}</span>
+ li #<span class="punctuation token">[</span>strong Copies<span class="punctuation token">:</span><span class="punctuation token">]</span> <span class="operator token">!</span><span class="punctuation token">{</span>data<span class="punctuation token">.</span>book_instance_count<span class="punctuation token">}</span>
+ li #<span class="punctuation token">[</span>strong Copies available<span class="punctuation token">:</span><span class="punctuation token">]</span> <span class="operator token">!</span><span class="punctuation token">{</span>data<span class="punctuation token">.</span>book_instance_available_count<span class="punctuation token">}</span>
+ li #<span class="punctuation token">[</span>strong Authors<span class="punctuation token">:</span><span class="punctuation token">]</span> <span class="operator token">!</span><span class="punctuation token">{</span>data<span class="punctuation token">.</span>author_count<span class="punctuation token">}</span>
+ li #<span class="punctuation token">[</span>strong Genres<span class="punctuation token">:</span><span class="punctuation token">]</span> <span class="operator token">!</span><span class="punctuation token">{</span>data<span class="punctuation token">.</span>genre_count<span class="punctuation token">}</span></code></pre>
+
+<p>这个视图很简单。我们扩展了 <strong>layout.pug</strong> 基本模板,覆盖了名为 '<strong>content</strong>' 的模块 <code>block</code>。第一个<code>h1</code>标题,将是传递给<code>render()</code>函数的<code>title</code> 变量的转义文本 — 请注意 '<code>h1=</code>' 的使用方式,将使得接下來的文本,被视为 JavaScript 表达式。然后我们放入一个介绍本地图书馆的段落。</p>
+
+<p>在动态内容标题下,我们检查从<code>render()</code>函数传入的错误变量,是否已定义。如果是这样,我们列出这个错误。如果不是,我们从<code>data</code>变量中,获取并列出每个模型的副本数量。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 我们没有转义计数值 (i.e. 我们使用 <code>!{}</code> 语法) ,因为计数值已经被计算过了。如果信息是由终端用户提供的,那么我们就会转义該变量,以用于显示。</p>
+</div>
+
+<h2 class="highlight-spanned" id="它看起来像是">它看起来像是?</h2>
+
+<p>此处,我们应该已经创建了呈现index页面,所需要的每样东西。运行本地图书馆应用,并打开浏览器访问 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>。如果每样东西都设定正确了,你的网站看起来应该像底下的截图。</p>
+
+<p><img alt="Home page - Express Local Library site" src="https://mdn.mozillademos.org/files/14458/LocalLibary_Express_Home.png" style="display: block; height: 440px; margin: 0px auto; width: 1000px;"></p>
+
+<div class="note">
+<p><strong>注意: </strong>您将无法使用侧边栏链接,因为这些网页的网址,视图和模板尚未定义。例如,如果您尝试,取决于您点击的链接,您将获取“尚未实作:图书清单”等错误。在“控制器”文件中的不同控制器中,會指定这些字符串文字(将被合适的数据替换)。</p>
+</div>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 下個部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page">书本列表页面</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/index.html
new file mode 100644
index 0000000000..b59601e248
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/index.html
@@ -0,0 +1,87 @@
+---
+title: 'Express 教程 5: 呈现图书馆数据'
+slug: learn/Server-side/Express_Nodejs/Displaying_data
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</div>
+
+<p class="summary">我们现在准备好要新增网页,以显示<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">本地图书馆网站</a>的书本与其它资料。这些网页将包含一个主页 ,显示我们拥有的每个模型的记录数,以及所有模型的清单和详细信息页面。借此,我们将获得从数据库获取记录、以及使用模板的实战经验。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前置条件:</th>
+ <td>完成先前教程主题 (包含 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/routes">Express 教程 Part 4: 路由与控制器</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>
+ <p>了解如何使用异步模组与Pug 样版语言,以及如何从我们的控制器函数中的URL取得信息。</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>在我们先前的教程中,定义了可以用来跟资料库互动的 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Mongoose models</a> ,并创建了一些初始的图书馆记录。我们接着<a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">创建本地图书馆网站需要的所有路由</a>  ,但仅使用"空壳控制器" 函数(这些是骨架控制器函数,当一个网页被存取时,只回传一个"未实现" 信息)。</p>
+
+<p>下一步,是为这些显示图书馆信息的网页,提供适当的实现(我们将在后面的文章,聚焦网页表单的实现,像是创建、更新、删除信息)。这包含了更新控制器函数,以利用我们的模型获取记录,并定义模板,为用户显示这些信息。</p>
+
+<p>我们在一开始,提供概述/入门主题,解释在控制器函数中,如何管理异步操作,以及如何使用Pug编写模板。然后我们将为每一个主要的 "只读" 页面提供实现步骤,并且在使用到任何特别的、新的特性时附上简短的解释说明。</p>
+
+<p>本教程的最后,你对路由、异步函数、视图、模型如何实际运作,应该有了更好的理解。</p>
+
+<h2 id="本教程的章节">本教程的章节</h2>
+
+<p>本教程分为下列章节,讲解了为了显示图书馆网站需求的页面而新增各种特性的过程 。在进入下一个教程之前,你需要阅读并逐一实现下列章节。</p>
+
+<ol>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async">使用 async 进行异步流控制</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer">模版入门</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template">本地图书馆基础样版</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page">主页</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page">书本清单页面</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page">书本实例清单页面</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment">日期格式化-使用 moment</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page">作者清单页面、分类清单页面</a><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge">、</a><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page">与自我挑战</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page">分类详情页面</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page">书本详情页面</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page">作者详情页面</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge">书本实例详情页面、与自我挑战</a></li>
+</ol>
+
+<h2 id="总结">总结</h2>
+
+<p>我们现在已经为我们的网站,创建了所有 "只读" 的页面: 一个主页,可以显示每一个模组的实例数量,书本的列表与详细信息页面,书本的实例、作者、分类。沿着目前的学习路径,我们学到了许多基本知识,有控制器、在异步操作时管理流控制、使用Pug创建视图模板、使用模型查询数据库、如何从视图传送信息到模板、如何创建并扩展模板。而完成挑战的人,还会学到如何用moment处理日期。</p>
+
+<p>在下一篇文章,我们将依据目前为止学到的知识,创建HTML 表单以及表单管理代码,开始修改储存在网站中的资料。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="http://caolan.github.io/async/docs.html">Async </a>模组 (Async 模组官方文件)</li>
+ <li><a href="https://expressjs.com/en/guide/using-template-engines.html">在Express中使用模板引擎</a> (Express 官方文件)</li>
+ <li><a href="https://pugjs.org/api/getting-started.html">Pug</a> (Pug 官方文件)</li>
+ <li><a href="http://momentjs.com/docs/">Moment</a> (Moment 官方文件)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</p>
+
+
+
+<h2 id="本教程文章列表">本教程文章列表</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node 介绍</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/development_environment">架设 Node (Express) 开发环境</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express 教程: 本地图书馆网站</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express 教程 2: 新建网站骨架</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/mongoose">Express 教程 3: 使用数据库 (Mongoose)</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/routes">Express 教程 4: 路由和控制器</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/forms">Express 教程 6: 使用表单</a></li>
+ <li><a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/deployment">Express 教程 7: 部署至生产环境</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html
new file mode 100644
index 0000000000..41d851e7d7
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html
@@ -0,0 +1,69 @@
+---
+title: 本地图书馆基础模板
+slug: learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template
+---
+<p>现在我们了解如何使用Pug拓展模板,让我们开始项目,创建一个基础模板。这个模板会有一个侧边栏,连结到本教程中将要创建的各个页面(例如,呈现并创建书本、种类、作者等等),以及一个主要内容区域,我们将在每个页面中进行覆写。</p>
+
+<p>开启 <strong>/views/layout.pug</strong> ,并以下列代码置换其内容。</p>
+
+<pre class="brush: html line-numbers language-html notranslate"><code class="language-html">doctype html
+html(lang='en')
+ head
+ title= title
+ meta(charset='utf-8')
+ meta(name='viewport', content='width=device-width, initial-scale=1')
+ link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css')
+ script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js')
+ script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js')
+ link(rel='stylesheet', href='/stylesheets/style.css')
+ body
+ div(class='container-fluid')
+ div(class='row')
+ div(class='col-sm-2')
+ block sidebar
+ ul(class='sidebar-nav')
+ li
+ a(href='/catalog') Home
+ li
+ a(href='/catalog/books') All books
+ li
+ a(href='/catalog/authors') All authors
+ li
+ a(href='/catalog/genres') All genres
+ li
+ a(href='/catalog/bookinstances') All book-instances
+ li
+ hr
+ li
+ a(href='/catalog/author/create') Create new author
+ li
+ a(href='/catalog/genre/create') Create new genre
+ li
+ a(href='/catalog/book/create') Create new book
+ li
+ a(href='/catalog/bookinstance/create') Create new book instance (copy)
+
+ div(class='col-sm-10')
+ block content</code></pre>
+
+<p>此模板使用(并包含)来自 <a class="external external-icon" href="http://getbootstrap.com/" rel="noopener">Bootstrap</a> 的 JavaScript 和 CSS ,以改进HTML页面的布局和呈现方式。使用Bootstrap 或其它客户端网页框架,是一种快速的方式,可以创建吸引人的网页,能够良好地适应不同的浏览器尺寸,并且允许我们处理页面的呈现,而不需要纠缠于任何不同尺寸的细节—此处我们只想专注于伺服端代码!</p>
+
+<p>布局的安排应该相当明白,假如你已经阅读了之前的 <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#Template_primer">模板入门</a>。注意,使用 <code>block content</code> 当做定位符号,放到页面内容将要放置的地方。</p>
+
+<p>基础模板也参考了一个本地 css 档 (<strong>style.css</strong>) ,此档提供了一些额外的样式。打开 <strong>/public/stylesheets/style.css</strong> ,并用底下的 CSS 代码,取代它的内容:</p>
+
+<pre class="brush: css line-numbers language-css notranslate"><code class="language-css"><span class="selector token"><span class="class token">.sidebar-nav</span> </span><span class="punctuation token">{</span>
+ <span class="property token">margin-top</span><span class="punctuation token">:</span> <span class="number token">20</span>px<span class="punctuation token">;</span>
+ <span class="property token">padding</span><span class="punctuation token">:</span> <span class="number token">0</span><span class="punctuation token">;</span>
+ <span class="property token">list-style</span><span class="punctuation token">:</span> none<span class="punctuation token">;</span>
+<span class="punctuation token">}</span></code></pre>
+
+<p>当我们开始运行网站时,我们应该看到侧边栏出现!在本教程的下个部分,我们将使用以上的布局,来定义各个页面。</p>
+
+<h2 id="下一步">下一步</h2>
+
+<ul>
+ <li>回到 <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li>
+ <li>继续教程 5 的下個部分: <a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page">主页</a></li>
+</ul>
diff --git a/files/zh-cn/learn/server-side/express_nodejs/displaying_data/template_primer/index.html b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/template_primer/index.html
new file mode 100644
index 0000000000..374957bb1b
--- /dev/null
+++ b/files/zh-cn/learn/server-side/express_nodejs/displaying_data/template_primer/index.html
@@ -0,0 +1,149 @@
+---
+title: 模板入门
+slug: learn/Server-side/Express_Nodejs/Displaying_data/Template_primer
+translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer
+---
+<p>模板是一个文字档,定义了一个输出档的结构或者排版,使用定位符号表示,当模板被绘制时,资料将插入到何处(在Express,模板被称为视图)。</p>
+
+<h2 id="Express_模板选择">Express 模板选择</h2>
+
+<p>Express 可以与许多不同的<a href="https://expressjs.com/en/guide/using-template-engines.html">模板渲染引擎</a>一起使用。在本教程中,我们使用<a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a>(以前称为Jade)作为模板。这是最流行的 Node 模板语言,并且官方将自身描述为 “用于编写HTML,语法干净且空格敏感,受 <a class="external external-icon" href="http://haml.info/" rel="noopener">Haml</a>影响很大”。</p>
+
+<p>不同的模板语言使用不同的方法,来定义布局和标记数据的占位符 —  一些使用 HTML 来定义布局,而另一些则使用可以编译为 HTML 的不同标记格式。Pug 是第二种类型;它使用 HTML 的表示形式,其中任何行中的第一个单词,通常表示HTML元素,后续行中的缩进,用于表示嵌套在这些元素中的任何内容。结果是一个页面定义直接转换为 HTML,但可以说更简洁,更容易阅读。</p>
+
+<div class="note">
+<p><strong>注意:</strong> 使用 Pug 的缺点,是它对缩进和空格敏感(如果在错误的位置添加额外的空格,可能会得到没什么帮助的错误代码)。但是,一旦您的模板到位,它们就很容易阅读和维护。</p>
+</div>
+
+<h2 class="highlight-spanned" id="模板组态"><span class="highlight-span">模板</span>组态</h2>
+
+<p>在我们<a href="/zh-CN/docs/Learn/Server-side/Express_Nodejs/skeleton_website">创建骨架网站</a>时,LocalLibrary 配置为使用 <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a>。您应该看到 Pug 模块作为依赖项,包含在网站的 <strong>package.json</strong>文件中,以及 <strong>app.js</strong>文件中的以下配置设置。设置告诉我们,使用 Pug 作为视图引擎,Express 应该在<strong> /views</strong>子目录中搜索模板。</p>
+
+<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// View engine setup.</span>
+app<span class="punctuation token">.</span><span class="keyword token">set</span><span class="punctuation token">(</span><span class="string token">'views'</span><span class="punctuation token">,</span> path<span class="punctuation token">.</span><span class="function token">join</span><span class="punctuation token">(</span>__dirname<span class="punctuation token">,</span> <span class="string token">'views'</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+app<span class="punctuation token">.</span><span class="keyword token">set</span><span class="punctuation token">(</span><span class="string token">'view engine'</span><span class="punctuation token">,</span> <span class="string token">'pug'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<p>如果查看 views 目录,您将看到项目默认视图的 .pug 文件。这包括需要用自己的内容替换的主页(<strong>index.pug</strong>)和基本模板(<strong>layout.pug</strong>)的视图。</p>
+
+<pre><code>/express-locallibrary-tutorial //the project root
+ /views
+ error.pug
+ <strong>index.pug</strong>
+ layout.pug</code>
+</pre>
+
+<h2 class="highlight-spanned" id="模板语法">模板语法</h2>
+
+<p>下面的示例模板文件,展示了许多 Pug 最有用的功能。</p>
+
+<p>首先要注意的是,该文件映射典型 HTML 文件的结构,其中(几乎)每一行中的第一个单词是 HTML 元素,并且缩进用于指示嵌套元素。因此,例如,<code>body</code> 本文元素位于 <code>html</code> 元素内,而段落元素(<code>p</code>)位于 <code>body</code> 元素内等。非嵌套元素(例如,各个段落)位于不同的行上。</p>
+
+<pre class="brush: html line-numbers language-html"><code class="language-html">doctype html
+html(lang="en")
+ head
+ title= title
+ script(type='text/javascript').
+ body
+ h1= title
+
+ p This is a line with #[em some emphasis] and #[strong strong text] markup.
+ p This line has un-escaped data: !{'<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>em</span><span class="punctuation token">&gt;</span></span> is emphasised<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>em</span><span class="punctuation token">&gt;</span></span>'} and escaped data: #{'<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>em</span><span class="punctuation token">&gt;</span></span> is not emphasised<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>em