如何构建和结构一个Node.js MVC应用程序
在一个重要的应用程序中,架构和代码本身的质量一样重要。我们可以有编写良好的代码段,但如果我们没有良好的组织,随着复杂性的增加,我们将会遇到困难。没有必要等到项目完成了一半才开始考虑架构;最好的时间是在开始之前,把我们的目标作为我们选择的灯塔。
Node.js不像Ruby有Rails框架那样有一个事实上的框架,它对架构和代码组织没有强烈的意见。因此,很难开始使用Node构建完整的web应用程序。
在本教程中,我们将使用MVC架构构建笔记应用程序的基本功能。为了实现这一点,我们将使用Hapi.js框架node . js而且SQLite作为数据库,使用Sequelize.js,加上其他小型公用事业,以加快我们的发展。我们将使用哈巴狗模板语言。
什么是MVC?
模型-视图-控制器(或MVC)可能是最流行的应用程序架构之一。和其他很多人一样计算机历史上很酷的事情时,MVC模型被构思出来帕洛阿尔托研究中心为Smalltalk语言提供了一种解决方案,可以用图形用户界面组织应用程序。它是为桌面应用程序创建的,但从那以后,这个想法已经适应了包括Web在内的其他媒介。
我们可以用简单的术语来描述MVC架构:
- 模型:应用程序中处理数据库或任何数据相关功能的部分。
- 视图:用户将看到的所有内容——基本上就是我们要发送给客户端的页面。
- 控制器:我们网站的逻辑,以及模型和视图之间的粘合剂。在这里,我们调用模型来获取数据,然后我们把数据放在视图上,发送给用户。
我们的应用程序将允许我们创建、查看、编辑和删除纯文本笔记。它不会有其他功能,但因为我们已经定义了一个坚实的体系结构,所以以后添加东西时不会有很多麻烦。
本教程假设您的计算机上安装了Node的最新版本。如果不是这样,请咨询我们的关于安装和运行Node的教程.
您可以查看最终的应用程序附带的GitHub存储库,这样您就可以大致了解应用程序的结构。
打好基础
构建任何Node.js应用程序的第一步是创建一个package.json
文件,它将包含我们所有的依赖项和脚本。而不是手动创建这个文件,npm可以使用初始化
命令:
mkdirnotes-boardcdnotes-boardnpminit - y
流程完成后,我们会有一个package.json
文件准备使用。
注意:如果您不熟悉这些命令,请检查我们的npm初学者指南.
我们将继续安装Hapi.js -本教程选择的框架。它在简单性、稳定性和功能之间提供了很好的平衡,这些功能将很好地适用于我们的用例(尽管还有其他选项也可以很好地工作)。
npm安装@hapi / hapi@18.4.0
这个命令将下载Hapi.js并将其添加到我们的package.json
文件作为依赖项。
注意:我们已经指定了Hapi.js的v18.4.0版本,因为它与Node版本8、10和12兼容。如果使用Node 12,可以选择安装最新版本(Hapi v19.1.0)。
现在我们可以创建我们的入口文件- web服务器将启动一切。继续创建一个server.js
文件在你的应用程序目录,并添加以下代码:
“使用严格的”;常量哈皮神=需要(“@hapi /哈皮神”);常量设置=需要(”。/设置”);常量初始化=异步()= >{常量服务器=新哈皮神.服务器({港口:设置.港口});服务器.路线({方法:“获得”,路径:“/”,处理程序:(请求,h)= >{返回“你好,世界!”;}});等待服务器.开始();控制台.日志(`服务器运行在:$ {服务器.信息.uri}`);};过程.在(“unhandledRejection”,犯错= >{控制台.日志(犯错);过程.退出(1);});初始化();
这将是我们应用程序的基础。
首先,我们表明我们将使用严格模式,这是一个常见的做法当使用hadoop .js框架时。
接下来,我们将包含依赖项并实例化一个新的服务器对象,我们将连接端口设置为该对象3000
(端口可以是任意数字大于1023,小于65535).
我们的服务器的第一个路由将作为一个测试来检查是否一切正常,所以一个“Hello, world!”这句话对我们来说就足够了。在每个路由中,我们必须定义它将响应的HTTP方法和路径(URL),以及处理程序(处理HTTP请求的函数)。handler函数可以接受两个参数:请求
而且h
.第一个包含关于HTTP调用的信息,第二个将为我们提供处理对该调用的响应的方法。
最后,我们使用server.start ()
方法。
存储我们的设置
将配置变量存储在专用文件中是一个很好的实践。这个文件导出了一个包含数据的JSON对象,其中每个键都是从一个环境变量分配的——但没有忘记一个回退值。
在这个文件中,我们还可以根据我们的环境(比如开发环境或生产环境)进行不同的设置。例如,出于开发目的,我们可以在内存中使用SQLite实例,但在生产中使用真正的SQLite数据库文件。
根据当前环境选择设置非常简单。因为我们还有一个env
变量,该变量将包含其中之一发展
或生产
,我们可以像下面这样做来获取数据库设置:
常量dbSettings=设置[设置.env].db;
所以dbSettings
将包含内存中数据库的设置时env
变量是发展
时,或将包含数据库文件的路径env
变量是生产
.
此外,我们还可以添加对.env
文件,我们可以在本地存储环境变量以用于开发目的。这是使用包来完成的dotenvfor Node.js,它将读取一个.env
文件,并自动将找到的值添加到环境中。
注意:如果你决定也用一个.env
文件,确保您安装包NPM安装dotenv
然后添加到.gitignore
所以你不会发布任何敏感信息。
我们的settings.js
文件看起来是这样的:
//这将加载我们的.env文件,并将值添加到process.env,//重要:如果你不想使用这个功能,省略这一行需要(“dotenv”).配置({沉默:真正的});模块.出口={港口:过程.env.港口||3000,env:过程.env.NODE_ENV||“发展”,//环境相关设置发展:{db:{方言:“sqlite”,存储:”:记忆:“}},生产:{db:{方言:“sqlite”,存储:“db / database.sqlite”}}};
现在,我们可以通过执行以下命令并导航到来启动应用程序http://localhost:3000在我们的网页浏览器中:
节点server.js
注意:该项目在Node v12.15.0上进行测试。如果您得到任何错误,请确保您已经更新了安装。
定义路由
路由的定义为我们提供了应用程序所支持的功能的概述。为了创建额外的路由,我们只需要复制已经在我们的server.js
文件,改变每一个内容。
让我们首先创建一个名为自由
在我们的项目中。这里我们将包括所有的JS组件。
内部自由
,让我们创建一个routes.js
文件并添加以下内容:
“使用严格的”;常量路径=需要(“路径”);模块.出口=[//我们将在这里定义我们的路由];
在这个文件中,我们将导出一个对象数组,其中包含应用程序的每个路由。要定义第一条路由,将以下对象添加到数组中:
{方法:“获得”,路径:“/”,处理程序:(请求,h)= >{返回“所有的笔记都会出现在这里”;},配置:{描述:“获取所有可用的笔记”}},
我们的第一个路径是主页(/
),因为它只返回信息,所以我们给它赋值a得到
方法。现在,它只会给我们“所有的注释将出现在这里”的消息,稍后我们将为控制器函数更改该消息。的描述
在配置
节仅用于文档目的。
的下面,为笔记创建四种路径/注意/
路径。因为我们要建立一个CRUD应用程序,我们需要为每个操作提供一个路由HTTP方法.
在前面的路由后面增加如下定义:
{方法:“职位”,路径:“/注意”,处理程序:(请求,h)= >{返回“新笔记”;},配置:{描述:"添加新注释"}},{方法:“获得”,路径:“/注意/{蛞蝓}”,处理程序:(请求,h)= >{返回“这是一张纸条”;},配置:{描述:“获取笔记的内容”}},{方法:“把”,路径:“/注意/{蛞蝓}”,处理程序:(请求,h)= >{返回“编辑笔记”;},配置:{描述:"更新所选笔记"}},{方法:“获得”,路径:“/注意/{蛞蝓}/删除”,处理程序:(请求,h)= >{返回"这张纸条已经不存在了";},配置:{描述:"删除所选笔记"}}
我们所做的与前面的路由定义相同,但这一次我们更改了方法以匹配我们想要执行的操作。
唯一的例外是删除路由。在本例中,我们将用得到
方法而不是删除
再加一个/删除
在路上。这样,我们只需访问相应的URL就可以调用删除操作。
注意:如果计划实现严格的REST接口,则必须使用删除
方法,并删除/删除
路径的一部分。
我们可以用花括号将单词括起来来命名路径中的参数。因为我们要通过音段来识别音符,我们添加{蛞蝓}
的路径除外帖子
路线;我们不需要它,因为我们不会与特定的音符交互,而是创建一个音符。
你可以在官方文档.
现在,我们必须将新路线添加到server.js
文件。让我们在文件的顶部导入路由文件:
常量路线=需要(”。/ lib /路线”);
然后让我们将当前的测试路线替换为以下内容:
服务器.路线(路线);
构建模型
模型允许我们定义数据的结构以及使用它的所有功能。
在这个例子中,我们将使用SQLite数据库Sequelize.js,这将为我们提供一个更好的界面,使用ORM (对象关系映射)技术。它还将为我们提供一个与数据库无关的接口。
建立数据库
SQLite和Sequelize的安装命令如下:
npm安装sequelize sqlite3
现在创建一个模型
目录里面lib /
文件名为index.js
,它将包含数据库和Sequelize.js设置,并包括以下内容:
“使用严格的”;常量Fs=需要(“fs”);常量路径=需要(“路径”);常量Sequelize=需要(“sequelize”);常量设置=需要(“. . / . . /设置”);常量dbSettings=设置[设置.env].db;常量sequelize=新Sequelize(dbSettings.数据库,dbSettings.用户,dbSettings.密码,dbSettings);常量db={};Fs.readdirSync(__dirname).过滤器(文件= >文件.indexOf(“。”)= = !0& &文件= = !“index.js”).forEach(文件= >{常量模型=sequelize.进口(路径.加入(__dirname,文件));db[模型.的名字]=模型;});db.sequelize=sequelize;db.Sequelize=Sequelize;模块.出口=db;
首先,我们将包括将要使用的模块:
Fs
,以读取内部的文件模型
文件夹,它将包含所有的模型路径
,以连接当前目录中每个文件的路径Sequelize
,这将允许我们创建一个新的Sequelize实例设置
,其中包含我们的数据settings.js
项目根目录下的文件
接下来,我们创建一个newsequelize
变量,它将包含Sequelize
实例,使用当前环境的数据库设置。我们要用sequelize
导入所有的模型,并使它们在我们的db
对象。
的db
对象将被导出,并将包含每个模型的数据库方法。当我们需要对数据做一些事情时,它将在我们的应用程序中可用。
加载所有的模型,而不是手动定义它们,我们在模型
目录(除了index.js
文件)并使用进口
函数。返回的对象将为我们提供CRUD方法,然后将其添加到db
对象。
最后,我们加sequelize
而且Sequelize
作为我们的一部分db
对象。第一个将用于我们的server.js
文件在启动服务器之前连接到数据库,如果您在其他文件中也需要它,则包括第二个文件以方便使用。
创建我们的笔记模型
在本节中,我们将使用Moment.js包来帮助设置日期格式。你可以使用以下命令安装它并将其作为依赖项包含:
npm安装时刻
我们要创建一个note.js
文件内的模型
目录,这将是我们应用程序中唯一的模型。它将提供我们所需要的所有功能。
将以下内容添加到该文件中:
“使用严格的”;常量时刻=需要(“时刻”);模块.出口=(sequelize,数据类型)= >{常量请注意=sequelize.定义(“注意”,{日期:{类型:数据类型.日期,得到:函数(){返回时刻(这.getDataValue(“日期”)).格式(“MMMM Do, YYYY”);}},标题:数据类型.字符串,鼻涕虫:数据类型.字符串,描述:数据类型.字符串,内容:数据类型.字符串});返回请注意;};
导出一个函数,该函数接受sequelize
实例,以定义模型,以及数据类型
对象中包含数据库中所有可用类型。
接下来,我们使用一个对象定义数据的结构,其中每个键对应于一个数据库列,键的值定义了我们要存储的数据类型。类型中的数据类型列表Sequelize.js文档.数据库中的表将根据这些信息自动创建。
对于date列,我们还定义了Sequelize应该如何返回值getter函数(得到
密钥)。我们在返回信息之前指明这一点。它应该首先通过Moment实用程序以更易于阅读的方式进行格式化(MMMM Do, YYYY
).
注意:虽然我们得到的是一个简单易读的日期字符串,但它被存储为JavaScript的精确日期字符串产品日期对象。所以这不是一个破坏性的操作。
最后,返回我们的模型。
同步数据库
现在,在应用程序中使用数据库之前,我们必须同步数据库。在server.js
,导入文件顶部的模型:
//导入models目录下的index.js文件常量模型=需要(”。/ lib /模型/”);
接下来,删除以下代码块:
等待服务器.开始();控制台.日志(`服务器运行在:$ {服务器.信息.uri}`);
把它替换成这个:
等待模型.sequelize.同步();等待服务器.开始();控制台.日志(`服务器运行在:$ {服务器.信息.uri}`);
这段代码将使模型与我们的数据库同步。完成此操作后,服务器将启动。
构建控制器
控制器是接受请求而且响应工具包对象。的请求
对象包含有关所请求资源的信息,我们使用回复
向客户端返回信息。
在我们的应用程序中,我们现在只返回一个JSON对象,但是我们将在构建视图后添加视图。
我们可以把控制器看作是连接模型和视图的函数;它们将与我们的模型通信以获取数据,然后在视图中返回该数据。
家庭控制器
我们将要构建的第一个控制器将处理站点的主页。创建一个home.js
文件在lib /控制器
目录,内容如下:
“使用严格的”;常量模型=需要(“. . /模型/”);模块.出口=异步(请求,h)= >{常量结果=等待模型.请注意.findAll({订单:[[“日期”,“DESC”]]});返回{数据:{笔记:结果},页面:“Home -便签板”,描述:“欢迎来到我的笔记板”};};
方法获取数据库中的所有音符findAll
我们模型的方法。这个函数将返回一个Promise,如果它解决了,我们将得到一个包含数据库中所有notes的数组。
方法将结果按降序排列订单
对象中的参数findAll
方法,因此最后一项将首先出现。中的所有可用选项Sequelize.js文档.
一旦我们有了家庭控制器,我们可以编辑我们的routes.js
文件。首先,我们在文件顶部导入模块,位于路径
模块导入:
常量首页=需要(”。/控制器/家”);
然后我们将刚刚创建的控制器添加到数组中:
{方法:“获得”,路径:“/”,处理程序:首页,配置:{描述:“获取所有可用的笔记”}},
您可以通过重新启动服务器(节点server.js
)及参观http://localhost:3000/.您应该看到以下响应:
{“数据”:{“笔记”:[]},“页面”:“Home -便签板”,“描述”:“欢迎来到我的笔记板”}
注释控制器的样板
因为我们要用一个蛞蝓来识别我们的音符,所以我们可以使用音符的标题和鼻涕虫库,所以让我们安装它,并使用以下命令将其作为依赖项包含:
npm安装鼻涕虫
我们必须在应用程序中定义的最后一个控制器将允许我们创建、读取、更新和删除注释。
我们可以继续创建note.js
文件内的lib /控制器
目录并添加以下内容:
“使用严格的”;常量{请注意}=需要(“. . /模型/”);常量Slugify=需要(“鼻涕虫”);常量路径=需要(“路径”);模块.出口={//这里我们将在routes.js文件中包含处理剩余请求的函数。};
的创建
函数
为了向数据库中添加注释,我们将编写一个创建
函数将会包装创建
方法中使用有效负载对象中包含的数据。
在导出的对象中添加以下内容:
创建:异步(请求,h)= >{常量结果=等待请注意.创建({日期:新日期(),标题:请求.有效载荷.noteTitle,鼻涕虫:Slugify(请求.有效载荷.noteTitle,{较低的:真正的}),描述:请求.有效载荷.noteDescription,内容:请求.有效载荷.noteContent});//生成一个带有'result'数据的新记录返回结果;},
创建了注释之后,我们将返回注释数据并将其作为JSON发送给客户端回复
函数。
现在,我们只返回结果,但是一旦我们在下一节中构建了视图,我们就能够生成带有新注释的HTML,并在客户机上动态添加它。虽然这并不是完全必要的,并且取决于您如何处理前端逻辑,但我们将返回一个HTML块来简化客户机上的逻辑。
另外,请注意,日期是在执行函数时动态生成的,使用新的日期()
.
的读
函数
要只搜索一个元素,可以使用findOne
方法。既然我们通过音符的弦段来识别音符,那么在哪里
过滤器必须在URL中包含客户端提供的段码(http://localhost: 3000 /注意:鼻涕虫:
):
读:异步(请求,h)= >{常量请注意=等待请注意.findOne({在哪里:{鼻涕虫:请求.参数个数.鼻涕虫}});返回请注意;},
与前面的函数一样,我们只返回结果,它将是一个包含note信息的对象。中构建视图后,视图将被使用构建视图部分。
的更新
函数
要更新注释,可以使用更新
方法。它有两个对象——我们要替换的新值,以及包含a的选项在哪里
使用注释段进行过滤,这是我们要更新的注释:
更新:异步(请求,h)= >{常量值={标题:请求.有效载荷.noteTitle,描述:请求.有效载荷.noteDescription,内容:请求.有效载荷.noteContent};常量选项={在哪里:{鼻涕虫:请求.参数个数.鼻涕虫}};等待请注意.更新(值,选项);常量结果=等待请注意.findOne(选项);返回结果;},
在更新数据之后,由于数据库不会返回更新后的笔记,所以我们可以再次找到修改后的笔记并将其返回给客户端,这样我们就可以在更改完成后立即显示更新后的版本。
的删除
函数
删除控制器将通过向摧毁
我们模型的函数。然后,一旦注释被删除,我们将重定向到主页。为了实现这一点,我们使用重定向Hapi响应工具包的功能:
删除:异步(请求,h)= >{等待请注意.摧毁({在哪里:{鼻涕虫:请求.参数个数.鼻涕虫}});返回h.重定向(“/”);}
在我们的路由中使用Note控制器
此时,我们应该准备好带有所有CRUD操作的笔记控制器文件。但是要使用它们,我们必须将它包含在我们的路由文件中。
的顶部导入控制器routes.js
文件:
常量请注意=需要(”。/控制器/注意”);
我们必须用我们的新函数替换每个处理程序,所以我们应该有我们的路由文件如下:
{方法:“职位”,路径:“/注意”,处理程序:请注意.创建,配置:{描述:"添加新注释",有效载荷:{多部分:真正的,}}},{方法:“获得”,路径:“/注意/{蛞蝓}”,处理程序:请注意.读,配置:{描述:“获取笔记的内容”}},{方法:“把”,路径:“/注意/{蛞蝓}”,处理程序:请注意.更新,配置:{描述:"更新所选笔记",有效载荷:{多部分:真正的,}}},{方法:“获得”,路径:“/注意/{蛞蝓}/删除”,处理程序:请注意.删除,配置:{描述:"删除所选笔记"}}
注意:我们包括我们的函数没有()
最后,因为我们引用了函数而没有调用它们。
在Hapi v19中,request.payload.multipart
改为假
默认情况下.我们得把它调回真正的
为帖子
而且把
路径,我们会用到FormData
对象将数据传输到服务器,并且传输的数据将在多部分/格式
格式。
构建视图
此时,我们的站点正在接收HTTP调用并响应JSON对象。为了使它对每个人都有用,我们必须创建以良好的方式呈现信息的页面。
在这个例子中,我们将使用哈巴狗(以前是Jade)模板语言,尽管这不是强制性的,我们可以使用其他语言的Hapi.js。我们要用愿景插件在我们的服务器中启用视图功能。
注意:如果你不熟悉Jade/Pug,请参阅我们的哈巴狗初学者指南.
可以使用以下命令安装软件包:
npm安装@hapi / vision@5.5.4哈巴狗
这里我们正在安装v5.5.4版本的vision插件,它与Hapi v18兼容。如果您选择安装Hapi v19,您可以简单地输入NPM I @hapi/vision
获取最新版本。
Note组件
首先,我们将构建note组件,它将在视图之间重用。此外,我们将在一些控制器函数中使用该组件在后端动态地构建一个注释,以简化客户机上的逻辑。
在lib /视图/组件
被称为note.pug
内容如下:
文章.contenth2.title:一个(href=`/注意/$ {请注意.鼻涕虫}`)=请注意.标题p.subtitle.is-6发布于#{note.date}p=请注意.内容
它由笔记的标题、出版日期和笔记的内容组成。
基本布局
基本布局包含页面的常见元素——换句话说,对于我们的例子来说,包含所有非内容的元素。在lib /视图/
被称为layout.pug
内容如下:
doctype html头元(字符集=“utf - 8”)元(的名字=“视口”内容=的宽度=设备-宽度,初始=1')标题=页面元(的名字=“描述”内容=描述)链接(rel=“样式表”href=“https://cdn.jsdelivr.net/npm/bulma@0.8.0 / css / bulma.min.css”)脚本(推迟=”src=“https://use.fontawesome.com/releases/v5.3.1/js/all.js”)身体块的内容脚本(src=“/脚本/ main.js”)
其他页面的内容将被加载在块的内容
.方法中显示一个页面变量标题
元素,一个描述
中的变量元(name =“描述”)
元素。稍后我们将在路由中创建这些变量。
出于样式的目的,我们包括Bulma CSS框架而且字体太棒了来自CDN。我们还包括amain.js
该文件将包含所有用于前端的自定义JavaScript代码。请现在创建该文件静态/公共/脚本/
目录中。
Home View
在我们的主页上,我们将显示数据库中所有音符的列表和一个按钮,该按钮将显示一个带有允许我们通过Ajax创建新音符的表单的模态窗口。
在lib /观点
被称为home.pug
内容如下:
延伸布局块的内容部分.section.containerh1.title.has-text-centered|笔记板.tabs.is-centeredul李一个.show-modal(href=“#”)发布主要(容器).notes-list每一个请注意在数据.笔记包括组件/注意人力资源.modal.modal-background.modal-card头.modal-card-headp.modal-card-title添加注按钮delete(aria-label=“关闭”)部分.modal-card-body形式(行动=/注意的方法=“职位”).note-form# note-form.field范式输入.input(的名字=“noteTitle”类型=“文本”占位符=“标题”).field范式输入.input(的名字=“noteDescription”类型=“文本”占位符=的简短描述).field范式文本区域.textarea(的名字=“noteContent”占位符=“内容”).field范式按钮.button.is-link保存
视图
笔记页面与主页非常相似,但在本例中,我们显示了一个菜单,其中包含特定于当前笔记的选项、笔记的内容以及与主页相同的表单,但是已经填充了当前笔记信息,因此当我们更新它时它就在那里。
在lib /观点
被称为note.pug
内容如下:
延伸布局块的内容部分.section.containerh1.title.has-text-centered|笔记板.tabs.is-centeredul李:一个(href=' / ')首页李:一个.show-modal(href=“#”)更新李:一个(href=`/注意/$ {请注意.鼻涕虫}/删除`)删除包括组件/注意.modal.modal-background.modal-card头.modal-card-headp.modal-card-title编辑注意按钮delete(aria-label=“关闭”)部分.modal-card-body形式(行动=`/注意/$ {请注意.鼻涕虫}`方法=“把”).note-form# note-form.field范式输入.input(的名字=“noteTitle”类型=“文本”占位符=“标题”价值=请注意.标题).field范式输入.input(的名字=“noteDescription”类型=“文本”占位符=的简短描述价值=请注意.描述).field范式文本区域.textarea(的名字=“noteContent”占位符=“内容”)# {note.content}.field范式按钮.button.is-link保存
客户端的JavaScript
为了创建和更新笔记,我们将使用一些JavaScript,既可以显示/隐藏带有表单的模态,也可以通过Ajax提交请求。虽然这不是必须的,但我们认为它为用户提供了更好的体验。
这就是我们的内容main.js
在静态/公共/脚本/
目录:
/ /模态常量模态=文档.querySelector(“.modal”);常量超文本标记语言=文档.querySelector(“html”);常量showModal=()= >{模态.班级名册.添加(“活跃”);超文本标记语言.班级名册.添加(“夹”);};常量hideModal=()= >{模态.班级名册.删除(“活跃”);超文本标记语言.班级名册.删除(“夹”);};文档.querySelector(“a.show-modal”).addEventListener(“点击”,函数(e){e.preventDefault();showModal();});模态.querySelector(".模态delete).addEventListener(“点击”,函数(e){e.preventDefault();hideModal();});//表单提交常量形式=文档.querySelector(“# note-form”);常量url=形式.getAttribute(“行动”);常量方法=形式.getAttribute(“方法”);常量prependNote=超文本标记语言= >{常量notesList=文档.querySelector(“.notes-list”);常量div=文档.createElement(" div ");div.innerHTML=超文本标记语言;notesList.方法(div.写上。,notesList.写上。);};常量updateNote=超文本标记语言= >{常量文章=文档.querySelector(“文章”);常量div=文档.createElement(" div ");div.innerHTML=超文本标记语言;文章.parentNode.方法的(div.写上。,文章);};常量调用onSuccess=超文本标记语言= >{hideModal();形式.重置();如果(方法= = =“职位”){prependNote(超文本标记语言);}其他的如果(方法= = =“把”){updateNote(超文本标记语言);}};形式.addEventListener(“提交”,e= >{e.preventDefault();获取(url,{方法,身体:新FormData(形式)}).然后(响应= >响应.文本()).然后(文本= >调用onSuccess(文本)).抓(错误= >控制台.错误(错误));});
每次用户在模式窗口中提交表单时,我们都会从表单元素中获取信息并将其发送到后端,这取决于操作URL和方法(帖子
或把
).然后,我们将以包含新注释数据的HTML块的形式获得结果。当我们添加一个笔记时,我们只会将它添加到主页列表的顶部,当我们更新一个笔记时,我们会在笔记视图中替换新笔记的内容。
添加对服务器上视图的支持
为了使用视图,我们必须将它们包含在控制器中,并添加所需的设置。
在我们的server.js
文件,让我们导入节点路径在文件顶部的实用程序,因为我们在代码中使用它来指示视图的路径:
常量路径=需要(“路径”);
现在,替换server.route(路线);
与下面的代码块对齐:
等待服务器.注册([需要(“@hapi /愿景”)]);服务器.的观点({引擎:{哈巴狗:需要(“哈巴狗”)},路径:路径.加入(__dirname,“lib /视图”),compileOptions:{漂亮的:假},isCached:设置.env= = =“生产”});//添加路由服务器.路线(路线);
在我们添加的代码中,我们首先注册愿景插件和我们的Hapi.js服务器,它将提供视图功能。然后我们为我们的视图添加设置——比如我们将要使用的引擎和视图所在的路径。在代码块的末尾,我们重新添加我们的路由。
这将使我们的视图在服务器上工作,但我们仍然必须声明我们将用于每个路由的视图。
设置主视图
打开lib /控制器/ home.js
文件并替换返回
声明如下:
返回h.视图(“回家”,{数据:{笔记:结果},页面:“家-便签板”,描述:“欢迎来到我的笔记板”});
在注册Vision插件之后,我们现在有了一个视图
方法在应答对象上可用。我们将用它来选择首页
查看我们的的观点
目录,并发送将在呈现视图时使用的数据。
在提供给视图的数据中,我们还包括页面标题和搜索引擎的元描述。
如果你想在这个时候尝试一些东西,请转到http://localhost:3000/.你会看到一个漂亮的便签板,上面有一个发布不做任何事情的按钮。
设置备注视图:创建
函数
现在,每当我们创建一个笔记时,我们都会从服务器向客户端发送一个JSON对象。但是,由于我们使用Ajax处理这个过程,所以可以将新注释作为HTML发送,以便添加到页面中。要做到这一点,我们渲染请注意组件与我们拥有的数据。
开始要求帕格在顶部控制器/ note.js
文件:
常量哈巴狗=需要(“哈巴狗”);
然后,在创建
方法,替换行返回结果;
使用以下代码块:
//生成一个带有'result'数据的新记录返回哈巴狗.renderFile(路径.加入(__dirname,“. . /视图/组件/ note.pug”),{请注意:结果});
我们使用renderFile
方法使用我们刚从模型中接收到的数据来渲染注释模板。
设置备注视图:读
函数
当我们进入一个笔记页面时,我们应该得到带有笔记内容的笔记模板。要做到这一点,我们必须替换读
函数的返回注意;
这样行:
返回h.视图(“注意”,{请注意,页面:`$ {请注意.标题}-笔记板`,描述:请注意.描述});
与主页一样,我们选择一个视图作为第一个参数,而将使用的数据作为第二个参数。
设置备注视图:更新
函数
每当我们更新一个笔记时,我们将类似于创建新笔记时的回复。取代返回结果;
行更新
使用以下代码执行函数:
//生成带有更新数据的新记录返回哈巴狗.renderFile(路径.加入(__dirname,“. . /视图/组件/ note.pug”),{请注意:结果});
注意:删除功能不需要视图,因为一旦删除了注释,它就会重定向到主页。
静态文件服务
我们在客户端使用的JavaScript和CSS文件是由Hapi.js从静态/公共/
目录中。但这不会自动发生;我们必须向服务器表明我们想要将这个文件夹定义为公共文件夹。这是使用惰性包,您可以使用以下命令安装:
npm安装@hapi /惰性
在server.register
函数。server.js
导入惰性插件,并像这样注册到Hapi:
等待服务器.注册([需要(“@hapi /愿景”),需要(“@hapi /惰性”)]);
现在我们必须定义提供静态文件的路由,以及它们在服务器文件系统上的位置。中导出对象的末尾添加以下条目routes.js
:
{//静态文件方法:“获得”,路径:“/{参数*}”,处理程序:{目录:{路径:路径.加入(__dirname,“. . /静态/公众”)}},配置:{描述:“提供静态资源”}}
此路线将使用得到
方法,我们用一个包含要公开的目录的对象替换了处理程序函数。
中可以找到有关提供静态内容的更多信息Hapi.js文档.
结论
至此,我们有了一个使用MVC架构的非常基本的Hapi.js应用程序。尽管在将应用程序投入生产环境之前还需要注意一些事情(例如输入验证、错误处理、错误页面等等),但这应该是学习和构建自己的应用程序的基础。
如果您想进一步了解这个示例,在完成所有小细节(与体系结构无关)以使其成为健壮的应用程序之后,您可以实现一个身份验证系统,以便只有注册用户能够发布和编辑注释。但你的想象力是有限的,所以请尽情享用应用程序库去镇上吧!
深入Node.js,进一步阅读: