摘要:鑒于此目的,我決定快速構(gòu)建一個用于此目的的問卷調(diào)查應(yīng)用程序。這將啟動一個服務(wù)器并將應(yīng)用程序部署到該服務(wù)器。圖應(yīng)用程序配置基礎(chǔ)前端這個問卷調(diào)查應(yīng)用程序?qū)ΤR娪脩艚缑婧筒季质褂昧丝蚣堋?/p>
全棧教程。轉(zhuǎn)自 使用 Node.js、Express、AngularJS 和 MongoDB 構(gòu)建一個實(shí)時問卷調(diào)查應(yīng)用程序
最近,在向大學(xué)生們介紹 HTML5 的時候,我想要對他們進(jìn)行問卷調(diào)查,并向他們顯示實(shí)時更新的投票結(jié)果。鑒于此目的,我決定快速構(gòu)建一個用于此目的的問卷調(diào)查應(yīng)用程序。我想要一個簡單的架構(gòu),不需要太多不同的語言和框架。因此,我決定對所有一切都使用 JavaScript — 對服務(wù)器端使用 Node.js 和 Express,對數(shù)據(jù)庫使用 MongoDB,對前端用戶界面使用 AngularJS。
這個 MEAN 堆棧(Mongo、Express、Angular 和 Node)只需要一天即可完成,遠(yuǎn)比 Web 應(yīng)用程序開發(fā)和部署所用的 LAMP 堆棧(Linux、Apache、MySQL 和 PHP)簡單得多。
運(yùn)行該應(yīng)用程序
在 JazzHub 上獲取源代碼
我選擇使用 JazzHub 來管理我的項(xiàng)目的源代碼。它不僅為我的代碼提供了一個完整的版本控制系統(tǒng),還為在云中編輯代碼提供了一個在線 IDE,以及用于項(xiàng)目管理的敏捷特性。JazzHub 很容易與 Eclipse 相集成,Eclipse 還提供了一些插件,支持對平臺( 比如 Bluemix 或 Cloud Foundry)的一鍵式部署。
構(gòu)建該應(yīng)用程序的先決條件基本了解 Node.js 和 Node.js 開發(fā)環(huán)境
具有以下這些 Node.js 模塊:Express framework、Jade、Mongoose 和 socket.io
AngularJS JavaScript 框架
MongoDB NoSQL 數(shù)據(jù)庫
Eclipse IDE,已安裝了 Nodeclipse 插件
第 1 步. 構(gòu)建一個基礎(chǔ) Express 后臺在 Eclipse 中,切換到 Node 透視圖,并創(chuàng)建一個新的 Node Express 項(xiàng)目。如果您創(chuàng)建了一個 JazzHub 項(xiàng)目,請像我所做的那樣,使用相同的名稱為您的 Node Express 項(xiàng)目命名。選擇使用 Jade 作為模板引擎。Eclipse 會自動下載所需的 npm 模塊,以便創(chuàng)建一個簡單 Express 應(yīng)用程序。
運(yùn)行這個 Express 應(yīng)用程序在 Project Explorer 中,找到位于您項(xiàng)目的根目錄中的 app.js,右鍵單擊并選擇 Run As > Node Application。這將啟動一個 Web 服務(wù)器并將應(yīng)用程序部署到該服務(wù)器。
接下來,打開瀏覽器并導(dǎo)航到 http://localhost:3000。
這個問卷調(diào)查應(yīng)用程序?qū)ΤR娪脩艚缑婧筒季质褂昧?Bootstrap 框架?,F(xiàn)在,讓我們對 Express 應(yīng)用程序做一些改動來反映這一點(diǎn)。首先,打開 routes/index.js,將標(biāo)題屬性更改為 Polls:
exports.index = function(req, res){ res.render("index", { title: "Polls" }); };
接著,更改 views/index.jade 模板以包含 Bootstrap。Jade 是一種速記模板語言,可編譯成 HTML。它使用縮進(jìn)消除了對結(jié)束標(biāo)簽的需求,極大地縮小了模板的大小。您只需要使用 Jade 作為主頁面布局即可。在下一步中,還可以使用 Angular 局部模板向這個頁面添加功能。
doctype 5 html(lang="en") head meta(charset="utf-8") meta(name="viewport", content="width=device-width, initial-scale=1, user-scalable=no") title= title link(rel="stylesheet", href="http://netdna.bootstrapcdn.com/bootstrap/3.0.1/ css/bootstrap.min.css") link(rel="stylesheet", href="/stylesheets/style.css") body nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") div.navbar-header a.navbar-brand(href="#/polls")= title div.container div
想要查看對您的應(yīng)用程序所做的更改,請結(jié)束 Eclipse 中的 Web 服務(wù)器進(jìn)程,再次運(yùn)行 app.js 文件:
注意:在使用 Jade 模板時,注意適當(dāng)縮進(jìn)您的代碼,否則您會遇到麻煩。另外,還要避免使用混合縮進(jìn)樣式,如果您嘗試這樣做,Jade 將會報錯。
第 2 步. 使用 AngularJS 提供前端用戶體驗(yàn)如果要使用 Angular,首先需要在您的 HTML 頁面中包含它,還需要在 HTML 頁面中添加一些指令。在 views/index.jade 模板中,對 html 元素進(jìn)行如下更改:
html(lang="en", ng-app="polls")。
在該文件的標(biāo)頭中添加以下腳本元素:
script(src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js") script(src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8 /angular-resource.min.js")
接下來,更改模板中的 body 元素,添加一個 ng-controller 屬性(稍后使用該屬性將用戶界面綁定到控制器邏輯代碼中):
body(ng-controller="PollListCtrl")。
最后,更改模板中的最后一個 div 元素,以便包含一個 ng-view 屬性:div(ng-view)。
構(gòu)建 Angular 模塊Angular 中令人影響較為深刻的特性就是數(shù)據(jù)綁定,在后臺模型發(fā)生改變時,該功能會自動更新您的視圖。這極大地減少了需要編寫的 JavaScript 的數(shù)量,因?yàn)樗鼘α鑱y的 DOM 操作任務(wù)進(jìn)行了抽象。
在默認(rèn)情況下,Express 發(fā)布了靜態(tài)資源,比如 JavaScript 源文件、CSS 樣式表以及位于您項(xiàng)目的公共目錄中的圖像。在公共目錄中,創(chuàng)建一個名為 javascripts 的新的子目錄。在這個子目錄中,創(chuàng)建一個名為 app.js 的文件。該文件將包含用于應(yīng)用程序的 Angular 模塊,而且還定義了用于用戶界面的路由和模板:
angular.module("polls", []) .config(["$routeProvider", function($routeProvider) { $routeProvider. when("/polls", { templateUrl: "partials/list.html", controller: PollListCtrl }). when("/poll/:pollId", { templateUrl: "partials/item.html", controller: PollItemCtrl }). when("/new", { templateUrl: "partials/new.html", controller: PollNewCtrl }). otherwise({ redirectTo: "/polls" }); }]);
Angular 控制器定義了應(yīng)用程序的范圍,為要綁定的視圖提供數(shù)據(jù)和方法。
// Managing the poll list function PollListCtrl($scope) { $scope.polls = []; } // Voting / viewing poll results function PollItemCtrl($scope, $routeParams) { $scope.poll = {}; $scope.vote = function() {}; } // Creating a new poll function PollNewCtrl($scope) { $scope.poll = { question: "", choices: [{ text: "" }, { text: "" }, { text: "" }] }; $scope.addChoice = function() { $scope.poll.choices.push({ text: "" }); }; $scope.createPoll = function() {}; }創(chuàng)建局部 HTML 和模板
為了呈現(xiàn)來自控制器的數(shù)據(jù),Angular 使用了局部 HTML 模板,該模板允許您使用占位符和表達(dá)式來包含數(shù)據(jù)和執(zhí)行操作,比如條件和迭代操作。在公共目錄中,創(chuàng)建一個名為 partials 的新的子目錄。我們將為我們的應(yīng)用程序創(chuàng)建 3 個局部模板,第一個局部模板將會展示可用投票的列表,我們將使用 Angular 通過一個搜索字段輕松過濾該列表。
Poll List
- No polls in database. Would you like to create one?
第二個局部模板允許用戶查看投票。它使用 Angular 切換指令來確定用戶是否已投票,并根據(jù)這些判斷,顯示一個就此次問卷調(diào)查進(jìn)行投票的表格,或者一個包含顯示問卷調(diào)查結(jié)果的圖表。
View Poll
Question
{{poll.question}}Please select one of the following options.
{{choice.text}}
{{choice.votes.length}} {{poll.totalVotes}} votes counted so far. You voted for {{poll.userChoice.text}} .
第三個也是最后一個局部模板定義了支持用戶創(chuàng)建新的問卷調(diào)查的表單。它要求用戶輸入一個問題和三個選項(xiàng)。提供一個按鈕,以便允許用戶添加額外的選項(xiàng)。稍后,我們將驗(yàn)證用戶至少輸入了兩個選項(xiàng) — 因?yàn)槿绻麤]有幾個選項(xiàng),就不能稱之為問卷調(diào)查。
Create New Poll
最后,為了顯示結(jié)果,我們需要向 style.css 添加一些 CSS 聲明。將該文件的內(nèi)容替換為以下內(nèi)容:
body { padding-top: 50px; } .result-table { margin: 20px 0; width: 100%; border-collapse: collapse; } .result-table td { padding: 8px; } .result-table > tbody > tr > td:first-child { width: 25%; max-width: 300px; text-align: right; } .result-table td table { background-color: lightblue; text-align: right; }
此時,如果您運(yùn)行該應(yīng)用程序,就會看到一個空的問卷調(diào)查列表。如果您試著創(chuàng)建一個新的問卷調(diào)查,就能看到此表單并添加更多的選項(xiàng),但您不能保存該問卷調(diào)查。我們將在下一步中詳細(xì)介紹所有這些內(nèi)容。
第 3 步. 使用 Mongoose 在 MongoDB 中存儲數(shù)據(jù)為了存儲數(shù)據(jù),該應(yīng)用程序使用了 MongoDB 驅(qū)動程序和 Mongoose npm 模塊。它們允許應(yīng)用程序與 MongoDB 數(shù)據(jù)庫進(jìn)行通信。要獲得這些模塊,請打該應(yīng)用程序根目錄中的 package.json 文件,并在依賴關(guān)系部分中添加以下這些代碼行:。
"mongodb": ">= 1.3.19", "mongoose": ">= 3.8.0",
保存文件,在 Project Explorer 中右鍵單擊并選擇 Run As > npm install。這將安裝 npm 模塊和其他所有依賴關(guān)系。
創(chuàng)建一個 Mongoose 模型在您應(yīng)用程序的名為 models 的根目錄中創(chuàng)建一個新的子目錄,并在這個子目錄中創(chuàng)建一個名為 Poll.js 的新文件。在這個文件中,我們定義了我們的 Mongoose 模型,該模型將用于查詢數(shù)據(jù),并以結(jié)構(gòu)化數(shù)據(jù)的形式將這些數(shù)據(jù)保存到 MongoDB 。
var mongoose = require("mongoose"); var voteSchema = new mongoose.Schema({ ip: "String" }); var choiceSchema = new mongoose.Schema({ text: String, votes: [voteSchema] }); exports.PollSchema = new mongoose.Schema({ question: { type: String, required: true }, choices: [choiceSchema] });定義數(shù)據(jù)存儲的 API 路由
接下來,在您應(yīng)用程序的根目錄下的 app.js 文件中設(shè)置一些路由,以便創(chuàng)建一些 JSON 端點(diǎn),這些端點(diǎn)可用于根據(jù) Angular 客戶端代碼來查詢和更新 MongoDB。找到 app.get("/", routes.index) 行,并將下列代碼添加到這一行的后面:
:
app.get("/polls/polls", routes.list); app.get("/polls/:id", routes.poll); app.post("/polls", routes.create);
現(xiàn)在,您需要實(shí)現(xiàn)這些功能。將 routes/index.js 文件的內(nèi)容替換為下列代碼:
var mongoose = require("mongoose"); var db = mongoose.createConnection("localhost", "pollsapp"); var PollSchema = require("../models/Poll.js").PollSchema; var Poll = db.model("polls", PollSchema); exports.index = function(req, res) { res.render("index", {title: "Polls"}); }; // JSON API for list of polls exports.list = function(req, res) { Poll.find({}, "question", function(error, polls) { res.json(polls); }); }; // JSON API for getting a single poll exports.poll = function(req, res) { var pollId = req.params.id; Poll.findById(pollId, "", { lean: true }, function(err, poll) { if(poll) { var userVoted = false, userChoice, totalVotes = 0; for(c in poll.choices) { var choice = poll.choices[c]; for(v in choice.votes) { var vote = choice.votes[v]; totalVotes++; if(vote.ip === (req.header("x-forwarded-for") || req.ip)) { userVoted = true; userChoice = { _id: choice._id, text: choice.text }; } } } poll.userVoted = userVoted; poll.userChoice = userChoice; poll.totalVotes = totalVotes; res.json(poll); } else { res.json({error:true}); } }); }; // JSON API for creating a new poll exports.create = function(req, res) { var reqBody = req.body, choices = reqBody.choices.filter(function(v) { return v.text != ""; }), pollObj = {question: reqBody.question, choices: choices}; var poll = new Poll(pollObj); poll.save(function(err, doc) { if(err || !doc) { throw "Error"; } else { res.json(doc); } }); };使用 Angular 服務(wù)將數(shù)據(jù)綁定到前端
此時,設(shè)置后臺,以便啟用查詢,并將問卷調(diào)查數(shù)據(jù)保存到數(shù)據(jù)庫,但我們需要在 Angular 中做一些更改,以便讓它知道如何與數(shù)據(jù)庫進(jìn)行通信。使用 Angular 服務(wù)很容易完成這項(xiàng)任務(wù),它將與服務(wù)器端進(jìn)行通信的過程包裝到了簡單的函數(shù)調(diào)用中:
angular.module("pollServices", ["ngResource"]). factory("Poll", function($resource) { return $resource("polls/:pollId", {}, { query: { method: "GET", params: { pollId: "polls" }, isArray: true } }) });
在創(chuàng)建的這一文件之后,您需要將它包括在您的 index.jade 模板中。在文件標(biāo)頭部分的最后一個腳本元素的下面添加以下這行代碼:
script(src="/javascripts/services.js")。
您還需要告訴您的 Angular 應(yīng)用程序使用這個服務(wù)模塊。要實(shí)現(xiàn)這一點(diǎn),請打開 public/javascripts/app.js 并將第一行更改為可讀,如下所示:
angular.module("polls", ["pollServices"])。
最后,更改 Angular 控制器,以便使用該服務(wù)在數(shù)據(jù)庫中進(jìn)行查詢和存儲問卷調(diào)查數(shù)據(jù)。在 public/javascripts/controllers.js 文件中,將 PollListCtrl 更改如下:。
function PollListCtrl($scope, Poll) { $scope.polls = Poll.query(); } ...
更新 PollItemCtrl 函數(shù),以便根據(jù)問卷調(diào)查的 ID 來查詢某個問卷調(diào)查:
... function PollItemCtrl($scope, $routeParams, Poll) { $scope.poll = Poll.get({pollId: $routeParams.pollId}); $scope.vote = function() {}; } ...
類似地,更改 PollNewCtrl 函數(shù),以便在提交表單時將新調(diào)查數(shù)據(jù)發(fā)送到服務(wù)器。
function PollNewCtrl($scope, $location, Poll) { $scope.poll = { question: "", choices: [ { text: "" }, { text: "" }, { text: "" }] }; $scope.addChoice = function() { $scope.poll.choices.push({ text: "" }); }; $scope.createPoll = function() { var poll = $scope.poll; if(poll.question.length > 0) { var choiceCount = 0; for(var i = 0, ln = poll.choices.length; i < ln; i++) { var choice = poll.choices[i]; if(choice.text.length > 0) { choiceCount++ } } if(choiceCount > 1) { var newPoll = new Poll(poll); newPoll.$save(function(p, resp) { if(!p.error) { $location.path("polls"); } else { alert("Could not create poll"); } }); } else { alert("You must enter at least two choices"); } } else { alert("You must enter a question"); } }; }運(yùn)行應(yīng)用程序
您已經(jīng)離成功不遠(yuǎn)了!此時,應(yīng)用程序應(yīng)該允許用戶查看和搜索問卷調(diào)查數(shù)據(jù)、創(chuàng)建新的問卷調(diào)查并查看單個問卷調(diào)查的投票選項(xiàng)。在運(yùn)行該應(yīng)用程序之前,請確保您已經(jīng)本地運(yùn)行 MongoDB。這通常和打開一個終端或命令提示符以及運(yùn)行 mongod 命令一樣簡單。確保在您運(yùn)行應(yīng)用程序時終端窗口處于打開狀態(tài):
在運(yùn)行應(yīng)用程序之后,在您的瀏覽器中導(dǎo)航到 http://localhost:3000 并創(chuàng)建一些問卷調(diào)查。如果您單擊一個問卷調(diào)查,您就能夠看到可用的選項(xiàng),但是,您無法實(shí)際對該問卷調(diào)查進(jìn)行投票,或者暫時看不到問卷調(diào)查結(jié)果。我們將在下一步和最后一步中對此進(jìn)行介紹。
第 4 步. 使用 Socket.io 進(jìn)行實(shí)時投票Web Sockets 允許服務(wù)器端直接與客戶端通信以及向客戶端發(fā)送消息。
剩下的惟一需要構(gòu)建的特性就是投票功能。該應(yīng)用程序允許用戶進(jìn)行投票,在他們投票后,會在所有已連接的客戶端上實(shí)時更新投票結(jié)果。使用 socket.io 模塊很容易完成這項(xiàng)工作,現(xiàn)在就讓我們來實(shí)現(xiàn)它吧。
打開您應(yīng)用程序的根目錄中的 package.json 文件,將下列代碼添加到依賴關(guān)系部分:
"socket.io": "~0.9.16"。
保存文件,在 Package Explorer 中右鍵單擊,然后選擇 Run As
> npm install 來安裝 npm 模塊。
接下來,打開應(yīng)用程序根目錄中的 app.js 文件, 刪除位于文件末尾的 server.listen... 代碼塊,將其替換為:
var server = http.createServer(app); var io = require("socket.io").listen(server); io.sockets.on("connection", routes.vote); server.listen(app.get("port"), function(){ console.log("Express server listening on port " + app.get("port")); });
接下來,修改 index.jade 模塊以包含 socket.io 客戶端庫。在運(yùn)行該應(yīng)用程序時,該庫會自動在指定的位置上變得可用,因此不需要擔(dān)心自己如何尋找該文件。確保想包含模板中的 angular-resource 庫的行的后面包含此文件:
script(src="/socket.io/socket.io.js")。
最后,您需要創(chuàng)建投票功能,以便在用戶向 socket.io 發(fā)送消息時保存新的投票,并在具有更新結(jié)果時將消息發(fā)送給所有客戶端。將 添加到路由目錄中的 index.js 文件的結(jié)尾處:
// Socket API for saving a vote exports.vote = function(socket) { socket.on("send:vote", function(data) { var ip = socket.handshake.headers["x-forwarded-for"] ||
socket.handshake.address.address;
Poll.findById(data.poll_id, function(err, poll) {
var choice = poll.choices.id(data.choice);
choice.votes.push({ ip: ip });
poll.save(function(err, doc) {
var theDoc = {
question: doc.question, _id: doc._id, choices: doc.choices,
userVoted: false, totalVotes: 0
};
for(var i = 0, ln = doc.choices.length; i < ln; i++) {
var choice = doc.choices[i];
for(var j = 0, jLn = choice.votes.length; j < jLn; j++) {
var vote = choice.votes[j];
theDoc.totalVotes++;
theDoc.ip = ip;
if(vote.ip === ip) {
theDoc.userVoted = true;
theDoc.userChoice = { _id: choice._id, text: choice.text };
}
}
}
socket.emit("myvote", theDoc);
socket.broadcast.emit("vote", theDoc);
});
});
});
};
注意:如果您想知道為什么應(yīng)用程序會在常規(guī) API 地址屬性的前面查找標(biāo)頭 "x-forwarded-for",因?yàn)檫@將確保當(dāng)應(yīng)用程序被部署到負(fù)載平衡環(huán)境中時,所使用的是正確的客戶端 IP。如果您將該應(yīng)用程序部署到 Bluemix 或 Cloud Foundry,這對于應(yīng)用程序是否能正常工作至關(guān)重要。
添加一個 Angular 服務(wù)將數(shù)據(jù)發(fā)送到 Web 套接字。Web Sockets 的后端功能現(xiàn)已創(chuàng)建完畢。目前剩下要做的工作是綁定前端,以發(fā)送和監(jiān)聽套接字事件。最佳方法是添加一個新的 Angular 服務(wù)。使用以下代碼替換 public/javascripts 文件夾中的 services.js 文件:
angular.module("pollServices", ["ngResource"]). factory("Poll", function($resource) { return $resource("polls/:pollId", {}, { query: { method: "GET", params: { pollId: "polls" }, isArray: true } }) }). factory("socket", function($rootScope) { var socket = io.connect(); return { on: function (eventName, callback) { socket.on(eventName, function () { var args = arguments; $rootScope.$apply(function () { callback.apply(socket, args); }); }); }, emit: function (eventName, data, callback) { socket.emit(eventName, data, function () { var args = arguments; $rootScope.$apply(function () { if (callback) { callback.apply(socket, args); } }); }) } }; });
最后,您需要編輯 PollItemCtrl 控制器,以便它能夠監(jiān)聽和發(fā)送用于投票的 Web Socket 消息。將原始控制器替換為:
function PollItemCtrl($scope, $routeParams, socket, Poll) { $scope.poll = Poll.get({pollId: $routeParams.pollId}); socket.on("myvote", function(data) { console.dir(data); if(data._id === $routeParams.pollId) { $scope.poll = data; } }); socket.on("vote", function(data) { console.dir(data); if(data._id === $routeParams.pollId) { $scope.poll.choices = data.choices; $scope.poll.totalVotes = data.totalVotes; } }); $scope.vote = function() { var pollId = $scope.poll._id, choiceId = $scope.poll.userVote; if(choiceId) { var voteObj = { poll_id: pollId, choice: choiceId }; socket.emit("send:vote", voteObj); } else { alert("You must select an option to vote for"); } }; }查看最終產(chǎn)品
問卷調(diào)查應(yīng)用程序現(xiàn)已創(chuàng)建完成。確保 mongod 仍在運(yùn)行,并在 Eclipse 中再次運(yùn)行 Node 應(yīng)用程序。在瀏覽器中輸入 http://localhost:3000,導(dǎo)航到一個問卷調(diào)查并進(jìn)行投票。隨后您就可以看到結(jié)果。要查看實(shí)時更新,請找到您的本地 IP 地址,并用該地址替換 localhost。在您的局域網(wǎng)中,使用不同的機(jī)器(甚至智能手機(jī)或平板電腦也可以)導(dǎo)航到這個地址。當(dāng)您在另一個設(shè)備上進(jìn)行投票時,結(jié)果會顯示在該設(shè)備上,而且會自動發(fā)布到您的主要計(jì)算機(jī)瀏覽器上:
您剛才創(chuàng)建的這個問卷調(diào)查應(yīng)用程序是一個不錯的起點(diǎn),但還有很大的改進(jìn)空間。在計(jì)劃創(chuàng)建這類應(yīng)用程序時,我喜歡使用一種敏捷方法來定義用戶案例,并將項(xiàng)目劃分為幾塊來實(shí)現(xiàn)。對于這個項(xiàng)目,我使用了 JazzHub,通過將項(xiàng)目的附屬代碼和源代碼一起保存在一個云托管的存儲庫中,JazzHub 使得開發(fā)變得非常簡單。
如果您對您的應(yīng)用程序感到很滿意,下一步就是跟全世界的人分享它。在過去,即使部署一個非常簡單的應(yīng)用程序,可能也會是一場噩夢,但值得慶幸的是,那些日子已經(jīng)一去不復(fù)返了。使用 IBM 新興的兼容 Cloud Foundry 的 Bluemix 平臺,您只需幾分鐘就可以通過最少的配置將您的應(yīng)用程序部署到云中,一點(diǎn)都不麻煩。
結(jié)束語這對于開發(fā)人員,現(xiàn)在是一個很好的時機(jī)。我們手頭有大量框架和工具,它們使得開發(fā)大量應(yīng)用程序不僅更簡單、更快速,而且更加令人感到愉快。在本文中,您學(xué)習(xí)了如何使用被稱為 MEAN 體系結(jié)構(gòu)(Mongo、Express、Angular 和Node)的技術(shù)構(gòu)建一個應(yīng)用程序。該堆棧可能只需要一天時間就可以完成任務(wù),遠(yuǎn)遠(yuǎn)超過了 LAMP 體系結(jié)構(gòu)(Linux、Apache、MySQL 和 PHP),在 Web 應(yīng)用程序開發(fā)和部署方面,該體系結(jié)構(gòu)也許同樣會超越 LAMP 體系結(jié)構(gòu)。對我而言,我已經(jīng)迫不及待躍躍欲試了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/18745.html
摘要:自年發(fā)布以來,走過了漫長的道路。一下子,工程師認(rèn)為自己不只是前端開發(fā)者了。這種趨勢被稱為全棧的或純的解決方案??梢哉J(rèn)為它是文檔結(jié)構(gòu)的數(shù)據(jù)庫,而不是由行列表組成的數(shù)據(jù)庫。也是高度可測試的,這是很重要的。 JavaScript自1995年發(fā)布以來,走過了漫長的道路。已經(jīng)有了幾個主要版本的ECMAScript規(guī)范,單頁Web應(yīng)用程序也慢慢興起,還有支持客戶端的JavaScript框架。作為一...
摘要:一個標(biāo)準(zhǔn)性的事件就是年的橫空出世。引擎快速處理能力和異步編程風(fēng)格,讓開發(fā)者從多線程中解脫了出來。其次,通過異步編程范式將其高并發(fā)的能力發(fā)揮的淋漓盡致。它也僅僅是一個處理請求并作出響應(yīng)的函數(shù),并無任何特殊之處。 showImg(https://segmentfault.com/img/remote/1460000010819116); 在正式學(xué)習(xí) Express 內(nèi)容之前,我們有必要從大...
摘要:年新星調(diào)查中顯示,越來越流行,其熱度已經(jīng)逐漸超過了。及其框架位于全球最受歡迎使用最廣泛的技術(shù)榜榜首。本文轉(zhuǎn)載自框架的游戲年流行趨勢英文原文JavaScript 生態(tài)系統(tǒng)復(fù)雜多變,各種框架讓人眼花繚亂。究竟孰優(yōu)孰劣,如今的發(fā)展趨勢是怎樣的,用人單位又需要怎樣的人才?本文站在一個中立者的角度,客觀分析了當(dāng)前這場框架的游戲中,JavaScript 的流行趨勢。 Javascript 的生態(tài)環(huán)境讓我...
摘要:我所在的美團(tuán)酒店事業(yè)部去年月份成立,新的業(yè)務(wù)新的開發(fā)團(tuán)隊(duì),這一切使得我們的前后端分離推進(jìn)的很徹底。日志監(jiān)控平臺日志監(jiān)控平臺是美團(tuán)內(nèi)部的一個日志收集系統(tǒng),目前美團(tuán)統(tǒng)一使用收集日志,具有接收格式日志的能力,而日志監(jiān)控平臺也是以格式日志來收集。 轉(zhuǎn)自:美團(tuán)技術(shù)團(tuán)隊(duì) 作者:美團(tuán)技術(shù)團(tuán)隊(duì) 分享理由:很好的分享,可見,基于Node的前后端分離的架構(gòu)是越顯流行和重要,前端攻城獅們,No...
摘要:選擇是因?yàn)樗唵危m合高并發(fā)的服務(wù),而且我們的開發(fā)人員能夠熟練使用它,關(guān)于的優(yōu)缺點(diǎn)我在知乎上也曾經(jīng)回答過使用的優(yōu)勢和劣勢都有哪些。 其實(shí)早就該寫這篇博客了,一直說忙于工作沒有時間,其實(shí)時間擠擠總會有的,可能就是因?yàn)閼邪?!?013年11月一直拖到現(xiàn)在,其實(shí)我是不怎么擅長寫技術(shù)博客的,因?yàn)樯蠈W(xué)的時候語文不是很好,每次寫作文都不知道自己在寫啥,作為一開始就參與 Worktile 開發(fā)的技術(shù)...
閱讀 1214·2021-11-24 09:39
閱讀 3683·2021-09-02 15:21
閱讀 2227·2021-08-24 10:01
閱讀 776·2021-08-19 10:55
閱讀 2496·2019-08-30 15:55
閱讀 1277·2019-08-30 14:16
閱讀 3076·2019-08-29 15:17
閱讀 3299·2019-08-29 13:53