使用Express MongoDB開發一個完整MVC專案
先決條件
這個教程需要使用以下技術:
以及良好的網路條件,基礎的HTML,CSS和Javascript技能是完成教程的必備的。
c9使用者可以看這裡
如果你是使用,例如c9,你就不需要安裝Node,要安裝MongoDB,在命令列輸入$ sudo apt-get install mongodb-org
,安裝完成後,你需要輸入以下的命令來執行MongoDB:
$mongod --smallfiles
這讓mongod
服務執行起來,--smallfiles
引數是MongoDB預設使用小檔案模式,對於IDE有檔案限制來說,這個命令就很重要。
docs_c9_clemjs_setup04
之後,點選圖中的+
,開啟新的命令列介面,確保MongoDB在後臺執行,每次在重新開啟c9的時候都要記得執行mongodb。
最後,大部分的教程使用1ocalhost:3000
開啟,c9會給出一個特定的連結格式如下:替代
。另外c9使用
8080
埠代替3000
埠。
安裝Node.js以及NPM
提醒:Node會隨著NPM一起安裝。
MAC OSX&Windows
進入。下載相應的檔案按照指導完成安裝。
Linux
選項1-透過PPA安裝
sudo add-apt-repository ppa:chris-lea/node.js sudo apt-get update sudo apt-get install node.js
選項2-透過LinuxBrew
首先,確定已經安裝,之後,輸入下面的命令:
$ brew install node
安裝MongoDB
MongoDB的安裝可以看。
NPM包安裝
以上步驟完成後,下一步就是安裝程式要使用的Node包檔案。
第一步就是建立package.json
檔案,這是一個存放所有和程式有關的資訊檔案的集合。
在命令列介面輸入$ npm init
,經過一系列的問題之後,在專案根目錄下就生成了package.json
檔案。如果你不知道這些問題的答案,直接一路enter下去就行。下面是類似的檔案:
{ "name": "beginner-app", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "", "license": "MIT"}
你可以任意修改這個檔案,最重要的事情就是將main
對應為server.js
。
現在我們初始化NPM之後,我們開始來安裝NPM包。
在命令列介面輸入:$npm install express mongodb --save
這個命令用來安裝Express和MongoDB,之後你可能注意到出現一個新的資料夾叫node_modules
。這就是存放Node包的本地資料夾。
--save
是將包依賴內容新增到package.json
檔案。如果你開啟這個檔案,將會是以下的內容:
{ "name": "beginner-app", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "", "license": "MIT", "dependencies": { "express": "^4.12.4", "mongodb": "^2.0.33" } }
關於Express
Express是一個node框架,用來為node生成web程式提供更多的功能性。框架一般意味著使用另外的技術書寫,提供額外的功能。本質上,Express就是為Node提供了一系列非常有用的功能。
如果不使用Express,開發者不得不在每開始一個新的專案而寫重複的程式碼。另外,Express沒有自以為是的部署程式的實施。如果有要舉一個自以為是的框架名就是--Ember.js。
更多的資訊,檢視website and documentation。
關於MongoDB
MongoDB我們都知道是一個文件存放的資料庫,每一條記錄都存放在一個單獨的“文件“中,這種資料庫型別叫做NoSQL資料庫,是沒有適用SQL資料結構的資料庫。
SQL資料庫是資料庫的老字號。最基本的例子例如MySQL以及PostgreSQL。NoSQL資料庫是SQL資料庫的替代。
MongoDB是MEAN組合中的一部分,可以方便的使用Javascript語句運算元據庫。
更多的關於MongoDB的資料,可以看。如果你掌握了Node,我建議你學習這門。
MongoDB Node.js可以讓我們使用Node操作MongoDB資料庫。
.gitignore
我們經常會在專案的根目錄中看到.gitignore
檔案,這個檔案是告訴git(版本控制軟體)所要忽略的檔案。如果你沒有這個檔案,可以新建一個,使用$ touch .gitignore
命令新建,touch
命令是用來更新或者調整檔案,或者當檔案不存在的時候也能用來新建一個新的檔案。
很多時候,專案中包含了node_modules
資料夾。使用.gitignore
可以阻止向類似GitHub上傳這個資料夾。在.gitignore
檔案中新增:
node_modules/
建立檔案結構
現在讓我們花點時間看一下檔案結構。
+-- Project Folder +-- app | -- controllers | -- routes | +-- public | -- css | -- img
專案檔案或者根資料夾包含:
.gitignore檔案
package.json檔案
app資料夾包含:
controllers資料夾 —— 用來存放運算元據或者服務端的控制器。
routes資料夾 —— 這個檔案包含了根據路徑顯示不同內容的路由檔案。
public資料夾:
css —— 包含程式樣式
img —— 包含照片檔案
程式結構
在開始寫程式之前,最好我們對程式結構有一定的認識,要知道程式碼之間都是怎麼聯絡在一起的。我們現在使用的是MVC架構。
這是一種常見的網站程式架構。Model負責管理程式中的資料邏輯關係,View用來表現頁面,Controller在頁面和Model之間幫助管理資料,將最後的結果送給頁面。
這是圖例:
更多的關於MVC的內容可以看。
在這個教程中,我們準備建立一個具有以下功能的小型程式:
記錄按鈕被按下的次數
在資料庫中儲存
重新設定計數為0
在程式的內容中,我們要做到以下:
Node/Express網站服務用來響應HTTP要求,以及傳檔案給瀏覽器。
MongoDB資料庫用來儲存點選的次數。
服務端controller(控制器)可以新增,可以重設,可以接受來自資料庫的記錄
資料將會傳給API
客戶端控制器使用暴露的API能夠根據使用者的輸入響應不同的內容。
客戶端能都給使用者顯示介面
HTML頁面要包含一個logo以及使用者互動的按鈕。
以上就是簡單的網站內容。
簡單的Node伺服器
開始我們的程式,首先建立一個簡單的Node伺服器。這將是我們程式的基礎,根據這個基礎我們在上面加內容。
在專案根目錄新建server.js
檔案。在檔案中輸入:
'use strict';var express = require('express');var app = express(); app.get('/', function (req, res) { res.send('Hello world!'); }); app.listen(3000, function () { console.log('Listening on port 3000...'); });
我們現在來逐條分析程式碼:use strict
這個程式碼的意思就是開啟Javascript的"strict mode"。
var express = require('express');
這是node語法,意思是引用依賴Express。我們將其儲存成變數,以備之後的需要。
var app = express();
這段是將Express例項化,這樣可以使用app
這個變數獲得express的功能。Express有很多很有用的方法可以使網站程式更快實現。
app.get( ... )
是Express的一個方法,可以接受來自客戶端(browser)的要求和響應,使用res.send
可以傳送訊息給browser。這是在Express中常見的引數。
app.listen( ... )
是用來告訴Node要監聽的埠。例子中,我們使用了一個回撥函式,來告訴我們程式已經執行起來了。
現在我們開始測試程式。在命令列介面輸入$ node server.js
。你就能看到以下內容:
$ node server.js Listening on port 3000...
很好!
現在,開啟瀏覽器輸入localhost:3000
。在瀏覽器中你應該能看到Hello, world!
訊息。
現在我們需要在每次修改完Node伺服器後重啟,我們可以按在命令列介面按Ctrl+C
停止服務。輸入$ node server
開始。
我們來更近一步,給瀏覽器傳送一個HTML檔案。在專案資料夾,新建一個index.html
檔案。
檔案內容如下:
html>First Node App Hello, world!
這是一個極其簡單的HTML檔案,但重要的是我們在向瀏覽器傳送檔案,而不僅僅是訊息。
我們來更新server.js
檔案。
之前:
app.get('/', function (req, res) { res.send('Hello world!'); });
之後:
app.get('/', function (req, res) { res.sendFile(process.cwd() + '/index.html'); });
首先,我們告訴Exprss我們要傳送一個檔案給瀏覽器,之後我們告訴要傳送檔案的位置以及名字:res.sendFile(process.cwd() + '/index.html')
;Node的process.cwd()
方法是顯示現在的工作路徑,讓我們和檔案一起拼成字串。這樣讓Node能都找到檔案。
現在我們測試程式是否工作正常。在命令列介面$ node server.js
。
瀏覽器中開啟localhost:3000
,你應該能看到“Hello, world!“
配置路由
下一步是配置路由,路由是Express或者Node中最普遍的模式。網站程式中包含了大量的路由資訊(HTTP要求到server),一般要將歸類他們儲存在不同檔案中。這就是我們教程中目標其中之一。
從新建在/app/routes
資料夾中index.js
開始,這個檔案將存放我們的路由檔案。
刪除server.js
檔案中的這部分內容:
app.get('/', function (req, res) { res.sendFile(process.cwd() + '/index.html'); });
下一步,新增新的依賴到server.js
。
server.js:
'use strict';var express = require('express'), routes = require('./app/routes/index.js');var app = express(); ... ...
我們將路由檔案傳遞到routes
的函式物件。我們之後會暴露我們的路由,這個函式接受一個引數app
,這樣可以在routes
函式中使用Express中的方法內容。
程式碼顯示如下:
routes(app);
server.js
看起來如下:
'use strict';var express = require('express'), routes = require('./app/routes/index.js');var app = express(); routes(app); app.listen(3000, function () { console.log('Listening on port 3000...'); });
現在到了新增index.js
檔案的時候了。我們使用module.exports
方法將這個函式擴充套件到其他的Node檔案(例如server.js檔案)。這個函式接受一個引數(app),也就是Expres app。
index.js:
'use strict';module.exports = function (app) { app.route('/') .get(function (req, res) { res.sendFile(process.cwd() + '/public/index.html'); }); };
一些內容看起來有點眼熟,但是我們要知道我們使用的是Express的路由方法app.route
。這個是app.get的替代,將所有型別的routes綁到一個頁面中。剩下的程式碼和以前使用app.get
的程式碼一模一樣。
下一步,將index.html檔案放到/public
資料夾中。這將是這個檔案的固定位置了。
之後我們測試一下效果,執行$ node server.js
。
開啟瀏覽器中localhost:3000
,你能看到熟悉的`Hello, world!"。
下面我們新增點內容到HTML檔案當中。
新增元素到index.html檔案中
在這節中,我們要更新我們的HTML檔案,讓其包含更多的內容以及互動性。這是相關的程式碼:
html>Clementine.js - The elegant and lightweight full-stack boilerplate. [站外圖片上傳中……(6)]
Clementine.js
You have clicked the button times.
在HTML中,我們新增了一些內容,你可以根據你的喜好執行新增。如果你想要新增Clementine logo,可以從新增。把檔案放在`/public/img資料夾中。
HTML中包含兩個div
元素。上面的div中是一幅圖片和一段話,下面的div是包含一段話和兩個按鈕,一個是新增次數的按鈕,另外一個是重設的按鈕。我們的計劃就是將中的元素更新為按鈕點按的次數。
現在確認所有的內容都能執行良好。測試之後發現如下的介面:
clemjstut01
照片沒有被載入,好吧,當Node想要進入/public/img/
資料夾,沒有任何方式方法進入這個相對路徑。
我們可以在server.js
檔案中解決的方法:
'use strict'; ... app.use('/public', express.static(process.cwd() + '/public')); app.use('/controllers', express.static(process.cwd() + '/app/controllers')); routes(app); ...
這裡使用了Express的app.use
和express.static
將/public
繫結到了/public
。現在當/public
被引用的時候,node就知道檔案的地方了。
現在重啟服務,開啟localhost:3000
都顯示出來了。
連線MongoDB
在客戶端和資料庫之間傳遞資料要使用API。API是前端和資料之間的連線方式。
首先,我們先設定MongoDB的資料庫。在檔案server.js
中,我們要做一些修改。
'use strict';var express = require('express'), routes = require('./app/routes/index.js'), mongo = require('mongodb').MongoClient;var app = express(); mongo.connect('mongodb://localhost:27017/clementinejs', function (err, db) { if (err) { throw new Error('Database failed to connect!'); } else { console.log('MongoDB successfully connected on port 27017.'); } app.use('/public', express.static(process.cwd() + '/public')); app.use('/controllers', express.static(process.cwd() + '/app/controllers')); routes(app, db); app.listen(3000, function () { console.log('Listening on port 3000...'); }); });
這都是一些小的修改,最明顯的是,之前所有的程式碼都被mongo.connect
函式包圍。以及更上面的引入MongoDBrequire('mongodb').MongoClient
。mongoClient()
是可以使用類似connect
方法的物件。
同樣的,在使用資料庫之前,將Express例項化也是很重要的。例子中,mongo.connect之前我們例項化了express。
接下來,我們使用connect
方法連線資料庫和MongClient物件。其中,第一個引數是字串的形式,表示資料庫的地址,埠27017是MongoDB的預設使用埠,這個埠也能隨意更改。clementinejs
是資料庫的名稱,如果資料庫中不存在,MongoDB就會新建一個。
第二個引數是一個回撥函式,這個函式有兩個引數,一個是錯誤,另外一個是資料庫物件。
之後是連線資料庫的時候顯示錯誤,可以使用throw new Error(...)
丟擲錯誤。
如果沒有錯誤,就會在命令列顯示'MongoDB successfully connected on Port 27017'的內容。餘下的內容和之前都一樣。
routes(app, db);
和引數app
一樣,我們要向路由傳遞我們的資料庫物件。
現在可以測試程式是否執行正常了。一切正常的話,命令列就會顯示一條連線成功的訊息提示。
設定服務端控制器
就像客戶端控制器可以將資料在客戶端(瀏覽器)和API之間傳遞,我們同樣需要伺服器端的控制器傳遞資料。
這個控制器可以查詢資料,可以更新結果,而客戶端的控制器會直接在瀏覽器中更新API結果。
我們開始建造服務端的控制器。首先在/app/controllers
資料夾中新建clickHandler.server.js
檔案。
clickHandler.server.js:
'use strict';function clickHandler (db) { var clicks = db.collection('clicks'); this.getClicks = function (req, res) { var clickProjection = { '_id': false }; clicks.findOne({}, clickProjection, function (err, result) { if (err) { throw err; } res.json(result); }); }; }module.exports = clickHandler;
再一次,我們看見了一些熟悉的程式碼。首先定義了一個MongoDB collection。這樣就能在資料庫中使用。Collection就像是SQL資料庫中的表格,一個資料庫中可以有很多的collection。如果資料庫中沒有相應的collection,MongoDB會新建一個collection。
就像程式中那樣,我們的collection的名字就是clicks
,之後,我們要新建一個從資料庫中取得現在點選次數的方法。這個方法命名為getClicks()
。
現在我們來看看getClicks
方法中的內容:
function(req, res) —— 這和前面講的內容一樣,接受命令和響應作為函式引數的一個函式。
clicks —— 這是資料庫中collection的名稱,是我們上面透過
var clicks = ...
獲得的。var clickProjection ...
每一個mongodb資料庫中的document都有一個獨一無二的_id
,除非是自己指定,一般資料庫會自動生成,這樣我們可以透過id來獲取資料,我們先不去設定id,這個例子中,得出的結果中我們不想包含id內容,所以我們將id設定為false。.findOne —— 這是MongoDB獲取內容的方法。我們也能使用
find()
方法,鑑於我們的資料庫中只有一個document,所以沒有必要。{}
,這是findOne()
方法要搜尋的內容。如果我們有很多不同內容的document,我們要在這裡指定要過濾出來的內容。clickProjection
—— 上面已經定義過的,過濾結果的引數。function(err, result){...}
findOne方法的回撥函式,用來處理錯誤和結果。res.json(reslut)
—— 用JSON形式將資料傳送給瀏覽器。
這裡可不少新的內容。最後,我們將函式暴露出去。
我們的新的服務端的控制器就已經能用了,但是這有一個問題,假如資料庫中沒有相應的doucument怎麼辦?沒關係,MongoDB是很智慧的,它會自動生成相應的資料庫collections,但是document一定要特別指定。如果這是第一次使用資料庫,那裡面一定沒有collection,我們要讓這個控制器更加完善。
現在我們來更新getClick()
方法:
this.getClicks = function (req, res) { var clickProjection = { '_id': false }; clicks.findOne({}, clickProjection, function (err, result) { if (err) { throw err; } if (result) { res.json(result); } else { clicks.insert({ 'clicks': 0 }, function (err) { if (err) { throw err; } clicks.findOne({}, clickProjection, function (err, doc) { if (err) { throw err; } res.json(doc); }); }); } }); };
首先確認findOne()
能否有資料返回,如果有資料返回if(result){...}
就傳遞給瀏覽器。
如果沒有資料返回,我們就要在資料庫中插入資料。資料包含兩個引數,{clicks: 0}
,以及一個回撥函式。這個回撥函式是用來處理結果。如果過程中有錯誤發生,就會丟擲錯誤。如果完成資料插入,我們就會在資料庫中查詢最新的資料並在瀏覽器中用JSON格式顯示出來。
測試之前,我們再來做點改變。
index.js:
'use strict';var ClickHandler = require(process.cwd() + '/app/controllers/clickHandler.server.js');module.exports = function (app, db) { var clickHandler = new ClickHandler(db); app.route('/') .get(function (req, res) { res.sendFile(process.cwd() + '/public/index.html'); }); app.route('/api/clicks') .get(clickHandler.getClicks); };
我們來深入看下:
var ClickHandler...
, 這裡我們使用一個變數儲存函式物件。var clickHandler = new ClickHandler(db)
,這裡我們例項化了函式物件,並向其傳遞了MongoDB物件作為引數。這樣我們就能使用檔案clickHandler.server.js
中的內容以及資料庫中的內容。app.route('/api/clicks')
——定義了一個新的路由.get(clickHandler.getclicks)
——當路徑為api/clicks
的HTTP GET的時候,所要執行的getClicks
函式。
接下來,我們開始測試。如果你是跟著教程做的,那麼現在的資料庫中應該是空的。瀏覽器中輸入localhost:3000/api/clicks
。載入完成後,你就能看見[{"clicks":0}]
的內容。這意味著,所有的設定應該是正確的。
透過MongoDB終端測試API
如果想要使用終端再測試一次,我們可以使用MongoDB終端來進行手動測試。保持Node執行中,開啟新的終端,輸入$ mongo
連線MongoDB。
如果連線成功,你就能看見:
Mongo Shell Version: 3.0.3connecting to: test>
之後輸入use clementinejs
,命令列會提示使用clementinejs。之後輸入db.clicks.find({})
。這個命令會找出所有clicks collection中的資料。你就能看到以下的結果:
{ "_id": ObjectId(randomNumber), "clicks": 0 }
現在,我們來刪除document。在終端輸入db.click.remove({})
。這樣會刪除所有在collection中的內容。如果回到瀏覽器中重新整理頁面,新的內容就會又加入到資料庫中。
新增新的方法和路由
我們現在能查詢並且將資料庫內容返回。但是,我們需要提供路由和邏輯告訴程式當HTML按鈕按下的時候要做哪些事情。也就是說我們要新增相應的功能來響應按鈕被按下之後的事情。
現在來更新我們的控制器檔案。
clickHandler.server.js:
this.addClick = function (req, res) { clicks .findAndModify( {}, { '_id': 1 }, { $inc: { 'clicks': 1 } }, function (err, result) { if (err) { throw err; } res.json(result); } ); };this.resetClicks = function (req, res) { clicks .update( {}, { 'clicks': 0 }, function (err, result) { if (err) { throw err; } res.json(result); } ); };
這兩個方法,addClick
以及resetClicks
和getClicks
方法差不多。但是,其中每個方法都使用了不同的MongoDB方法。
addClick
使用了findAndModify
方法。前兩個引數和findOne
中使用的方法一樣。{}
是要返回所有的資料。_id:1
是使用排序,但在這個例子中,因為只有一種資料,所以排序的關係不是很大。
{$inc: {'click': 1}}
的意思是使用了方法。$inc方法找出要調整的資料clicks
,使用提供的數字來使原來的數字+1。所以每一次使用addClick
函式,這個數字的內容就會增加1。
之後我們使用了回撥函式如果出現問題的時候丟擲錯誤。沒有錯誤的時候就會以JSON格式顯示結果。
最後,我們在resetClicks方法中使用方法。之後的兩個引數,{}
是返回所有內容,{'click': 0}
是要更新的內容。整了理解就是resetClick方法更新了clicks的內容為0。最後將結果傳回到瀏覽器。
以下是所有的程式碼內容:
clickHandler.server.js:
function clickHandler (db) { var clicks = db.collection('clicks'); this.getClicks = function (req, res) { var clickProjection = { '_id': false }; clicks.findOne({}, clickProjection, function (err, result) { if (err) { throw err; } if (result) { res.json(result); } else { clicks.insert({ 'clicks': 0 }, function (err) { if (err) { throw err; } clicks.findOne({}, clickProjection, function (err, doc) { if (err) { throw err; } res.json(doc); }); }); } }); }; this.addClick = function (req, res) { clicks.findAndModify({}, { '_id': 1 }, { $inc: { 'clicks': 1 }}, function (err, result) { if (err) { throw err; } res.json(result); }); }; this.resetClicks = function (req, res) { clicks.update({}, { 'clicks': 0 }, function (err, result) { if (err) { throw err; } res.json(result); }); }; }module.exports = clickHandler;
最後,將方法新增到路由件中:
index.js:
app.route('/api/clicks') .get(clickHandler.getClicks) .post(clickHandler.addClick) .delete(clickHandler.resetClicks);
當HTTP GET/api/clicks
伺服器就會呼叫getClicks
方法。同樣的POST和DELETE也會根據方法同樣響應。
這些都是服務端的內容,現在我們看看客戶端的控制器。
新增客戶端控制器
客戶端的控制器會根據API的資料來響應,同時顯示在頁面。就是顯示當使用者點選按鈕的時候,頁面給出的反饋。
我們從細節著手:
當頁面載入的時候,同時載入'click'的次數
當‘click me’按鈕按下的時候,傳送一個請求給API,並更新資料。
當‘REST’按鈕按下的時候,傳送DELETE響應,並更新資料
還記得我們在服務端控制器中傳送POST的時候更新'clicks',傳送DELETE的時候,更新"clicks"為0。
新建控制器
在/app/controllers資料夾中新建一個
clickController.client.js`檔案。檔案中,我們用括號括起來函式表示使用IIFE(宣告後立即執行)方法。
clickController.client.js:
(function(){ })();
IIFE將所有的變數繫結在函式內,這樣就不會與程式中其他的變數有名稱空間的衝突。
下一步,我們使用Javascript來獲取HTML內容。這裡我們使用document.querySelector(cssSelector)
方法。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; })();
新建控制器函式
首先當頁面載入的時候,我們要接受API返回的資料庫內容s。
元素就是資料庫內容要顯示的位置。要做到這些,我們要先新建一個函式來檢視DOM是否已經載入,之後執行另外一個函式。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; function ready (fn) { if (typeof fn !== 'function') { return; } if (document.readyState === 'complete') { return fn(); } document.addEventListener('DOMContentLoaded', fn, false); } })();
現在我們拆開來看這段程式碼的意思。我們新建了一個名為ready
的函式,函式使用一個引數-fn
。之後使用typeof
驗證fn
是否是一個函式,如果不是一個函式就返回,不做任何動作。
之後,如果文件的readyState
屬性為complete
,我們就執行引數函式,並返回。
最後,如果文件沒有載入,我們就新增一個時間監聽器document.addEventListener(type, listener, useCapture)
。這個方法使用3個引數:
type
: 表示監聽事件的名稱,這裡是DOMContentLoaded
事件.listener
:事件發生後執行的函式。userCapture
表示當發起捕獲的時候,只要DOM樹下發現了該事件型別,都會先派發到該註冊監聽器,然後再派發到Dom樹中的註冊監聽器。如果沒有指定,預設值為false。
接下來製作一個接受API資料的函式。我們要使用XMLHttpRequest
,這個物件可以讓我們在不改變整個頁面的情況下,改變內容。這又叫做AJAX,是一個非常方便的方法。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; function ready (fn) { ... } function ajaxRequest (method, url, callback) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { callback(xmlhttp.response); } }; xmlhttp.open(method, url, true); xmlhttp.send(); } })();
我們再一點點看裡面的內容:
function ajaxRequest(method, url, callback){...}
:函式中有3個引數:
method
是http request。url
是HTTP request的地址callback
是當資料接收之後的回撥函式
var xmlhttp = new XMLHttpRequest()
:XMLHTTPRequest的例項化。
xmlhttp.onreadystatechange = function(){...}
:這裡我們給onreadystatechange
assigning一個回撥函式。每當readyState
屬性改變的時候,就會執行裡面的函式。
這個函式會在每次readyState
改變的時候執行。有很多種的readyState
數值,這裡我們時候用readyState === 4
,這意味著操作(例如資料接收完成)已經完成。同樣,我們要確保status
(HTTP status code)的數值是200
,這意味著request狀態正常。
如果這兩個條件都滿足,就執行其中的函式。同時將xmlhttp.response
屬性作為引數傳遞給函式。這個response
是AjAX request中得出的資料。
現在,我們已經寫好了函式。當函式第一次被執行的時候,我們要初始化request。xmlhttp.open(method, url, async)
就是做這件事情的。這個方法有3個引數:
method: HTTP方法,這裡我們使用
ajaxRequet
函式的引數。url: HTTP方法所要的url。
async: request要同步執行,還是非同步執行,這裡我們使用
true
。
最後,xmlhttp.send()
方法執行了上面初始化的request。這樣,你就寫了一個AJAX函式。
下一步,我們寫一個小的函式,來更新
元素。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; function ready (fn) { ... } function ajaxRequest (method, url, callback) { ... } function updateClickCount (data) { var clicksObject = JSON.parse(data); clickNbr.innerHTML = clicksObject.clicks; } })();
這個函式是非常重要的一個函式。看到函式中的引數使用了data
,這個data
是上面xmlhttp.response
引數。AJAX request發出HTTP requst,之後從API返回帶有數值的字串。
有一點不好的地方就是,我們希望返回的資料是物件而不是字串,這樣我們方便的使用資料,就像從API得出的資料{'click': 0}
。
我們使用JSON.parse()
方法將data
引數轉化為JSON物件。我們使用clicksObject儲存變數。
下一步,我們將clickNbr
(前面定義的)元素內容改變為上面物件中的數值。這裡我們使用了.innerHTML
。
下一步,我們定義當頁面載入的時候以及按鈕按下的時候的事件。
監聽事件
首先,在頁面中載入的時候顯示獲得的數值。我們就要使用ready
函式。ready函式引數是另外一個函式。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; function ready (fn) { ... } function ajaxRequest (method, url, callback) { ... } function updateClickCount (data) { ... } ready(ajaxRequest('GET', apiUrl, updateClickCount)); })();
現在,我們執行ready
函式,引數為Ajax函式,這個函式的引數為apiUrl
,以及updateClickCount
。
同樣的,我們用同樣的方法為按鈕新增方法。先從CLICK ME
按鈕開始。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; ... ... addButton.addEventListener('click', function () { ajaxRequest('POST', apiUrl, function () { ajaxRequest('GET', apiUrl, updateClickCount) }); }, false); })();
上面的程式碼應該有點熟悉,我們繫結一個事件監聽到addButton
元素,監聽click
事件。當事件發生的時候,執行函式。這個函式將會POST一個AJAX request,也就是增加一次click的數字。整個過程一旦完成,GET request會更新頁面的資料。
下一步,我們同樣新增一個類似的事件監聽到RESET
按鈕。
clickController.client.js:
'use strict'; (function () { var addButton = document.querySelector('.btn-add'); var deleteButton = document.querySelector('.btn-delete'); var clickNbr = document.querySelector('#click-nbr'); var apiUrl = 'api/clicks'; ... ... addButton.addEventListener( ... ); deleteButton.addEventListener('click', function () { ajaxRequest('DELETE', apiUrl, function () { ajaxRequest('GET', apiUrl, updateClickCount); }); }, false); })();
最後,我們新增事件到reset
按鈕。這和CLICK ME
事件類似。
最後,我們需要在我們的HTML中填入這個控制器。
index.html:
html> .........
現在可以測試程式了,輸入node server
,開啟localhost:3000
,看看是否能工作正常。
頁面現在看起來不好看,我們可以新增一些好看的顏色!
新增CSS樣式
教程就快進入尾聲了。
再在/public/css
資料夾新建一個main.css
檔案來修改樣式。
mian.css:
/****** Main Styling ******/body { font-family: 'Roboto', sans-serif; font-size: 16px; }p { margin: 8px 0 0 0; }.container p { text-align: center; padding: 0; }/****** Logo Div Styling ******/img { margin: 20px auto 0 auto; display: block; }.clementine-text { /* Styling for the Clementine.js text */ padding: 0; margin: -25px 0 0 0; font-weight: 500; font-size: 60px; color: #FFA000; }/****** Click Styling ******/.btn-container { /* Styling for the div that contains the buttons */ margin: -10px auto 0 auto; text-align: center; }.btn { /* Styling for buttons */ margin: 0 8px; color: white; background-color: #00BCD4; display: inline-block; border: 0; font-size: 14px; border-radius: 3px; padding: 10px 5px; width: 100px; font-weight: 500; }.btn:focus { /* Remove outline when hovering over button */ outline: none; }.btn:active { /* Scale the button down by 10% when clicking on button */ transform: scale(0.9, 0.9); -webkit-transform: scale(0.9, 0.9); -moz-transform: scale(0.9, 0.9); }.btn-delete { /* Styling for delete button */ background-color: #ECEFF1; color: #212121; }
現在把它和HTML檔案合為一體。
index.html:
Clementine.js - The elegant and lightweight full-stack boilerplate.
第一個是引用了Google字型,這不是必要的,但是我真的喜歡Roboto字型,第二個是和css檔案聯絡。
現在我們啟動我們的程式,就是如下的效果:
下一步
恭喜你完成了教程內容,如果你是初學者,建議你開始真正嘗試做一些東西,例如登入程式,留言程式。這樣會讓你理解更深,並能強制你學習到更多的東西。
作者:lvsjack1
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1868/viewspace-2802673/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一個完整的scrapy 專案
- 實戰GraphQL+express+mysql專案完整demoExpressMySql
- Vue+Express全棧開發專案實戰技能:從0到1打造完整電商專案VueExpress全棧
- Spring MVC——專案的開發流程SpringMVC
- 開源一個功能完整的SpringBoot專案框架Spring Boot框架
- 建立一個ASP.NET MVC 5專案ASP.NETMVC
- 開發一個完整的JavaScript元件JavaScript元件
- 最新Python開發專案實戰(完整)Python
- 一個專案完整的管理流程有哪些
- ASP.NET + MVC5 入門完整教程三 (上) --- 第一個 MVC 專案_lingshuangcanxue-CSDN 部落格_asp.net mvcASP.NETMVCGC
- 使用js開發一個快速開啟前端專案的alfred外掛JS前端Alfred
- Vue 搭配 Spring MVC 建立一個 web 專案VueSpringMVCWeb
- 如何5分鐘跑起來一個完整專案?
- 完整的設計一個專案需要什麼?
- 開發一個好專案:八、建立viewView
- 我如何用Django開發一個專案Django
- Nancy .Net 輕量級mvc框架使用(1)搭建一個簡單專案NaNMVC框架
- Flutter開發第一個專案android studio 開發工具的使用說明FlutterAndroid
- 使用Express開發小說API介面服務1.0(一)ExpressAPI
- 使用 Docker 開發 PHP 專案(一):安裝DockerPHP
- Laravel 完整開源專案大全Laravel
- 【譯】使用 MongoDB,React,Node 和 Express(MERN)構建一個全棧應用MongoDBReactExpress全棧
- 開始一個專案
- Demo丨GitHub Codespaces,雲上開發完整專案Github
- 一個外行如何快速融入node專案開發
- 【VIP視訊網站專案上線】基於Nodejs的Express框架開發的VIP視訊網站專案及完整程式碼分享...網站NodeJSExpress框架
- 《Node+MongoDB+React 專案實戰開發》已出版MongoDBReact
- Spring Security專案Spring MVC開發RESTful API(二)SpringMVCRESTAPI
- 一個完整的機器學習專案在Python中演練(四)機器學習Python
- 一個完整的機器學習專案在Python中演練(三)機器學習Python
- spring mvc專案配置一覽SpringMVC
- 如何發起並運營一個開源專案
- 使用mongo-express管理mongodb資料庫ExpressMongoDB資料庫
- 使用 Docker 部署 NodeJS + MongoDB 專案DockerNodeJSMongoDB
- webpack實戰(一):真實專案中一個完整的webpack配置Web
- 一個完整的機器學習專案在Python中的演練(一)機器學習Python
- MVC3使用Area解耦專案MVC解耦
- 完全使用 Docker 開發 PHP 專案 (一): 安裝篇DockerPHP