sails.js 是一个新兴的 node.js 框架,专注于自由和智能默认设置。在本文中,我们将了解 sails 提供的一些开箱即用的数据功能,以便轻松制作复杂的应用程序。
为什么 Sails 与其他框架不同
选择 Sails 的原因由 Sails 的创建者 Mike McNeil 说得最好,“Sails 的创建是出于需要”。您看到的许多框架几乎都是为学术方面而构建的,这些框架通常会促进最佳实践,并为开发人员创建一个平台,以便更快或更好地创建东西。
另一方面,Sails 是为生产而创建的,它并不是试图为您提供新的语法或平台,而是一个坚实的基础,旨在快速创建“客户工作”。对比可能很微妙,但还是有一些明显的差异。
为了说明我所指的内容,让我们看一下 Meteor。 Meteor 可能是当今领先的 JS 平台,但它是框架的一个主要示例,只是为了框架。现在这并不是一件坏事,我是 Meteor 的大力支持者,我的意思是,他们着手构建一个框架并且做得很好,另一方面 Mike 着手让客户工作更快。风帆只是达到目的的一种手段。
在 Meteor 中,几乎所有内容都被抽象化,您可以使用 JavaScript 加上 Meteor API 来对所有内容进行编码。而 Sails 并不是一个新平台,因此没有隐藏任何内容。
它建立在 Socket.io 和流行的 Express 框架之上,您可以在本机上完整地访问它们。您开始看到差异了吗?
此外,由于 Sails 首先面向生产,因此它具有多种扩展和安全选项。
有很多内容要谈,但在本文中,我想重点讨论 Sails 如何处理数据,以及如何利用 Sails 的一些更高级的功能来执行一些非常酷的操作。
安装
以防万一您尚未安装 Sails,您可以通过 NPM 运行以下命令来安装:
sudo npm install -g sails
Socket.io 和 Express
现在,在讨论 Sails 之前,让我们先谈谈 Socket.io 和 Express。如果您感兴趣的话,Andrew Burgess 有一个关于 Express 的很好的高级系列,但我将在这里介绍这两个库的相关基础知识:
Socket.io
Socket.io 是一个 pub/sub 库,它在服务器和客户端上运行,并且允许它们通过 Web 套接字进行通信。
一个简短的示例可能如下所示:
//Code For Server var io = require("socket.io"); io.sockets.on("connection", function (sock) { sock.emit("welcomeMessage", { hello: "world" }); } io.listen(80);
此代码首先需要 socket.io 库,侦听连接,然后当另一个套接字连接时,它将向其发送一条消息,发送给 welcomeMessage 事件,最后传递一些 JSON .
接下来,在客户端上您将编写如下内容:
//Code For Client var sock = io.connect('http://localhost'); sock.on('welcomeMessage', function (json) { //Handle Event Received });
在这里,我们连接到服务器并监听我们刚刚创建的 welcomeMessage 事件。正如您所看到的,这是一个相当简单的发布/订阅服务器,它是双向的(客户端也可以向服务器发出消息)。
现在让我们看一下 Express:
快递
快速路线的最简单形式可能类似于:
app.get('/users', function(req, res) { res.send("Hello from '/users' !"); });
这定义了一个简单的路由,这样当用户访问您网站的地址并尝试访问 /users 页面时,他们将看到消息 “Hello from ‘/users’ !”。
So Express 是一个处理 HTTP 请求的框架,而 Socket.io 是一个 websocket 通信库。 Sails 团队所做的是将所有 Express 路由内部映射到 Socket.io。这意味着,您可以通过 Web 套接字调用任何 HTTP 路由。
现在这太酷了!但是,仍然缺少一块拼图,那就是帆蓝图。
Sails 允许您像在其他框架中一样生成模型,不同之处在于,Sails 还可以生成生产就绪的 RESTfull API 来配合它们。这意味着,如果您生成名为“users”的模型,您可以立即对“/users”资源运行 RESTfull 查询,而无需任何编码。
如果您不熟悉 RESTful API,它只是一种访问数据的方式,其中 CRUD 操作映射到各种 HTTP 方法。
因此,对 ‘/users’ 的 GET 请求将获取所有用户,POST 请求将创建一个新用户,等等。
那么这一切意味着什么?
这意味着我们拥有完整的 RESTfull API,通过 Sails 映射到 Socket.io,无需编写任何代码!
但是为什么套接字比 Ajax 请求更擅长检索数据呢?好吧,除了是一个更精简的协议之外,套接字还保持开放状态以进行双向通信,Sails 已经利用了这一点。 Sails 不仅会向您传递数据,还会自动为您订阅该数据库的更新,每当添加、删除或更新某些内容时,您的客户端都会通过 Web 套接字收到通知,让您了解相关情况。
这就是 Sails 如此出色的原因!
帆+骨干
我想讨论的下一个主题是 Backbone 集成,因为如果您不使用 JavaScript 框架,那么您就做错了。
考虑到这一点,Sails 和 Backbone 是完美的组合。 Backbone 与 Sails 一样,非常不显眼,它的所有功能都是可用的,能够被覆盖,并且是可选的。
如果您之前使用过 Backbone,您可能知道它与 REST API 本地连接,因此开箱即用,您可以将前端上的数据与 Sails 应用程序同步。
但是现在已经说得够多了,让我们通过创建一个基本的聊天应用程序来看看所有这一切的实际情况。首先,打开终端窗口并输入:
sails new ChatApp cd ChatApp sails generate model users sails generate model messages sails generate controller messages sails generate controller main
这将为我们创建一个新应用程序并生成一些文件。从上面您可以看到,您可以生成两种不同的资源;模型和控制器。如果您熟悉 MVC 设计模式,那么您应该知道它们是什么,但简而言之,模型是您的数据,控制器保存您的逻辑代码。因此,我们需要两个集合,一个用于保存用户,另一个用于保存消息。
接下来,对于控制器,我们需要一个来处理页面路由,我将其命名为“main”,然后我们有第二个控制器,名为“messages”。现在你可能想知道为什么我创建了一个与我们的 messages 模型同名的控制器?好吧,如果您还记得的话,我说过 Sails 可以为您创建 REST API。发生的情况是,通过创建与模型同名的空白控制器,Sails 将知道回退并为相应的资源构建 REST API。
因此,我们已经为 messages 模型创建了一个控制器,但不需要为 users 模型创建一个控制器,所以我将其省略。这就是创建模型和控制器的全部内容。
接下来,让我们设置一些路线。
路线
路由始终是一个安全的起点,因为您通常很清楚要创建哪些页面。
所以打开 config 文件夹中的 routes.js 文件,一开始可能看起来有点不知所措,但是如果你删除所有注释并在以下路由中添加留下这样的东西:
module.exports.routes = { '/' : { controller: 'main', action: 'index' }, '/signup' : { controller: 'main', action: 'signup' }, '/login' : { controller: 'main', action: 'login' }, '/chat' : { controller: 'main', action: 'chat' } };
我们有一个主页、一个聊天页面,以及两个用于处理登录和注册页面的页面。我将它们全部放在同一个控制器中,但在 Sails 中,您可以创建任意数量的控制器。
型号
接下来,我们看一下生成的 messages 模型,该模型位于“api > models > Messages.js”。我们需要将必要的列添加到我们的模型中。现在这不是绝对必要的,但它会为我们创建一些我们可以使用的辅助函数:
//Messages Model module.exports = { attributes : { userId: 'INT', username: 'STRING', message: 'STRING' } };
对于 messages 模型,我们从该消息所属用户的 id 开始,一个 username 这样我们就不必单独查询它,然后是实际的 message。
现在让我们填写用户的模型:
//Users Model module.exports = { attributes : { username: 'STRING', password: 'STRING' } };
就是这样,我们只有 username 和 password 属性。下一步是在 MainController 中创建路由函数。
控制器
因此,打开MainController,可以在“api>controllers>MainController.js”中找到它。让我们首先为上面定义的每个路由创建一个函数:
var MainController = { index: function (req, res) { }, signup: function (req, res) { }, login: function (req, res) { }, chat: function (req, res) { } }; module.exports = MainController;
如果您熟悉 Express,那么您会很高兴看到这些功能是标准的 Express 路线功能。它们接收两个变量,req 用于 HTTP 请求,res 用于创建响应。
遵循 MVC 模式,Sails 提供了渲染视图的功能。主页不需要任何特殊的东西,所以我们只渲染视图。
index: function (req, res) { res.view(); },
Sails 更倾向于约定优于配置,因此当您调用 res.view(); 时,Sails 将使用以下模式查找视图文件(默认情况下带有 .ejs 扩展名): ‘视图 > 控制器名称 > 方法名称.ejs’。因此,对于此调用,它将搜索“views>main>index.ejs”。还值得注意的是,这些视图仅包含页面的视图特定部分。如果你看一下’views>layout.ejs’,你会在中间看到一个对 的调用,这是你的视图文件将被插入的地方。默认情况下,它使用“layout.ejs”文件,但您只需将布局名称传递到名为“layout”的属性下的 res.view() 函数即可使用其他布局文件。例如: ‘res.view( { layout: “other.ejs” } );’。
我将使用默认布局文件并进行一些小调整,我将添加 jQuery、Backbone 和 Underscore。因此,在 ‘layout.ejs’ 文件中,在关闭 标记之前,添加以下行:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
完成后,我们现在就可以创建主页了。
主页
让我们在 views 文件夹中创建一个名为 main 的新文件夹,并在新的 main 文件夹中创建一个名为“index.ejs”的新文件。
在文件中,我们只创建一个登录和注册表单:
<h1>Code Chat</h1> <div> <h3>Login</h3> <input type="text" id="loginName" placeholder="name" /> <input type="password" id="loginPassword" placeholder="password" /> <button id="loginButton">Login</button> </div> <div> <h3>Signup</h3> <input type="text" id="signupName" placeholder="name" /> <input type="password" id="signupPassword" placeholder="password" /> <input type="password" id="signupConfirmPassword" placeholder="confirm password" /> <button id="signupButton">Signup</button> </div>
非常简单,只是要点。
登录和注册区域
接下来我们需要添加一些 JS 来与服务器进行通信。现在这不是 Sails 特有的,我们只是通过 jQuery 向 Sails 服务器发送 AJAX 请求。
此代码可以包含在页面本身中,也可以通过单独的 JS 文件加载。为了方便起见,我将其放在同一页面的底部:
<script> $("#loginButton").click(function(){ var username = $("#loginName").val(); var password = $("#loginPassword").val(); if (username && password) { $.post( '/login', {username: username, password:password}, function () { window.location = "/chat"; } ).fail(function(res){ alert("Error: " + res.getResponseHeader("error")); }); } else { alert("A username and password is required"); } }); </script>
这只是标准的 JS 和 jQuery,我们正在监听登录按钮上的单击事件,确保填写用户名和密码字段,并将数据发布到 ‘/login’ 路由。如果登录成功,我们将用户重定向到聊天页面,否则将显示服务器返回的错误。
接下来,让我们为注册区域创建相同的内容:
$("#signupButton").click(function(){ var username = $("#signupName").val(); var password = $("#signupPassword").val(); var confirmPassword = $("#signupConfirmPassword").val(); if (username && password) { if (password === confirmPassword) { $.post( '/signup', {username: username, password:password}, function () { window.location = "/chat"; } ).fail(function(res){ alert("Error: " + res.getResponseHeader("error")); }); } else { alert("Passwords don't match"); } } else { alert("A username and password is required"); } });
这段代码几乎是相同的,以至于您可以将整个 Ajax 部分抽象为它自己的函数,但对于本教程来说这很好。
现在我们需要返回到“MainController”并处理这两个路由,但在此之前,我想安装一个 Node 模块。我们需要对密码进行哈希处理,因为纯文本密码不是一件好事,甚至不利于演示!我发现了一个不错的模块,名为“password-hash”,由 David Wood 编写,效果很好。
要安装它,只需在终端中转到 Sails 应用程序的根目录并输入:npm install password-hash。
安装完成后,让我们打开 MainController 并实现两个所需的路由。让我们从 signup 开始:
signup: function (req, res) { var username = req.param("username"); var password = req.param("password"); Users.findByUsername(username).done(function(err, usr){ if (err) { res.send(500, { error: "DB Error" }); } else if (usr) { res.send(400, {error: "Username already Taken"}); } else { var hasher = require("password-hash"); password = hasher.generate(password); Users.create({username: username, password: password}).done(function(error, user) { if (error) { res.send(500, {error: "DB Error"}); } else { req.session.user = user; res.send(user); } }); } }); }
这有点冗长,但我们在这里所做的只是从 POST 请求中读取用户名和密码,并确保用户名尚未被占用。你可以看到我也在使用我们刚刚安装的密码哈希器,它使用起来非常简单,只需将密码传递到生成方法中,它就会使用随机盐对其进行哈希处理。
还值得一提的是,在我们可能遇到错误或问题的每个可能位置,我们都会发回 HTTP 错误代码并通过名为“error”的自定义标头返回消息,如果您请记住,我们正在索引页上的警报消息中显示。
另一个值得注意的点是,我们使用了一个名为“findByUsername”的“神奇”函数,这是因为我们的用户模型中有一个 username 列。
最后,在底部您可以看到是否一切顺利,我们将用户存储在会话变量中,并以默认状态代码 200 返回它,这将告诉 jQuery AJAX 请求已成功。
接下来我们来编写登录函数:
login: function (req, res) { var username = req.param("username"); var password = req.param("password"); Users.findByUsername(username).done(function(err, usr) { if (err) { res.send(500, { error: "DB Error" }); } else { if (usr) { var hasher = require("password-hash"); if (hasher.verify(password, usr.password)) { req.session.user = usr; res.send(usr); } else { res.send(400, { error: "Wrong Password" }); } } else { res.send(404, { error: "User not Found" }); } } }); }
同样,这与之前的 signup 函数非常相似,我们正在搜索与从表单中发布的用户名相同的用户,如果找到,我们使用哈希器的 验证方法。我们不能再次对密码进行哈希处理并将其传递到模型 find 函数的原因是因为哈希器使用随机盐,因此如果我们再次对密码进行哈希处理,它将等于其他内容。
其余代码相同;如果一切正常,我们将用户存储在会话中并返回它,否则我们会发回一条错误消息。
登录系统现已完成,我们终于可以继续构建聊天功能了。
构建聊天功能
由于我们将使用 Backbone 来获取消息,因此实际的路由功能将非常简单。这是完整的聊天功能:
chat: function (req, res) { if (req.session.user) { res.view({username: req.session.user.username}); } else { res.redirect('/'); } }
我们首先检查用户是否登录,如果检查成功,那么它将加载视图,并将会话中的用户名传递给它,否则我们只是重定向到主页。 p>
现在让我们在 main 文件夹中创建一个名为“chat.ejs”的新视图。打开它,让我们创建一个简单的表单来发布新消息,并创建一个 div 容器来显示所有消息。
<h2>Welcome <%= username %></h2> <div id="newMessageForm"> <textarea id="message" placeholder="Enter your message here:"></textarea> <button id="postMessageButton">Add Message</button> </div> <div id="messagesContainer"> </div>
对于这个视图,我们只使用了一些非常标准的 HTML。唯一可能需要解释的是 代码,这种编码风格并不是 Sails 特有的,它实际上是 EJS 的语法。这种语法与 PHP 的短标签非常相似。
我们的聊天功能的其余部分将全部由 JavaScript 实现。首先,让我们看看如何使用标准 Backbone 编写聊天功能,然后我们将了解如何利用网络套接字。
在页面底部添加以下JS:
<script> var MessageModel = Backbone.Model.extend({ urlRoot: '/messages', }); var MessageCollection = Backbone.Collection.extend({ url: '/messages', model: MessageModel, }); var messages = new MessageCollection(); messages.fetch(); $("#postMessageButton").click(function(){ var messageText = $("#message").val(); messages.create({message: messageText}, {wait: true}); $("#message").val(""); }); </script>
由于 Sails 自动创建 Backbone 本身可以理解的 API,因此无需编写额外的服务器代码,因此没有比这更容易的了。这就是我在说 Sails 不是为了成为一个“框架”时所谈论的内容。它不会试图让您使用自己的语法,它是为了完成任务而设计的,正如您所看到的,它提供了。
要对其进行测试,请打开终端窗口并导航到 Sails 应用程序文件夹,然后输入“sails lift”以启动它。默认情况下,它将启动到 http://localhost:1337。现在只需注册并发布一些消息即可。
要查看您发布的消息,您可以 console.log messages 变量,或者在浏览器控制台中查看它。现在我们接下来应该实现一个视图,以便我们可以在浏览器中看到发布的消息。
_.templateSettings = { interpolate : /{{(.+?)}}/g }; var MessagesView = Backbone.View.extend({ el: '#messagesContainer', initialize: function () { this.collection.on('add', this.render, this); this.render(); }, template: _.template("<div><p>{{ message }}</p></div>"), render: function () { this.$el.html(""); this.collection.each(function(msg){ this.$el.append(this.template(msg.toJSON())); }, this) } }); var mView = new MessagesView({collection: messages});
我们首先定义一个视图,将其附加到我们之前创建的 div,然后在集合上添加一个事件处理程序,以便在每次将新模型添加到集合时重新渲染 div。
您可以在顶部看到,我必须将默认的下划线设置从使用模板内部的 EJS 语法更改为使用 Mustache 的语法。这是因为该页面已经是一个 EJS 文档,因此它将在服务器上处理,而不是在 Underscore 中处理。
注意:我没有为此想出正则表达式,这归功于 Underscore 文档本身。
最后,在底部您可以看到我们创建了该视图的一个新实例,并向其传递了集合变量。
如果一切顺利,您现在应该在浏览器中看到您的消息,并且每当您创建新帖子时它都会更新。
航行政策
现在您可能已经注意到,我们在提交帖子时没有设置 userId 或 username,这是出于安全目的。
您不想将这种控制放在客户端。如果某人所要做的就是修改 JavaScript 变量来控制另一个用户的帐户,那么您将遇到一个大问题。
那么,你应该如何处理这个问题呢?嗯,当然有政策。
策略基本上是中间件,在实际 Web 请求之前运行,您可以根据需要在其中停止、修改甚至重定向请求。
对于此应用,让我们为我们的消息创建一个策略。策略应用于控制器,因此它们甚至可以在普通页面上运行,但在本教程中,我们只为 messages 模型使用一个策略。
在“api>policies”文件夹中创建一个名为“MessagesPolicy.js”的文件,并输入以下内容:
module.exports = function (req, res, next) { if (req.session.user) { var action = req.param('action'); if (action == "create") { req.body.userId = req.session.user.id; req.body.username = req.session.user.username; } next(); } else { res.send("You Must Be Logged In", 403); } };
那么,这是怎么回事?您可以看到该函数类似于普通的路由函数,但区别在于第三个参数,它将调用堆栈中的下一个中间件。如果您对中间件的概念不熟悉,您可以将其想象为俄罗斯嵌套娃娃。每一层都会收到请求以及响应变量,并且可以根据需要对其进行修改。如果满足了所有的要求,该层就可以进一步传入,直到到达中心,这就是路由函数。
所以我们在这里检查用户是否已登录,如果用户未登录,我们将显示 403 错误并且请求在此结束。否则,(即用户已登录)我们调用 next(); 来传递它。在上面代码的中间,是我们注入一些后置变量的地方。我们将此应用于“消息”控制器(基本上是 API)上的所有调用,因此我们获取操作并检查此请求是否正在尝试创建新消息,在这种情况下,我们为用户的 id 和 用户名。
接下来,打开config文件夹中的policies.js文件,并添加我们刚刚创建的策略。所以你的文件应该是这样的:
module.exports.policies = { '*': true, 'messages': 'MessagesPolicy' };
完成此操作后,我们需要删除所有旧记录,因为它们没有这些新信息。因此,关闭 Sails 服务器 (ctrl-c) 并在同一终端窗口中输入: rm -r .tmp 以删除临时数据库,让我们重新开始。
接下来,让我们将用户名添加到实际帖子中,因此在“chat.ejs”中将模板更改为:
template: _.template("<div><p><b>{{ username }}: </b>{{ message }}</p></div>"),
重新启动 Sails 服务器(再次使用 sails lift)并注册另一个新用户来测试它。如果一切正常,您应该能够添加消息并在帖子中看到您的名字。
此时我们已经有了一个非常好的设置,我们使用 Backbone 和 API 自动获取帖子,此外我们还有一些基本的安全措施。问题是,当其他人发布消息时它不会更新。现在,您可以通过创建 JavaScript 间隔并轮询更新来解决此问题,但我们可以做得更好。
利用 Websocket
我之前提到过,Sails 利用 websockets 的双向功能来发布订阅数据的更新。使用这些更新,我们可以侦听消息表中的新添加内容并相应地更新集合。
因此,在 chat.ejs 文件中,让我们创建一种新的集合; SailsCollection:
var SailsCollection = Backbone.Collection.extend({ sailsCollection: "", socket: null, sync: function(method, model, options){ var where = {}; if (options.where) { where = { where: options.where } } if(typeof this.sailsCollection === "string" && this.sailsCollection !== "") { this.socket = io.connect(); this.socket.on("connect", _.bind(function(){ this.socket.request("/" + this.sailsCollection, where, _.bind(function(users){ this.set(users); }, this)); this.socket.on("message", _.bind(function(msg){ var m = msg.uri.split("/").pop(); if (m === "create") { this.add(msg.data); } else if (m === "update") { this.get(msg.data.id).set(msg.data); } else if (m === "destroy") { this.remove(this.get(msg.data.id)); } }, this)); }, this)); } else { console.log("Error: Cannot retrieve models because property 'sailsCollection' not set on the collection"); } } });
现在可能很长,但实际上很简单,让我们来看看。我们首先向 Collection 对象添加两个新属性,一个用于保存 Sails“模型”的名称,另一个用于保存 Web 套接字。接下来我们修改sync函数,如果你熟悉Backbone,那么你就会知道,当你调用fetch等函数时,这个函数就是与服务器接口的函数。通常,它会触发 Ajax 请求,但我们将对其进行自定义以进行套接字通信。
现在,我们没有使用 sync 函数提供的大部分功能,主要是因为我们没有添加用户更新或删除消息的功能,但为了完整起见,我将它们包含在函数定义。
我们来看看sync函数的第一部分:
var where = {}; if (options.where) { where = { where: options.where } }
此代码首先检查是否发送了任何“where”子句,这将允许您执行以下操作: messages.fetch({ where : { id: 4 } }); 仅获取其中的行id 等于 4。
之后,我们有一些代码来确保已设置 ‘sailsCollection’ 属性,否则我们会记录一条错误消息。然后,我们创建一个新的套接字并连接到服务器,通过 on(‘connect’) 事件监听连接。
连接后,我们请求指定的“sailsCollection”索引以拉入当前模型列表。当它接收到数据时,我们使用集合的 set 函数来初始设置模型。
好吧,到目前为止,我们已经有了相当于标准 fetch 命令的命令。下一个代码块是推送通知发生的地方:
this.socket.on("message", _.bind(function(msg){ var m = msg.uri.split("/").pop(); if (m === "create") { this.add(msg.data); } else if (m === "update") { this.get(msg.data.id).set(msg.data); } else if (m === "destroy") { this.remove(this.get(msg.data.id)); } }, this));
现在正在执行的操作(无论是创建、更新还是销毁消息)都可以在实际的 msg 内部找到,然后该操作又在 uri 内部。为了获取操作,我们用正斜杠(“/”)分割 URI,并使用 pop 函数仅获取最后一段。然后,我们尝试将其与 create、update 或 destroy 三个可能的操作相匹配。
剩下的就是标准的Backbone,我们可以添加、编辑或删除指定的模型。随着我们的新类即将完成,剩下要做的就是更改当前的 MessageCollection。它需要扩展我们的新集合,而不是扩展 Backbone 集合,如下所示:
var MessageCollection = SailsCollection.extend({ sailsCollection: 'messages', model: MessageModel });
除了扩展我们的新集合之外,我们还将进行另一项更改,以便我们现在设置 sailsCollection 属性,而不是设置 URL 属性。这就是全部内容。在两个不同的浏览器(例如 Chrome 和 Safari)中打开应用程序并注册两个单独的用户。您应该看到从任一浏览器发布的消息都会立即显示在另一个浏览器上,无需轮询,没有麻烦。
结论
Sails 是在杂乱的框架中呼吸的新鲜空气。它在门口检查自己的自我,并尽其所能帮助开发商而不是品牌。我一直在与 Sails 开发人员聊天,我可以告诉您,还有更多令人惊叹的作品正在开发中,看看这个框架的发展方向将会很有趣。
总之,您已经了解了如何在 Sails 中设置、使用和保护数据,以及如何将其与流行的 Backbone 库连接。
与往常一样,如果您有任何意见,请随时在下面留言,或加入我们的 Nettuts+ IRC 频道(freenode 上的“#nettuts”)。感谢您的阅读。
本站部分资源来源于网络,仅限用于学习和研究目的,请勿用于其他用途。
如有侵权请发送邮件至1943759704@qq.com删除
码农资源网 » 使用Sails.js处理数据