Sails.js は、自由さとスマートなデフォルトに重点を置いた新興の Node.js フレームワークです。この記事では、複雑なアプリケーションを簡単にするために Sails が提供する、すぐに使えるデータ機能のいくつかを見ていきます。
Sails を選んだ理由は、Sails の作成者である Mike McNeil が最もよく言った言葉です。「Sails は必然的に作られたのです。」あなたが目にするフレームワークの多くは、ほとんどの場合、学術側向けに構築されており、これらのフレームワークは通常、ベスト プラクティスを推進し、開発者がより速く、より優れたものを作成するためのプラットフォームを作成します。
一方、Sails は実稼働用に作成されており、新しい構文やプラットフォームを提供しようとするものではなく、「クライアント作業」を迅速に作成するために設計された強固な基盤を提供しようとしています。コントラストは微妙かもしれませんが、いくつかの顕著な違いがあります。
私が何を言っているのかを説明するために、Meteor を見てみましょう。 Meteor は現在主要な JS プラットフォームかもしれませんが、フレームワークの代表的な例に過ぎません。それは悪いことではありません。私は Meteor の大支持者です。つまり、Meteor はフレームワークの構築に着手し、素晴らしい仕事をしました。一方、Mike は顧客の作業を高速化することに着手しました。帆は目的を達成するための手段にすぎません。
Meteor では、ほぼすべてが抽象化されており、JavaScript と Meteor API を使用してすべてをコーディングできます。そして、Sails は新しいプラットフォームではないため、何も隠されていません。
これは Socket.io と人気のある Express フレームワークの上に構築されており、ネイティブでそれらに完全にアクセスできます。違いが見え始めていますか?
さらに、Sails は実稼働を第一に考えているため、複数のスケーリングとセキュリティのオプションがあります。
話すべきことはたくさんありますが、この記事では、Sails がデータを処理する方法と、Sails のより高度な機能を利用して非常に優れた操作を実行する方法に焦点を当てたいと思います。
Socket.io と Express
Socket.io は、サーバーとクライアント上で実行され、Web ソケット経由で通信できるようにする pub/sub ライブラリです。
リーリー
このコードは、最初にsocket.io
ライブラリを必要とし、接続をリッスンし、次に別のソケットが接続すると、メッセージをwelcomeMessage イベントに送信し、最後に JSON を渡します。 .
次に、クライアント上で次のように記述します:
リーリー
welcomeMessage
イベントをリッスンします。ご覧のとおり、これは双方向の非常に単純なパブリッシュ/サブスクライブ サーバーです (クライアントはサーバーにメッセージを送信することもできます)。
次に、Express を見てみましょう:
###速達便###
リーリー
これは、ユーザーがサイトのアドレスにアクセスして"Hello from '/users' !" # が表示されるように、単純なルートを定義します。 ##。
Express は HTTP リクエストを処理するためのフレームワークであり、Socket.io は WebSocket 通信ライブラリです。 Sails チームが行ったことは、すべての Express ルートを内部で Socket.io にマッピングすることです。これは、Web ソケットを通じて任意の HTTP ルートを呼び出すことができることを意味します。
これはとてもクールです!しかし、パズルの 1 ピースがまだ欠けています。それは帆の設計図です。
Sails では、他のフレームワークと同じようにモデルを生成できます。ただし、モデルに合わせて本番環境に対応した RESTfull API も生成できる点が異なります。これは、「
」という名前のモデルを生成すると、コーディングなしで「
/users」リソースに対して RESTfull クエリをすぐに実行できることを意味します。
RESTful API に詳しくない方のために説明すると、RESTful API は、CRUD 操作がさまざまな HTTP メソッドにマップされているデータにアクセスする単なる方法です。
したがって、「/users
」への
リクエストはすべてのユーザーを取得し、
POST リクエストは新しいユーザーを作成します。
それでは、これは何を意味するのでしょうか?
これは、コードを 1 行も記述することなく、Sails 経由で完全な RESTfull API を 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
。< /p>
现在让我们填写用户的模型:
//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
',你会在中间看到一个对 <%- body %>
的调用,这是你的视图文件将被插入的地方。默认情况下,它使用“layout.ejs
”文件,但您只需将布局名称传递到名为“layout”的属性下的 res.view()
函数即可使用其他布局文件。例如: 'res.view( { layout: "other.ejs" } );
'。
我将使用默认布局文件并进行一些小调整,我将添加 jQuery、Backbone 和 Underscore。因此,在 'layout.ejs
' 文件中,在关闭 </head>
标记之前,添加以下行:
<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。唯一可能需要解释的是 <%= username %>
代码,这种编码风格并不是 Sails 特有的,它实际上是 EJS 的语法。这种语法与 PHP 的短标签非常相似。 <%
相当于 PHP 中的 <?
,<%=
与 <?=
相同。 EJS 的第一个片段允许您在页面上集成标准 JS 代码,而第二个片段则打印出其中的代码。这里我们只是打印出从控制器传入的用户名。
我们的聊天功能的其余部分将全部由 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 间隔并轮询更新来解决此问题,但我们可以做得更好。
我之前提到过,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”)。感谢您的阅读。
以上がSails.js を使用したデータの処理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。