用sail .js构建一个实时聊天应用
如果你是一个目前使用Django、Laravel或Rails等框架的开发人员,你可能听说过Node.js。你可能已经在你的项目中使用了一个流行的前端库,比如Angular或React。到目前为止,您应该考虑完全切换到基于Node.js的服务器技术。年代trong>
然而,最大的问题是从哪里开始。今天,JavaScript世界在过去几年中以令人难以置信的速度发展,而且似乎还在不断扩大。
如果您害怕失去在Node领域辛苦积累的编程经验,不要害怕,就像我们一样<一个href="https://sailsjs.com/">Sails.js一个>.
js是一个实时MVC框架,旨在帮助开发人员在短时间内构建生产就绪的企业级Node.js应用程序。js是一个纯JavaScript解决方案,支持多个数据库(同时)和多种前端技术。如果您是一名Rails开发人员,您将很高兴了解到这一点<一个href="https://twitter.com/mikermcneil">麦克。麦克内尔一个>js的创始人,是受到Rails的启发。你会发现Rails和sail .js项目之间有很多相似之处。
在本文中,我将通过向您展示如何构建一个简单的、用户友好的聊天应用程序,教你sail .js的基础知识。sailschat项目的完整源代码可以在这里找到<一个href="https://github.com/brandiqa/sp-sails-chat">GitHub回购一个>.
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665711301-sails-chat-app.png" alt="风帆聊天应用"loading="lazy">
先决条件
在开始之前,您至少需要有使用MVC架构开发应用程序的经验。本教程适用于中级开发人员。你还需要至少在以下方面有一个基本的基础:
为了使它对每个人都实用和公平,本教程将使用默认安装在新sail .js项目中的核心库。这里不涉及与React、Vue或Angular等现代前端库的集成。但是,我强烈建议您在本文之后研究一下它们。此外,我们不会进行数据库集成。相反,我们将使用默认的、本地磁盘的、基于文件的数据库进行开发和测试。
项目计划
本教程的目标是向您展示如何构建类似于<一个href="https://slack.com/">松弛一个>,<一个href="https://gitter.im/">git一个>或<一个href="https://discordapp.com/">必威体育手机版登录.
不是真的!我们投入了大量的时间和汗水去创造这些出色的平台。目前开发的功能数量相当庞大。
相反,我们将构建一个聊天应用程序的最小可行产品版本,它包括:
- 单人聊天室
- 基本身份验证(<一个href="//www.shaoxingby.com/what-is-passwordless-authentication/">无密码一个>)
- 配置文件更新。
我添加了概要文件特性作为额外的奖励,以涵盖更多关于sail .js特性的内容。
安装Sails.js
在开始安装sail .js之前,我们首先需要设置一个合适的Node.js环境。在撰写本文时,目前可用的最新稳定版本是v0.12.14。js v1.0.0也可用,但目前处于测试阶段,不建议用于生产环境。
我能访问的Node的最新稳定版本是v8.9.4。不幸的是,Sails.js v0.12不能与当前最新的LTS正常工作。但是,我已经用Node v.7.10进行了测试,发现一切都很顺利。这仍然很好,因为我们可以在代码中使用一些新的ES8语法。
作为JavaScript开发人员,您会意识到只使用一个版本的Node.js是不够的。因此,我建议使用<一个href="//www.shaoxingby.com/quick-tip-multiple-versions-node-nvm/">nvm工具一个>轻松管理多个版本的Node.js和NPM。如果你还没有这样做,只要清除你现有的Node.js安装,然后安装nvm来帮助你管理多个版本的Node.js。
下面是安装Node v7和Sails.js的基本说明:
#安装最新版本的Node v7 LTS年代p一个n>nvm<年代p一个n class="token function">安装年代p一个n>v7<年代p一个n class="token comment">#将节点v7设置为默认值年代p一个n>nvm违约<年代p一个n class="token builtin class-name">别名年代p一个n>v7<年代p一个n class="token comment">#安装Sails.js Global年代p一个n>npm年代p一个n>安装年代p一个n>- g帆
如果你有一个良好的网络连接,这应该只需要几分钟或更少的时间。现在让我们继续使用Sails generator命令创建我们的新应用程序:
#进入项目文件夹年代p一个n>cd年代p一个n>项目<年代p一个n class="token comment">#生成新应用年代p一个n>风帆生成新的聊天应用程序<年代p一个n class="token comment">#等待安装完成,然后导航到项目文件夹年代p一个n>cd年代p一个n>聊天软件<年代p一个n class="token comment">#启动应用程序年代p一个n>帆抬起
应用程序启动需要几秒钟的时间。您需要手动打开该urlhttp://localhost:1337
在浏览器中查看新创建的web应用程序。
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665711902-new-sails-app.png" alt="帆抬起"loading="lazy">
看到这一点,我们确认了我们有一个运行中的项目,没有错误,我们可以开始工作了。要停止项目,只需按一下控制+c在总站。使用您最喜欢的代码编辑器(我使用Atom)检查生成的项目结构。以下是你应该注意的主要文件夹:
api
:控制器、模型、服务和策略(权限)资产
:图片,字体,JS, CSS, Less, Sass等配置
:项目配置,例如数据库,路由,凭据,地区,安全等。node_modules
:已安装的NPM包任务
:用于编译和注入资产的Grunt配置脚本和管道脚本的观点
:查看页面-例如,EJS, Jade或任何你喜欢的模板引擎.tmp
:在开发模式下,由Sails用于构建和服务项目的临时文件夹。
在我们继续之前,我们需要做几件事:
- 更新EJS包年代trong>.中列出的EJS 2.3.4
package.json
,您需要立即将其更改为2.5.5进行更新。它包含一个严重的安全漏洞。在更改版本号之后,执行npm install来执行更新。 - 热重载年代trong>.我建议你安装<一个href="https://github.com/sgress454/sails-hook-autoreload">sails-hook-autoreload一个>这不是一个完美的解决方案,但会使开发更容易。要为当前版本的sail .js安装它,请执行以下命令:
npm年代p一个n>安装年代p一个n>帆-钩autoreload@for帆- 0.12——保存
安装前端依赖项
在本教程中,我们将花费尽可能少的时间构建UI。任何你熟悉的CSS框架都可以。在本教程中,我将使用<一个href="https://semantic-ui.com/">语义界面一个>CSS图书馆。
js没有关于如何安装CSS库的具体指南。有三种或更多的方法可以做到这一点。让我们分别看看。
你可以自己下载CSS文件和JS脚本,以及它们的依赖关系。下载后,将文件放在资产
文件夹中。
我不喜欢用这种方法,
因为它需要手动工作来保持文件更新。我喜欢自动完成任务。
2.使用鲍尔
此方法要求您创建一个名为.bowerrc
在你的项目的根源。粘贴以下代码片段:
{年代p一个n>“目录”年代p一个n>:年代p一个n>“资产/供应商”年代p一个n>}年代p一个n>
这将指示鲍尔安装到资产/供应商
文件夹,而不是默认的bower_components
文件夹中。接下来,全局安装Bower,并在本地使用Bower安装你的前端依赖项:
#通过npm-全局安装bower年代p一个n>npm年代p一个n>安装年代p一个n>- g鲍尔<年代p一个n class="token comment">#创建凉亭。Json文件,接受默认答案(除了选择y private)年代p一个n>鲍尔init<年代p一个n class="token comment">#通过bower安装semantic-ui年代p一个n>鲍尔<年代p一个n class="token function">安装年代p一个n>语义用户界面——保存<年代p一个n class="token comment">#安装jsrender年代p一个n>鲍尔<年代p一个n class="token function">安装年代p一个n>jsrender——保存
我会解释的目的jsrender
以后。我认为最好一次性完成安装依赖项的任务。您应该注意,jQuery也已安装,因为它是语义用户界面
.
安装后,更新资产/风格/ importer.less
要包括这一行:
@ import年代p一个n>“. . /供应商/语义/ dist / semantic.css '年代p一个n>;年代p一个n>
中包含JavaScript依赖项任务/ pipeline.js
:
var年代p一个n>jsFilesToInject<年代p一个n class="token operator">=年代p一个n>[年代p一个n>//加载帆。我o before everything else年代p一个n>“js /依赖/ sails.io.js”年代p一个n>,年代p一个n>//供应商依赖关系年代p一个n>“供应商/ jquery / dist / jquery.js”年代p一个n>,年代p一个n>“供应商/语义/ dist / semantic.js”年代p一个n>,年代p一个n>“供应商/ jsrender / jsrender.js”年代p一个n>,年代p一个n>// jQuery或Angular等依赖项被引入这里年代p一个n>“js /依赖/ * * / * . js”年代p一个n>,年代p一个n>//所有客户端JS文件年代p一个n>//将在这里注入,没有特定的顺序。年代p一个n>“js / * * / * . js”年代p一个n>]年代p一个n>;年代p一个n>
当我们奔跑帆抬起
,则会自动注入JavaScript文件视图/ layout.ejs
按pipeline.js
指令。当前的咕哝着说
setup将负责为我们注入CSS依赖项。
重要的是:加上这个词供应商
在.gitignore
文件。我们不希望在存储库中保存供应商依赖关系。
3.使用npm + grunt.copy
第三种方法需要花费更多的精力来设置,但是会导致占用更少的空间。使用npm安装依赖项,如下所示:
npm年代p一个n>安装年代p一个n>Semantic-ui-css jsrender——保存
jQuery将自动安装,因为它也被列为依赖项semantic-ui-css
.接下来我们需要放入代码任务/ config / copy.js
.这段代码将指示Grunt复制所需的JS和CSS文件node_modules
到资产/供应商
文件夹给我们。整个文件应该是这样的:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>函数年代p一个n>(年代p一个n>咕哝着说年代p一个n>)年代p一个n>{年代p一个n>咕哝着说<年代p一个n class="token punctuation">.年代p一个n>配置年代p一个n>.年代p一个n>集年代p一个n>(年代p一个n>“复制”年代p一个n>,年代p一个n>{年代p一个n>dev<年代p一个n class="token operator">:年代p一个n>{年代p一个n>文件<年代p一个n class="token operator">:年代p一个n>[年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>”。/资产年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“* * / * !(咖啡|更少)年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>“.tmp /公共”年代p一个n>}年代p一个n>,年代p一个n>/ /复制JQuery年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>“。/ node_modules / jquery / dist /”年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“jquery.min.js”年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>”。/资产/供应商/ jquery”年代p一个n>}年代p一个n>,年代p一个n>/ /复制jsrender年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>“。/ node_modules / jsrender /”年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“jsrender.js”年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>”。/资产/供应商/ jsrender”年代p一个n>}年代p一个n>,年代p一个n>//复制semantic-ui CSS和JS文件年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>“。/ node_modules / semantic-ui-css /”年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“semantic.css”年代p一个n>,年代p一个n>“semantic.js”年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>”。/资产/供应商/语义用户界面的年代p一个n>}年代p一个n>,年代p一个n>//复制语义ui图标字体年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>”。/ node_modules / semantic-ui-css /主题”年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“* *”。年代p一个n>,年代p一个n>“* * / * *”。年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>”。/资产/供应商/语义用户界面/主题年代p一个n>}年代p一个n>]年代p一个n>}年代p一个n>,年代p一个n>构建<年代p一个n class="token operator">:年代p一个n>{年代p一个n>文件<年代p一个n class="token operator">:年代p一个n>[年代p一个n>{年代p一个n>扩大<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>慢性消耗病<年代p一个n class="token operator">:年代p一个n>“.tmp /公共”年代p一个n>,年代p一个n>src<年代p一个n class="token operator">:年代p一个n>[年代p一个n>“* * / *”年代p一个n>]年代p一个n>,年代p一个n>桌子<年代p一个n class="token operator">:年代p一个n>“www”年代p一个n>}年代p一个n>]年代p一个n>}年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>咕哝着说<年代p一个n class="token punctuation">.年代p一个n>loadNpmTasks年代p一个n>(年代p一个n>“grunt-contrib-copy”年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>;年代p一个n>
将这一行添加到资产/风格/ importer.less
:
@ import年代p一个n>“. . /供应商/语义用户界面/ semantic.css”年代p一个n>;年代p一个n>
将JS文件添加到配置/ pipeline.js
:
//供应商依赖关系年代p一个n>“供应商/ jquery / jquery.min.js”年代p一个n>,年代p一个n>“供应商/语义用户界面/ semantic.js”年代p一个n>,年代p一个n>“供应商/ jsrender / jsrender.js”年代p一个n>,年代p一个n>
最后,执行此命令将文件从node_modules
的资产/供应商
文件夹中。你只需要为你的项目的每次干净安装做一次:
繁重副本:开发
记得加上供应商
到你的.gitignore
.
测试依赖项安装
无论选择哪种方法,都需要确保加载了所需的依赖项。为此,将代码替换为视图/ homepage.ejs
与以下:
<年代p一个n>h2年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI图标头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>我年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>设置图标<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>我年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>内容<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>帐户设置<年代p一个n class="token tag"><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>子头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>管理您的帐户设置和设置电子邮件首选项。<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>h2年代p一个n>>年代p一个n>
保存文件后,执行a帆抬起
.你的主页现在看起来应该是这样的:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665712403-semantic-ui-test.png" alt="semantic-ui-test"loading="lazy">
在重新启动应用程序后一定要刷新。如果图标缺失或字体看起来不正常,请仔细检查步骤,看看你错过了什么。使用浏览器的控制台查看哪些文件没有加载。否则,继续进行下一阶段。
创建视图
当涉及到项目开发时,我喜欢从用户界面开始。我们将使用<一个href="http://www.embeddedjs.com/getting_started.html">嵌入式JavaScript模板一个>创建视图。它是一个模板引擎,默认安装在每个sail .js项目中。但是,您应该意识到它的功能有限,并且已不再处于开发阶段。
开放配置/ bootstrap.js
并插入这一行,以便给我们的网页一个适当的标题。函数之前的现有函数中cb ()
声明:
帆<年代p一个n class="token punctuation">.年代p一个n>配置年代p一个n>.年代p一个n>浏览器名称年代p一个n>=年代p一个n>“风帆聊天应用”年代p一个n>;年代p一个n>
你可以看一下视图/ layout.ejs
来看看标题
标签设置完成。接下来,我们开始构建主页UI。
首页设计
开放/视图/ homepage.ejs
并将现有代码替换为:
<年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>横幅<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI段蓝绿色倒置<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>h1年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI中心对齐图标头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>我年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>聊天图标<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>我年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>内容<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>一个年代p一个n>href年代p一个n>=年代p一个n>"年代p一个n>/<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>帆聊天<年代p一个n class="token tag">年代p一个n>一个年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>子头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>与社区讨论您最喜欢的技术!<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>h1年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>部分<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI三列网格<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>列<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>列<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI中心填充紧凑凸起段<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>h3年代p一个n>>年代p一个n>注册或登录<年代p一个n class="token tag">年代p一个n>h3年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui分配器<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>[待办事项:登入表格在此]<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>列<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
要理解上面代码中使用的UI元素,请参考语义UI文档。我在下面列出了具体的链接:
中创建一个新文件资产/风格/ theme.less
并粘贴如下内容:
.banner年代p一个n>一个年代p一个n>{年代p一个n>颜色年代p一个n>:年代p一个n># fff年代p一个n>;年代p一个n>}年代p一个n>.centered年代p一个n>{年代p一个n>margin-left年代p一个n>:年代p一个n>汽车<年代p一个n class="token important">重要的!年代p一个n>;年代p一个n>margin-right年代p一个n>:年代p一个n>汽车<年代p一个n class="token important">重要的!年代p一个n>;年代p一个n>margin-bottom年代p一个n>:年代p一个n>30.年代p一个n>px年代p一个n>重要的!年代p一个n>;年代p一个n>}年代p一个n>.section年代p一个n>{年代p一个n>margin-top年代p一个n>:年代p一个n>30.年代p一个n>px年代p一个n>;年代p一个n>}年代p一个n>.menu年代p一个n>{年代p一个n>这个特性年代p一个n>:年代p一个n>0年代p一个n>重要的!年代p一个n>;年代p一个n>}年代p一个n>.note年代p一个n>{年代p一个n>字体大小年代p一个n>:年代p一个n>11年代p一个n>px年代p一个n>;年代p一个n>颜色年代p一个n>:年代p一个n># 2185 d0年代p一个n>;年代p一个n>}年代p一个n>#聊天的年代p一个n>{年代p一个n>高度年代p一个n>:年代p一个n>90年代p一个n>%年代p一个n>;年代p一个n>overflow-y年代p一个n>:年代p一个n>滚动<年代p一个n class="token punctuation">;年代p一个n>}年代p一个n>
这些都是我们将在项目中使用的自定义样式。其余的样式将来自语义界面
图书馆。
接下来,更新资产/风格/ importer.less
要包含我们刚刚创建的主题文件:
@ import年代p一个n>“theme.less”年代p一个n>;年代p一个n>
执行帆抬起
.你的项目现在看起来应该是这样的:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665712804-homepage.png" alt="首页视图"loading="lazy">
接下来,我们将讨论如何构建导航菜单。
导航菜单
这将被创建为一个部分,因为它将由多个视图文件共享。在的观点
文件夹,创建一个名为分音
.然后创建文件视图/泛音/ menu.ejs
并粘贴以下代码:
<年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI标签图标倒蓝绿色菜单<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>一个年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>项<年代p一个n class="token punctuation">"年代p一个n>href年代p一个n>=年代p一个n>"年代p一个n>/聊天<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>我年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>聊天图标<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>我年代p一个n>>年代p一个n>聊天室<年代p一个n class="token tag">年代p一个n>一个年代p一个n>>年代p一个n><年代p一个n>一个年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>项<年代p一个n class="token punctuation">"年代p一个n>href年代p一个n>=年代p一个n>"年代p一个n>/配置文件<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>我年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>用户图标<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>我年代p一个n>>年代p一个n>配置文件<年代p一个n class="token tag">年代p一个n>一个年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>正确的菜单<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>一个年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>项<年代p一个n class="token punctuation">"年代p一个n>href年代p一个n>=年代p一个n>"年代p一个n>/认证/注销<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>我年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>退出图标<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>我年代p一个n>>年代p一个n>注销<年代p一个n class="token tag">年代p一个n>一个年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
要理解上面的代码,只需参考<一个href="https://semantic-ui.com/collections/menu.html">菜单一个>文档。
如果检查上面的代码,您会注意到我们创建了一个链接/聊天
,/配置文件
而且/认证/注销
.让我们首先创建视图配置文件
而且聊天室
.
配置文件
创建文件视图/ profile.ejs
并粘贴以下代码:
<% include部分/菜单%><年代p一个n class="token tag"><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui容器<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>h1年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>以UI为中心的头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>配置文件更新!<年代p一个n class="token tag">年代p一个n>h1年代p一个n>>年代p一个n><年代p一个n>人力资源年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>部分<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>[TODO把用户表单放在这里]<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
现在你应该已经熟悉了头
而且网格
UI元素,如果你已经阅读了链接的文档。在文档的根目录,你会注意到我们有一个容器
元素。(请在<一个href="https://semantic-ui.com/elements/container.html">容器一个>文档。
在构建完API之后,我们将在后面构建用户表单。接下来,我们将为聊天室创建一个布局。
聊天室布局
聊天室将由三个部分组成:
- 用户聊天年代trong>-用户列表
- 聊天信息年代trong>-消息列表
- 聊天后年代trong>-用于发布新消息的表单。
创建视图/ chatroom.ejs
并粘贴以下代码:
<% include部分/菜单%><年代p一个n class="token tag"><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>聊天”分区<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI容器网格<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><!——成员列表部分——> .年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>四宽柱<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>[TODO聊天用户]<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>十二宽柱<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><!——聊天消息——>年代p一个n>[TODO聊天信息]<年代p一个n class="token tag"><年代p一个n>人力资源年代p一个n>>年代p一个n><!——聊天贴——>年代p一个n>[TODO聊天贴]<年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
在查看页面之前,我们需要设置路由。
路由
开放配置/ routes.js
然后像这样更新:
' / '年代p一个n>:年代p一个n>{年代p一个n>视图<年代p一个n class="token operator">:年代p一个n>“主页”年代p一个n>}年代p一个n>,年代p一个n>“/配置文件”年代p一个n>:年代p一个n>{年代p一个n>视图<年代p一个n class="token operator">:年代p一个n>“配置文件”年代p一个n>}年代p一个n>,年代p一个n>' /聊天'年代p一个n>:年代p一个n>{年代p一个n>视图<年代p一个n class="token operator">:年代p一个n>“聊天室”年代p一个n>}年代p一个n>
js的路由非常灵活。根据场景,有许多定义路由的方法。这是我们将URL映射到视图的最基本版本。
启动你的Sails应用,或者如果你的页面还在后台运行,就刷新它。目前主页和其他页面之间没有链接。这是有意为之,因为我们稍后将构建一个基本的身份验证系统,将登录用户重定向到/聊天
.现在,使用浏览器的地址栏进行添加/聊天
或/配置文件
在结束URL。
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665713005-chat-layout.png" alt="chat-layout"loading="lazy">
在这个阶段,您应该拥有上述视图。让我们继续,开始创建API。
生成用户API
我们将使用Sails.js命令行实用程序来生成API。我们需要在这一步停止应用程序:
生成api用户
在一秒钟内,我们得到消息“创建了一个新的api!”基本上,一个User.js
模型和UserController.js
是为我们而生的。让我们更新api /模型/ User.js
使用一些模型属性:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>{年代p一个n>属性<年代p一个n class="token operator">:年代p一个n>{年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>}年代p一个n>,年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>独特的<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>}年代p一个n>,年代p一个n>《阿凡达》<年代p一个n class="token operator">:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>,年代p一个n>defaultsTo<年代p一个n class="token operator">:年代p一个n>“https://s.gravatar.com/avatar/e28f6f64608c970c663197d7fe1f5a59?s=60”年代p一个n>}年代p一个n>,年代p一个n>位置年代p一个n>:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>假年代p一个n>,年代p一个n>defaultsTo<年代p一个n class="token operator">:年代p一个n>''年代p一个n>}年代p一个n>,年代p一个n>生物<年代p一个n class="token operator">:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>假年代p一个n>,年代p一个n>defaultsTo<年代p一个n class="token operator">:年代p一个n>''年代p一个n>}年代p一个n>}年代p一个n>}年代p一个n>;年代p一个n>
我相信上面的代码是不言自明的。默认情况下,Sails.js使用本地磁盘数据库,该数据库基本上是位于.tmp
文件夹中。为了测试我们的应用程序,我们需要创建一些用户。最简单的方法是安装<一个href="https://www.npmjs.com/package/sails-seed">sails-seed包一个>:
npm年代p一个n>安装年代p一个n>sails-seed——保存
安装后,您会发现该文件配置/ seeds.js
都是为你而生的。粘贴以下种子数据:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>.年代p一个n>种子年代p一个n>=年代p一个n>{年代p一个n>用户<年代p一个n class="token operator">:年代p一个n>[年代p一个n>{年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>约翰·韦恩的年代p一个n>,年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>“johnnie86@gmail.com”年代p一个n>,年代p一个n>《阿凡达》<年代p一个n class="token operator">:年代p一个n>“https://randomuser.me/api/portraits/men/83.jpg”年代p一个n>,年代p一个n>位置年代p一个n>:年代p一个n>蒙巴萨的年代p一个n>,年代p一个n>生物<年代p一个n class="token operator">:年代p一个n>“大部分时间都待在海滩上”年代p一个n>}年代p一个n>,年代p一个n>{年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>“彼得·奎因”年代p一个n>,年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>“peter.quinn@live.com”年代p一个n>,年代p一个n>《阿凡达》<年代p一个n class="token operator">:年代p一个n>“https://randomuser.me/api/portraits/men/32.jpg”年代p一个n>,年代p一个n>位置年代p一个n>:年代p一个n>“兰利”年代p一个n>,年代p一个n>生物<年代p一个n class="token operator">:年代p一个n>“宁可不说”年代p一个n>}年代p一个n>,年代p一个n>{年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>“简爱”年代p一个n>,年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>“jane@hotmail.com”年代p一个n>,年代p一个n>《阿凡达》<年代p一个n class="token operator">:年代p一个n>“https://randomuser.me/api/portraits/women/94.jpg”年代p一个n>,年代p一个n>位置年代p一个n>:年代p一个n>“伦敦”年代p一个n>,年代p一个n>生物<年代p一个n class="token operator">:年代p一个n>“喜欢阅读励志书籍”年代p一个n>}年代p一个n>]年代p一个n>}年代p一个n>
现在我们已经生成了一个API,我们应该在文件中配置迁移策略配置/ models.js
:
迁移<年代p一个n class="token operator">:年代p一个n>“下降”年代p一个n>
在每次启动数据库时,sail .js使用三种迁移策略来确定如何重建数据库:
- 安全年代trong>-不要移动,我自己动手
- 改变年代trong>—迁移,但尽量保留已有数据
- 下降年代trong>-删除所有表并重新构建所有表
我更喜欢用下降
对于开发,因为我倾向于多次迭代。你可以设置改变
如果您想保留现有数据。尽管如此,我们的数据库每次都将由种子数据填充。
现在让我给你看一些很酷的东西。启动您的Sails项目并导航到地址/用户
而且/用户/ 1
.
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665713507-users-api.png" alt="users-api"loading="lazy">
多亏了Sails.js<一个href="https://sailsjs.com/documentation/concepts/blueprints">蓝图API一个>在美国,我们无需编写一行代码就拥有了一个功能齐全的CRUD API。您可以使用Postman访问User API并执行数据操作,例如创建、更新或删除用户。
现在让我们继续构建概要表单。
配置文件的形式
开放视图/ profile.ejs
并将现有的TODO行替换为以下代码:
<年代p一个n>img年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI小的居中圆形图像<年代p一个n class="token punctuation">"年代p一个n>src年代p一个n>=年代p一个n>"年代p一个n>< % =数据。阿凡达% ><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui网格<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>形式年代p一个n>行动年代p一个n>=年代p一个n>"年代p一个n>< % =<年代p一个n class="token punctuation">'年代p一个n>/ /更新/用户<年代p一个n class="token punctuation">'年代p一个n>+数据。id % ><年代p一个n class="token punctuation">"年代p一个n>方法年代p一个n>=年代p一个n>"年代p一个n>帖子<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>以UI为中心的表单<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>场<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>的名字<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>输入年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>的名字<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n><%= data.name %><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>场<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>电子邮件<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>输入年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>电子邮件<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n>< % =数据。电子邮件% ><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>场<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>位置<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>输入年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>位置<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n>< % =数据。位置% ><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>场<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>生物<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>文本区域年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>生物<年代p一个n class="token punctuation">"年代p一个n>行年代p一个n>=年代p一个n>"年代p一个n>4<年代p一个n class="token punctuation">"年代p一个n>关口年代p一个n>=年代p一个n>"年代p一个n>40<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>< % =数据。生物% ><年代p一个n class="token tag">年代p一个n>文本区域年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n> ><年代p一个n class="token tag"><年代p一个n>按钮年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI右侧浮动橙色按钮<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>提交<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>更新<年代p一个n class="token tag">年代p一个n>按钮年代p一个n>>年代p一个n>年代p一个n>形式年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
我们使用<一个href="https://semantic-ui.com/collections/form.html">语义用户界面形式一个>来构建表单界面。如果检查表单的动作值,/ user /更新/ ' + data.id
你会发现我用的是蓝图路线。这意味着当用户点击更新
按钮,Blueprint的更新操作将被执行。
但是,为了加载用户数据,我决定在用户控制器中定义一个自定义动作。更新api /控制器/用户控件
使用以下代码:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>{年代p一个n>渲染年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>试一试年代p一个n>{年代p一个n>让年代p一个n>数据<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>findOne年代p一个n>(年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>“johnnie86@gmail.com”年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>如果年代p一个n>(年代p一个n>!年代p一个n>数据<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>返回年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>notFound年代p一个n>(年代p一个n>“没有找到用户!”年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>视图年代p一个n>(年代p一个n>“配置文件”年代p一个n>,年代p一个n>{年代p一个n>数据<年代p一个n class="token punctuation">}年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>抓年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>serverError年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>}年代p一个n>}年代p一个n>}年代p一个n>;年代p一个n>
在这段代码中,您会注意到我使用了异步/等待
语法从数据库中获取User数据。另一种方法是使用回调,对于大多数开发人员来说,回调的可读性不强。我还硬编码了临时加载的默认用户帐户。稍后,当我们设置基本身份验证时,我们将更改它以加载当前登录的用户。
最后,我们需要改变路线/配置文件
开始使用新创建的用户控件
.开放配置/路线
并按如下方式更新配置文件路由:
...年代p一个n>“/配置文件”年代p一个n>:年代p一个n>{年代p一个n>控制器<年代p一个n class="token operator">:年代p一个n>“用户”年代p一个n>,年代p一个n>行动<年代p一个n class="token operator">:年代p一个n>“呈现”年代p一个n>}年代p一个n>,年代p一个n>...年代p一个n>
导航到URL/配置文件
,你应该有如下的视图:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665714109-profile-view.png" alt="侧面"loading="lazy">
尝试更改其中一个表单字段并点击更新按钮。你会看到这样的观点:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665714410-profile-update.png" alt="更新个人信息"loading="lazy">
您将注意到更新已经生效,但是显示的数据是JSON格式的。理想情况下,我们应该有一个视图的个人资料页面视图/ user / findOne.ejs
和一个更新个人资料页面/ / user / update.ejs观点
.Blueprint系统将猜测用于呈现信息的视图。如果找不到视图,它就会输出JSON。现在,我们将简单地使用这个巧妙的技巧。创建文件/ / user / update.ejs观点
并粘贴以下代码:
<年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>窗口年代p一个n>.年代p一个n>位置年代p一个n>=年代p一个n>“/配置文件”年代p一个n>;年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
下一次执行更新时,将被重定向到/配置文件
页面。现在我们有了用户数据,就可以创建文件了视图/泛音/ chat-users.js
用于视图/ chatroom.ejs
.创建文件后,粘贴以下代码:
<年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI基本段<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>h3年代p一个n>>年代p一个n>成员年代p一个n>年代p一个n>h3年代p一个n>>年代p一个n><年代p一个n>人力资源年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>users-content<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI中间对齐选择列表<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>// jsrender模板年代p一个n><年代p一个n>脚本年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>usersTemplate<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本/ x-jsrender<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>< div class = "项目" > < img类=“ui的化身形象”src = "{{:阿凡达}}" > < div class = "内容" > < div class = "头" >{{:名称}}< / div > < / div > < / div >年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n><年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>function loadUsers(){//加载现有用户io.socket。get('/user', function(用户,响应){renderChatUsers(用户);});//监听新的和更新的用户io.socket。On ('user', function(body) {io.socket。get('/user', function(用户,响应){renderChatUsers(用户);});});}函数renderChatUsers(data) {const template = $.templates('#usersTemplate');let htmlOutput = template.render(data);$ (' # users-content ') . html (htmlOutput); }年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
对于这个视图,我们需要一种客户端呈现方法来实时更新页面。这里,我们用到了<一个href="https://github.com/BorisMoore/jsrender">jsrender一个>库,比EJS更强大的模板引擎。的美jsrender
它可以接受数组或单个对象文字,并且模板仍然会正确呈现。如果我们做这个ejs
,我们需要结合如果
语句和为
循环来处理这两种情况。
让我来解释一下客户端JavaScript代码的流程:
loadUsers ()
.当页面第一次加载时,我们使用sail .js套接字库来执行得到
请求用户。此请求将由Blueprint API处理。然后我们将接收到的数据传递给renderChatUsers(数据)
函数。- 仍然在
loadUsers ()
函数,我们使用io.socket.on
函数。我们监听与模型相关的事件用户
.当我们收到通知时,我们再次获取用户并替换现有的HTML输出。 renderChatUsers(数据)
.这里我们获取一个带有id的脚本usersTemplate
使用jQuery模板()
函数。注意它的类型是文本/ x-jsrender
.通过指定自定义类型,浏览器将忽略并跳过该部分,因为它不知道它是什么。然后我们使用template.render ()
函数将模板与数据合并。这个过程将生成一个HTML输出,然后我们将其插入到HTML文档中。
我们写入的模板profile.ejs
在Node服务器上呈现,然后以HTML的形式发送到浏览器。对于聊天用户
,我们需要执行客户端渲染。这将允许聊天用户在不刷新浏览器的情况下看到新用户加入群组。
在测试代码之前,我们需要进行更新视图/ chatroom.ejs
以包括新创建的聊天用户
部分。取代[TODO聊天用户]
下面的代码:
…html <%包含部分/聊天用户。Ejs %>…
在同一个文件中,在最后添加这个脚本:
<年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>窗口年代p一个n>.年代p一个n>onload年代p一个n>=年代p一个n>函数年代p一个n>(年代p一个n>)年代p一个n>{年代p一个n>loadUsers年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
该脚本将调用loadUsers ()
函数。为了确认这是有效的,让我们执行一个帆抬起
并导航到/聊天
URL。
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665714611-chat-users.png" alt="聊天用户"loading="lazy">
你的视图应该像上图那样。如果有,让我们继续构建聊天室API。
ChatMessage API
和之前一样,我们将使用Sails.js生成API:
风帆生成api ChatMessage
接下来,填充api /模型/ ChatMessage.js
具有这些属性:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>{年代p一个n>属性<年代p一个n class="token operator">:年代p一个n>{年代p一个n>消息<年代p一个n class="token operator">:年代p一个n>{年代p一个n>类型<年代p一个n class="token operator">:年代p一个n>“字符串”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>}年代p一个n>,年代p一个n>createdBy<年代p一个n class="token operator">:年代p一个n>{年代p一个n>模型<年代p一个n class="token operator">:年代p一个n>“用户”年代p一个n>,年代p一个n>要求<年代p一个n class="token operator">:年代p一个n>真正的年代p一个n>}年代p一个n>}年代p一个n>}年代p一个n>;年代p一个n>
注意,我们已经声明了与用户
通过createdBy
属性。接下来,我们需要用一些聊天消息填充磁盘数据库。我们用配置/ bootstrap.js
.按如下方式更新整个代码。我们使用异步/等待
语法来简化我们的代码,避免回调地狱:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>.年代p一个n>引导年代p一个n>=年代p一个n>异步年代p一个n>函数年代p一个n>(年代p一个n>cb年代p一个n>)年代p一个n>{年代p一个n>帆<年代p一个n class="token punctuation">.年代p一个n>配置年代p一个n>.年代p一个n>浏览器名称年代p一个n>=年代p一个n>“风帆聊天应用”年代p一个n>;年代p一个n>//生成聊天消息年代p一个n>试一试年代p一个n>{年代p一个n>让年代p一个n>messageCount<年代p一个n class="token operator">=年代p一个n>ChatMessage年代p一个n>.年代p一个n>数年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>如果年代p一个n>(年代p一个n>messageCount<年代p一个n class="token operator">>年代p一个n>0年代p一个n>)年代p一个n>{年代p一个n>返回年代p一个n>;年代p一个n>//不重复消息年代p一个n>}年代p一个n>让年代p一个n>用户<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>找到年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>如果年代p一个n>(年代p一个n>用户<年代p一个n class="token punctuation">.年代p一个n>长度年代p一个n>> =年代p一个n>3.年代p一个n>)年代p一个n>{年代p一个n>控制台年代p一个n>.年代p一个n>日志年代p一个n>(年代p一个n>“生成消息……”年代p一个n>)年代p一个n>让年代p一个n>msg1<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>ChatMessage年代p一个n>.年代p一个n>创建年代p一个n>(年代p一个n>{年代p一个n>消息<年代p一个n class="token operator">:年代p一个n>“嘿!欢迎来到这个社区!”年代p一个n>,年代p一个n>createdBy<年代p一个n class="token operator">:年代p一个n>用户<年代p一个n class="token punctuation">[年代p一个n>1年代p一个n>]年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>控制台年代p一个n>.年代p一个n>日志年代p一个n>(年代p一个n>"已创建聊天消息:"年代p一个n>+年代p一个n>msg1<年代p一个n class="token punctuation">.年代p一个n>id年代p一个n>)年代p一个n>;年代p一个n>让年代p一个n>msg2<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>ChatMessage年代p一个n>.年代p一个n>创建年代p一个n>(年代p一个n>{年代p一个n>消息<年代p一个n class="token operator">:年代p一个n>“最近怎么样?”年代p一个n>,年代p一个n>createdBy<年代p一个n class="token operator">:年代p一个n>用户<年代p一个n class="token punctuation">[年代p一个n>2年代p一个n>]年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>控制台年代p一个n>.年代p一个n>日志年代p一个n>(年代p一个n>"已创建聊天消息:"年代p一个n>+年代p一个n>msg2<年代p一个n class="token punctuation">.年代p一个n>id年代p一个n>)年代p一个n>;年代p一个n>让年代p一个n>msg3<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>ChatMessage年代p一个n>.年代p一个n>创建年代p一个n>(年代p一个n>{年代p一个n>消息<年代p一个n class="token operator">:年代p一个n>“超级兴奋!”年代p一个n>,年代p一个n>createdBy<年代p一个n class="token operator">:年代p一个n>用户<年代p一个n class="token punctuation">[年代p一个n>0年代p一个n>]年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>控制台年代p一个n>.年代p一个n>日志年代p一个n>(年代p一个n>"已创建聊天消息:"年代p一个n>+年代p一个n>msg3<年代p一个n class="token punctuation">.年代p一个n>id年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>其他的年代p一个n>{年代p一个n>控制台年代p一个n>.年代p一个n>日志年代p一个n>(年代p一个n>“跳过消息生成”年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>}年代p一个n>抓年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>控制台年代p一个n>.年代p一个n>错误年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>}年代p一个n>//当你完成Bootstrap时触发这个回调方法是非常重要的!(否则你的服务器将永远不会提升,因为它正在等待Bootstrap)年代p一个n>cb年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>;年代p一个n>
最棒的是种子生成器之前运行过bootstrap.js
.这样我们就确定了用户
首先创建了数据,以便我们可以使用它来填充createdBy
字段。拥有测试数据将使我们能够在构建用户界面时快速迭代。
聊天消息界面
继续创建一个新文件视图/泛音/ chat-messages.ejs
,然后放置以下代码:
<年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI基本段<年代p一个n class="token punctuation">"年代p一个n>风格年代p一个n>=年代p一个n>"年代p一个n>高度年代p一个n>:年代p一个n>70年代p一个n>vh年代p一个n>;年代p一个n>"年代p一个n>>年代p一个n><年代p一个n>h3年代p一个n>>年代p一个n>社区的对话年代p一个n>年代p一个n>h3年代p一个n>>年代p一个n><年代p一个n>人力资源年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>聊天的<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui饲料<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>脚本年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>chatTemplate<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本/ x-jsrender<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n> {{:createdBy.name}} posted on {{:createdAt}} {{:message}} 年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n><年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>function loadMessages(){//加载现有聊天消息io.socket。get('/chatMessage',函数(消息,响应){renderChatMessages(消息);});//监听新的聊天消息io.socket。on('chatmessage',函数(body) {renderChatMessages(body.data);});}函数renderChatMessages(data) {const chatContent = $('#chat-content');const template = $.templates('# chattrplate ');let htmlOutput = template.render(data);chatContent.append (htmlOutput); // automatically scroll downwards const scrollHeight = chatContent.prop("scrollHeight"); chatContent.animate({ scrollTop: scrollHeight }, "slow"); }年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
这里的逻辑与聊天用户
.在听力部分有一个关键的区别。我们使用append而不是替换呈现的输出。然后我们做一个滚动动画到列表的底部,以确保用户看到新的传入消息。
接下来,让我们更新一下chatroom.ejs
包括新的聊天信息
部分也要更新脚本调用loadMessages ()
功能:
...<年代p一个n class="token comment"><!——聊天消息——>年代p一个n><%包含部分/聊天消息。Ejs %>…<年代p一个n class="token tag"><年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>...年代p一个n>loadMessages年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>...年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
你的视图现在应该是这样的:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665714912-chat-messages.png" alt="聊天信息"loading="lazy">
现在让我们构建一个允许用户向聊天室发布消息的简单表单。
聊天帖子界面
创建一个新文件视图/部分/ chat-post.ejs
然后粘贴这段代码:
<年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI基本段<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui表单<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui字段<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>发布消息<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>文本区域年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>编辑框<年代p一个n class="token punctuation">"年代p一个n>行年代p一个n>=年代p一个n>"年代p一个n>2<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>文本区域年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>按钮年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>post-btn<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI右侧浮动大橙色按钮<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>提交<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>帖子<年代p一个n class="token tag">年代p一个n>按钮年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>id年代p一个n>=年代p一个n>"年代p一个n>post-err<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI微小紧凑的负面消息<年代p一个n class="token punctuation">"年代p一个n>风格年代p一个n>=年代p一个n>"年代p一个n>显示年代p一个n>:年代p一个n>没有一个<年代p一个n class="token punctuation">;年代p一个n>"年代p一个n>>年代p一个n><年代p一个n>p年代p一个n>>年代p一个n>哦!出了问题。<年代p一个n class="token tag">年代p一个n>p年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n>
这里我们用的是using语义用户界面
元素来构建表单。接下来将这个脚本添加到文件底部:
<年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>function activateChat() {const postField = $('#post-field');const postButton = $('#post-btn');const postErr = $('#post-err');//绑定点击事件postButton.click(postMessage);//绑定输入按键事件postField.keypress(function(e) {var keycode = (e. keycode ?e.keyCode: e.which);if (keycode == '13') {postMessage();}});function postMessage() {if(postField.val() == "") {alert("请输入消息!");} else {let text = postField.val(); io.socket.post('/postMessage', { message: text }, function(resData, jwRes) { if(jwRes.statusCode != 200) { postErr.html("" + resData.message +"
") postErr.show(); } else { postField.val(''); // clear input field } }); } } }年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
这个脚本由两个函数组成:
activateChat ()
.该函数将发布按钮绑定到单击事件,将消息框(发布字段)绑定到按下(enter)事件。当任何一方被解雇时postMessage ()
函数被调用。postMessage
.这个函数首先进行快速验证,以确保后输入字段不是空白。如果在输入字段中提供了消息,则使用io.socket.post ()
函数将消息发送回服务器。这里我们使用一个经典的回调函数来处理来自服务器的响应。如果发生错误,则显示错误消息。如果我们得到一个200状态码,这意味着消息已被捕获,我们将清除post输入字段,准备输入下一条消息。
如果你回到聊天信息
脚本中,您将看到我们已经放置了用于检测和呈现传入消息的代码。你还应该注意到io.socket.post ()
正在向URL发送数据/ postMessage
.这不是蓝图路线,而是自定义路线。因此,我们需要为它编写代码。
前往api /控制器/ UserController.js
然后插入这段代码:
模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>{年代p一个n>postMessage年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>//确保这是一个套接字请求(不是传统的HTTP)年代p一个n>如果年代p一个n>(年代p一个n>!年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>isSocket年代p一个n>)年代p一个n>{年代p一个n>返回年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>badRequest年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>试一试年代p一个n>{年代p一个n>让年代p一个n>用户<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>findOne年代p一个n>(年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>“johnnie86@gmail.com”年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>让年代p一个n>味精<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>ChatMessage年代p一个n>.年代p一个n>创建年代p一个n>(年代p一个n>{年代p一个n>消息<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>消息年代p一个n>,年代p一个n>createdBy<年代p一个n class="token operator">:年代p一个n>用户<年代p一个n class="token punctuation">}年代p一个n>)年代p一个n>;年代p一个n>如果年代p一个n>(年代p一个n>!年代p一个n>味精<年代p一个n class="token punctuation">.年代p一个n>id年代p一个n>)年代p一个n>{年代p一个n>扔年代p一个n>新年代p一个n>错误年代p一个n>(年代p一个n>“消息处理失败!”年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>味精<年代p一个n class="token punctuation">.年代p一个n>createdBy年代p一个n>=年代p一个n>用户<年代p一个n class="token punctuation">;年代p一个n>ChatMessage年代p一个n>.年代p一个n>publishCreate年代p一个n>(年代p一个n>味精<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>}年代p一个n>抓年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>返回年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>serverError年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>}年代p一个n>返回年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>好吧年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>}年代p一个n>;年代p一个n>
因为我们还没有设置基本的身份验证,所以我们对用户进行了硬编码johnnie86@gmail.com
目前作为这条信息的作者。我们使用Model.create ()
水线ORM函数创建一个新的记录。这是一种奇特的插入记录的方式,无需编写SQL代码。接下来,我们向所有套接字发送一个通知事件,通知他们已经创建了一个新消息。我们用ChatMessage.publishCreate ()
函数,它在蓝图API中定义。在我们发出消息之前,我们要确保createdBy
字段中填充用户
对象。这是由聊天信息
部分以访问虚拟形象和创建消息的用户的名称。
接下来,转到配置/ routes.js
绘制/ postMessage
的URLpostMessage
我们刚刚定义的动作。插入以下代码:
...年代p一个n>' /聊天'年代p一个n>:年代p一个n>{年代p一个n>视图<年代p一个n class="token operator">:年代p一个n>“聊天室”年代p一个n>}年代p一个n>,年代p一个n>//在这里加逗号年代p一个n>' / postMessage '年代p一个n>:年代p一个n>{年代p一个n>控制器<年代p一个n class="token operator">:年代p一个n>“ChatMessageController”年代p一个n>,年代p一个n>行动<年代p一个n class="token operator">:年代p一个n>“postMessage”年代p一个n>}年代p一个n>...年代p一个n>
开放视图/ chatroom.js
并包括chat-post
部分。我们也会调用activateChat ()
函数在loadMessages ()
功能:
...<%包含部分/聊天消息。Ejs %>…<年代p一个n class="token tag"><年代p一个n>脚本年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>text / javascript<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>...年代p一个n>activateChat年代p一个n>(年代p一个n>)年代p一个n>;年代p一个n>...年代p一个n>年代p一个n>脚本年代p一个n>>年代p一个n>
刷新页面并尝试发送几条消息。
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665715213-chat-post.png" alt="chat-post"loading="lazy">
现在您应该有了一个功能正常的聊天系统。检查项目源代码,以防遇到困难。
基本身份验证
设置适当的身份验证和授权系统超出了本教程的范围。所以我们将满足于一个基本的无密码认证系统。让我们首先构建注册和登录表单。
创建一个新文件视图/ auth-form.ejs
并粘贴如下内容:
<年代p一个n>形式年代p一个n>方法年代p一个n>=年代p一个n>"年代p一个n>帖子<年代p一个n class="token punctuation">"年代p一个n>行动年代p一个n>=年代p一个n>"年代p一个n>/身份验证/认证<年代p一个n class="token punctuation">"年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>ui表单<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>场<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>全名<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>输入年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>文本<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>的名字<年代p一个n class="token punctuation">"年代p一个n>占位符年代p一个n>=年代p一个n>"年代p一个n>全名<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n><%= typeof name !=<年代p一个n class="token punctuation">'年代p一个n>未定义的<年代p一个n class="token punctuation">'年代p一个n>?名称:<年代p一个n class="token punctuation">'年代p一个n>'年代p一个n>% ><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>必需字段<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>标签年代p一个n>>年代p一个n>电子邮件<年代p一个n class="token tag">年代p一个n>标签年代p一个n>>年代p一个n><年代p一个n>输入年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>电子邮件<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>电子邮件<年代p一个n class="token punctuation">"年代p一个n>占位符年代p一个n>=年代p一个n>"年代p一个n>电子邮件<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n><%= typeof email !=<年代p一个n class="token punctuation">'年代p一个n>未定义的<年代p一个n class="token punctuation">'年代p一个n>?电子邮件:<年代p一个n class="token punctuation">'年代p一个n>'年代p一个n>% ><年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>按钮年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI蓝绿色按钮<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>提交<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>行动<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n>注册<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>报名<年代p一个n class="token entity named-entity" title="&">,年代p一个n>登录<年代p一个n class="token tag">年代p一个n>按钮年代p一个n>>年代p一个n><年代p一个n>按钮年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI蓝色按钮<年代p一个n class="token punctuation">"年代p一个n>类型年代p一个n>=年代p一个n>"年代p一个n>提交<年代p一个n class="token punctuation">"年代p一个n>的名字年代p一个n>=年代p一个n>"年代p一个n>行动<年代p一个n class="token punctuation">"年代p一个n>价值年代p一个n>=年代p一个n>"年代p一个n>登录<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>登录<年代p一个n class="token tag">年代p一个n>按钮年代p一个n>>年代p一个n><年代p一个n>p年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>请注意<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>*只提供登入电邮<年代p一个n class="token tag">年代p一个n>p年代p一个n>>年代p一个n>年代p一个n>形式年代p一个n>>年代p一个n><% if(typeof error != 'undefined') {%> . if(typeof error != 'undefined'<年代p一个n class="token tag"><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>UI错误消息<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n><年代p一个n>div年代p一个n>类年代p一个n>=年代p一个n>"年代p一个n>头<年代p一个n class="token punctuation">"年代p一个n>>年代p一个n>< % =错误。标题% ><年代p一个n class="token tag">年代p一个n>div年代p一个n>>年代p一个n><年代p一个n>p年代p一个n>>年代p一个n>< % =错误。消息% ><年代p一个n class="token tag">年代p一个n>p年代p一个n>>年代p一个n>年代p一个n>div年代p一个n>>年代p一个n><%} %>
的下一个开放视图/ homepage.ejs
用include语句替换TODO行:
...<% include partial /auth-form。Ejs %>…
我们已经创建了一个表单,允许您通过提供姓名和电子邮件的输入来创建一个新帐户。当你点击注册和登录
,就会创建一个新的用户记录,然后您就可以登录了。但是,如果该电子邮件已被其他用户使用,则会显示错误消息。如果您只是想登录,只需提供电子邮件地址并单击登录
按钮。身份验证成功后,您将被重定向到/聊天
URL。
现在,我刚才说的一切都不管用。我们需要实现这个逻辑。首先,让我们导航到/
地址确认auth-form
看起来商品。
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665715414-auth-form.png" alt="auth-form"loading="lazy">
政策
现在我们建立了一个认证系统,我们需要保护/聊天
而且/配置文件
来自公众访问的路由。应该只允许经过身份验证的用户访问它们。开放配置/ policies.js
然后插入这段代码:
ChatMessageController年代p一个n>:年代p一个n>{年代p一个n>‘*’年代p一个n>:年代p一个n>“sessionAuth”年代p一个n>}年代p一个n>,年代p一个n>用户控件年代p一个n>:年代p一个n>{年代p一个n>‘*’年代p一个n>:年代p一个n>“sessionAuth”年代p一个n>}年代p一个n>,年代p一个n>
通过指定控制器的名称,我们还有效地阻止了Blueprint API为用户和聊天消息提供的所有路由。不幸的是,策略只适用于控制器。这意味着路线/聊天
无法在当前状态下进行保护。我们需要为它定义一个自定义操作。开放api /控制器/ ChatroomController.js
然后插入这段代码:
...年代p一个n>渲染年代p一个n>:年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>返回年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>视图年代p一个n>(年代p一个n>“聊天室”年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>,年代p一个n>
的路由配置/聊天
有了这个配置/ routes.js
:
...年代p一个n>' /聊天'年代p一个n>:年代p一个n>{年代p一个n>控制器<年代p一个n class="token operator">:年代p一个n>“ChatMessageController”年代p一个n>,年代p一个n>行动<年代p一个n class="token operator">:年代p一个n>“呈现”年代p一个n>}年代p一个n>,年代p一个n>...年代p一个n>
的/聊天
路由现在应该被保护,不被公众访问。如果你重启你的应用并尝试访问/配置文件
,/聊天
,/用户
或/ chatmessage
,你会看到以下禁止的信息:
<我mg decoding="async" src="https://uploads.sitepoint.com/wp-content/uploads/2018/01/151665715915-forbidden.png" alt="被禁止的"loading="lazy">
如果您想将用户重定向到登录表单,请转到api /政策/ sessionAuth
然后像这样用重定向调用替换禁止调用:
...年代p一个n>//返回res.forbidden('您不允许执行此操作。');年代p一个n>返回年代p一个n>res<年代p一个n class="token punctuation">.年代p一个n>重定向年代p一个n>(年代p一个n>' / '年代p一个n>)年代p一个n>;年代p一个n>...年代p一个n>
尝试再次访问禁止页面,您将自动重定向到主页。现在让我们实现注册和登录代码。
认证控制器和服务
为了运行这个命令,你需要先停止Sails.js:
风帆生成控制器认证
这将创建一个空白api /控制器/ AuthController
对我们来说。打开它并插入以下代码:
进行身份验证年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>//注册用户年代p一个n>如果年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>行动年代p一个n>= =年代p一个n>“注册”年代p一个n>)年代p一个n>{年代p一个n>//验证注册表单年代p一个n>//检查邮件是否已注册年代p一个n>//创建新用户年代p一个n>}年代p一个n>//登录用户年代p一个n>}年代p一个n>,年代p一个n>注销年代p一个n>:年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>//注销用户年代p一个n>}年代p一个n>
我已经在评论中解释了逻辑将如何流动。我们可以把相关代码放在这里。然而,Sails.js建议我们保持我们的控制器代码简单,易于遵循。为了实现这一点,我们需要编写helper函数来帮助我们处理上面的每个注释任务。为了创建这些帮助函数,我们需要创建一个服务。通过创建一个新文件来做到这一点吗api /服务/ AuthService.js
.插入以下代码:
/** * AuthService.js ** */年代p一个n>常量年代p一个n>功能<年代p一个n class="token operator">=年代p一个n>需要年代p一个n>(年代p一个n>“功能”年代p一个n>)年代p一个n>//在哪里显示认证错误年代p一个n>常量年代p一个n>视图<年代p一个n class="token operator">=年代p一个n>“主页”年代p一个n>;年代p一个n>模块<年代p一个n class="token punctuation">.年代p一个n>出口年代p一个n>=年代p一个n>{年代p一个n>sendAuthError年代p一个n>:年代p一个n>(年代p一个n>响应<年代p一个n class="token punctuation">,年代p一个n>标题<年代p一个n class="token punctuation">,年代p一个n>消息<年代p一个n class="token punctuation">,年代p一个n>选项年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>选项<年代p一个n class="token operator">=年代p一个n>选项<年代p一个n class="token operator">||年代p一个n>{年代p一个n>}年代p一个n>;年代p一个n>常量年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token punctuation">,年代p一个n>的名字<年代p一个n class="token punctuation">}年代p一个n>=年代p一个n>选项<年代p一个n class="token punctuation">;年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>视图年代p一个n>(年代p一个n>视图<年代p一个n class="token punctuation">,年代p一个n>{年代p一个n>错误<年代p一个n class="token operator">:年代p一个n>{年代p一个n>标题<年代p一个n class="token punctuation">,年代p一个n>消息<年代p一个n class="token punctuation">}年代p一个n>,年代p一个n>电子邮件<年代p一个n class="token punctuation">,年代p一个n>的名字<年代p一个n class="token punctuation">}年代p一个n>)年代p一个n>;年代p一个n>返回年代p一个n>假年代p一个n>;年代p一个n>}年代p一个n>,年代p一个n>validateSignupForm年代p一个n>:年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>如果年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>的名字年代p一个n>= =年代p一个n>''年代p一个n>)年代p一个n>{年代p一个n>返回年代p一个n>AuthService年代p一个n>.年代p一个n>sendAuthError年代p一个n>(年代p一个n>响应<年代p一个n class="token punctuation">,年代p一个n>“注册失败!”年代p一个n>,年代p一个n>“你必须提供一个名字来注册”年代p一个n>,年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>电子邮件年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>其他的年代p一个n>如果年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>电子邮件年代p一个n>= =年代p一个n>''年代p一个n>)年代p一个n>{年代p一个n>返回年代p一个n>AuthService年代p一个n>.年代p一个n>sendAuthError年代p一个n>(年代p一个n>响应<年代p一个n class="token punctuation">,年代p一个n>“注册失败!”年代p一个n>,年代p一个n>“您必须提供电子邮件地址才能注册”年代p一个n>,年代p一个n>{年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>的名字年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>}年代p一个n>返回年代p一个n>真正的年代p一个n>;年代p一个n>}年代p一个n>,年代p一个n>checkDuplicateRegistration年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>试一试年代p一个n>{年代p一个n>让年代p一个n>existingUser<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>findOne年代p一个n>(年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>电子邮件年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>如果年代p一个n>(年代p一个n>existingUser<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>常量年代p一个n>选项<年代p一个n class="token operator">=年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>电子邮件年代p一个n>,年代p一个n>的名字<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>的名字年代p一个n>}年代p一个n>;年代p一个n>返回年代p一个n>AuthService年代p一个n>.年代p一个n>sendAuthError年代p一个n>(年代p一个n>响应<年代p一个n class="token punctuation">,年代p一个n>“重复注册!”年代p一个n>,年代p一个n>“所提供的电子邮件已注册”年代p一个n>,年代p一个n>选项<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>}年代p一个n>返回年代p一个n>真正的年代p一个n>;年代p一个n>}年代p一个n>抓年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>serverError年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>返回年代p一个n>假年代p一个n>;年代p一个n>}年代p一个n>}年代p一个n>,年代p一个n>registerUser年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>数据<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>试一试年代p一个n>{年代p一个n>常量年代p一个n>{年代p一个n>的名字<年代p一个n class="token punctuation">,年代p一个n>电子邮件<年代p一个n class="token punctuation">}年代p一个n>=年代p一个n>数据<年代p一个n class="token punctuation">;年代p一个n>常量年代p一个n>《阿凡达》<年代p一个n class="token operator">=年代p一个n>功能<年代p一个n class="token punctuation">.年代p一个n>url年代p一个n>(年代p一个n>电子邮件<年代p一个n class="token punctuation">,年代p一个n>{年代p一个n>年代<年代p一个n class="token operator">:年代p一个n>200年代p一个n>}年代p一个n>,年代p一个n>“https”年代p一个n>)年代p一个n>;年代p一个n>让年代p一个n>分类列出<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>创建年代p一个n>(年代p一个n>{年代p一个n>的名字<年代p一个n class="token punctuation">,年代p一个n>电子邮件<年代p一个n class="token punctuation">,年代p一个n>《阿凡达》<年代p一个n class="token punctuation">}年代p一个n>)年代p一个n>;年代p一个n>//让所有socket知道一个新用户已经创建年代p一个n>用户年代p一个n>.年代p一个n>publishCreate年代p一个n>(年代p一个n>分类列出<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>返回年代p一个n>分类列出<年代p一个n class="token punctuation">;年代p一个n>}年代p一个n>抓年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>{年代p一个n>响应<年代p一个n class="token punctuation">.年代p一个n>serverError年代p一个n>(年代p一个n>犯错<年代p一个n class="token punctuation">)年代p一个n>;年代p一个n>返回年代p一个n>假年代p一个n>;年代p一个n>}年代p一个n>}年代p一个n>,年代p一个n>登录年代p一个n>:年代p一个n>异步年代p一个n>(年代p一个n>请求<年代p一个n class="token punctuation">,年代p一个n>响应年代p一个n>)年代p一个n>= >年代p一个n>{年代p一个n>试一试年代p一个n>{年代p一个n>让年代p一个n>用户<年代p一个n class="token operator">=年代p一个n>等待年代p一个n>用户年代p一个n>.年代p一个n>findOne年代p一个n>(年代p一个n>{年代p一个n>电子邮件<年代p一个n class="token operator">:年代p一个n>请求<年代p一个n class="token punctuation">.年代p一个n>身体年代p一个n>.年代p一个n>电子邮件年代p一个n>}年代p一个n>)年代p一个n>;年代p一个n>