Giáo trình Mô đun Phát triển mã nguồn mở với Nodejs - Thiết kế trang Web
Thiết lập môi trường
Các đoạn code node.js thực chất là các đoạn mã Javascript. Node.js sẽ được sử
dung để thông dịch và thực thi các đoạn code Javascript.
Node.js phân phối dưới dạng các bản cài đặt cho các hệ điều hành Linux, Mac
OS X và Windown với các phiên bản 32 bit và 64 bit.
Để cài đặt Node.js các bạn có thể truy cập vào kho lưu trữ của Node.js và tải
xuống bản cài đặt phù hợp với hệ điều hành mình đang sử dụng.
Cài đặt trên Windows
Tải tệp cài đặt với định dạng .msi trên trang chủ download của Node.js
Chạy tệp cài đăt với định dạng .msi mà bạn đã tiến hành tải xuống trước đó
Làm theo hướng dẫn trong trình cài đặt.
Khởi động lại máy tính: Bạn sẽ không thể nào chạy được Node.js nếu không khởi
động lại máy tính sau khi cài đặt.
Cài đặt trên MacOs
Tải tệp cài đặt với định dạng .pkg trên trang chủ Node.Js
Mở tệp cài đặt với định dạng .pkg mà bạn vừa tải xuống
Làm theo các bước của hướng dẫn của bộ cài đặt
Cài đặt trên Linux
Với các máy chạy hệ điều hành Linux các bạn có thể cài đặt bằng cách tải xuống
và giải nén các tệp lưu trữ .tar.gz , sau đó chuyển các tệp được giải nén vào thư
mục /usr/local/nodejs
Tải tệp lưu trữ .tar.gz

Trang 1

Trang 2

Trang 3

Trang 4

Trang 5

Trang 6

Trang 7

Trang 8

Trang 9

Trang 10
Tải về để xem bản đầy đủ
Tóm tắt nội dung tài liệu: Giáo trình Mô đun Phát triển mã nguồn mở với Nodejs - Thiết kế trang Web
ng
socket ở đó, để các máy khách có thể thiết lập các kết nối khi được yêu cầu.
Tập tin được sử dụng bới máy chủ socket.io là „/socket.io/socket.io.js„
Sau khi hoàn thành các yêu cầu trên tệp index.html sẽ có nội dung như sau.
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 60
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
Hello World
Hello World
var socket = io();
Nếu bạn chạy ứng dụng và truy cập localhost:3000 bạn sẽ nhận được thông báo
“Hello Word” trên trình duyệt.
Để kiểm tra nhật ký ở bảng điều khiển console các bạn sẽ nhận được các thông
báo sau.
3. Xử lý sự kiện trong Socket.IO
3.1 Tổng quan module Events trong Node.js
“Module events với lớp đối tượng EventEmitter bên trong nó chính là cốt lõi của
kiến trúc hướng sự kiện không đồng bộ trong Node.js, hầu hết các core module built-in
trong Node.js đều kế thừa từ module events này.”
Vậy chính xác thì module này dùng để làm gì?
Câu trả lời đơn giản là: “Nó cho phép bạn lắng nghe các sự kiện và gán các
hành động để chạy khi những sự kiện đó xảy ra.”
Nếu bạn đã từng làm việc với Javascript phía trình duyệt, bạn chắc sẽ từng biết
về các sự kiện chuột hay bàn phím khi người dùng phía client tương tác với ứng dụng
như onclick, onkeyupvv, dựa vào những sự kiện đó mà chúng ta sẽ viết code xử lý
hành động tiếp theo.
Events cũng giống như vậy, ngoại trừ việc đó là chúng ta có thể tự định nghĩa sự
kiện, phát ra sự kiện khi chúng ta muốn, không cần thiết phải dựa trên tương tác của
người dùng.
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 61
“Module Events trong Node.js hỗ trợ cho chúng ta lập trình, viết code theo
kiến trúc Event-Driven.”
– “Events là một trong những lý do làm cho Node.js được nói là có tốc độ
cao: Vì hầu hết code core của Node.js đều dựa trên mô hình Event-Driven này nên
thay vì đọc tất cả những file cần thiết với mọi request (ví dụ như PHP). Thì thằng
Node này lại chỉ khởi động server, khởi tạo hết các biến, khai báo các function mà
mình viết rồi cứ thế ngồi đợi một sự kiện nào đó xảy ra và thực thi chức năng.
– “Khi sử dụng Events thì việc ghép nối các đoạn code của chúng ta lại sẽ
khá lỏng lẻo”: Nghĩa là khi một sự kiện được phát ra, nhưng nếu không có đoạn code
nào lắng nghe sự kiện đó, thì cũng không sao hết, nó sẽ chẳng làm gì luôn. Như vậy
khi cần xóa code, cụ thể là xóa một “bên lắng nghe” hoặc một “sự kiện không cần
dùng đến nữa” thì cũng không bao giờ bị thông báo lỗi code Javascript gì cả.
– “Sử dụng Events để thay thế cho Callbacks trong những trường hợp cụ
thể” (không phải thay thế hoàn toàn): Node.js có rất nhiều phương thức chạy không
đồng bộ (asynchronous), đồng nghĩa với việc có rất nhiều tác vụ liên quan đến nhau,
tác vụ sau cần dữ liệu của tác vụ trước để chạy, nếu dùng callback thì code của bạn sẽ
ngày càng trông giống một cái phễu lớn, người ta gọi đó là Callback Hell.
Để ngăn chặn Callback Hell, nhiều class trong node.js đã sử dụng events để
phát ra sự kiện, lắng nghe sự kiện và thực hiện các hành động ứng với các sự kiện.
Như vậy sẽ tổ chức code theo một cấu trúc khác gọn gàng hơn, không gặp phải
“callback heo” nữa.
3.2 Sử dụng module Events.
Sau khi đã phân tích khá nhiều lý thuyết ở trên, bây giờ chúng ta sẽ đi vào cách
sử dụng module.
Mình sẽ viết ví dụ đơn giản đầu tiên, Viết code để xử lý bài toán: Khi con mèo
chạy, cái chuông trên cổ con mèo sẽ kêu ring ring.
/**
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 62
* Created by trungquandev.com's author on 30/09/2018.
*/
const events = require("events");
let EventEmitter = new events.EventEmitter();
let ringBell = () => {
console.log("ring ring ring...");
}
// Lắng nghe sự kiện khi mèo chạy thì gọi tới function ringBell
EventEmitter.on("catRun", ringBell);
// Phát sự kiện con mèo chạy.
EventEmitter.emit("catRun");
Đầu tiên là nạp module events, đối tượng events này có một thuộc tính duy nhất
đó là lớp EventEmitter.
Bên trong EventEmitter có 2 phương thức chính là emit và on tương ứng
với phát và lắng nghe sự kiện.
Khi chạy EventEmitter.emit sẽ emit (phát ra) một sự kiện tên là “catRun” do
chúng ta đặt, và rồi EventEmitter.on sẽ lắng nghe sự kiện “catRun” sau đó chạy
function ringBell. Nếu bỏ đi một trong 2 method .emit hay .on ở trên thì chương trình
cũng không bị lỗi gì cả.
Ngoài ra chúng ta còn có thể include thêm dữ liệu khi emit sự kiện như thế
này:
EventEmitter.emit("catRun", data);
Thì ở bên lắng nghe:
EventEmitter.on("catRun", (data) => {
// Làm gì đó với data nhận được ở đây...
});
Chúng ta có thể lắng nghe nhiều lần trên cùng một sự kiện như thế này:
EventEmitter.on("catRun", (data) => {
// Sử dụng data cho công việc 1.
});
EventEmitter.on("catRun", (data) => {
// Sử dụng data cho công việc 2.
});
EventEmitter.on("catRun", (data) => {
// Sử dụng data cho công việc 3.
});
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 63
Mặc định Node.js cho phép 10 listeners trên cùng một sự kiện, có nghĩa là
trong sự kiện “catRun” ở trên, Tới công việc thứ 11 Node.js sẽ thông báo lỗi. Nhưng
không sao cả, chúng ta có thể sử dụng hàm setMaxListeners để tăng giới hạn đó.
EventEmitter.setMaxListeners(17); // ví dụ mình nâng lên 17 listeners.
Còn rất nhiều phương thức hay nữa như:
.once(); .removeListener(); .removeAllListener(); .listener(); vv.
3.3 Viết một module khác kế thừa module Events
Trong thực tế khi viết code, sẽ còn hay hơn nữa khi mà chúng ta có thể viết một
Class khác mà kế thừa các phương thức cũng như thuộc tính của lớp
EventEmitter trong module events. Vì EventEmitter cũng là Javascript thông
thường và có thể sử dụng trong các module khác.
Nếu từng sử dụng module http của node.js, bạn có thể thấy nó cũng có một
phương thức là .on();
/**
* Created by trungquandev.com's author on 30/09/2018.
*/
var http = require("http");
var server = http.createServer();
server.on("request", function (req, res) {
res.end("This is the response.");
});
server.listen(8017);
Ví dụ ở trên cho thấy phương thức .on(); của lớp EventEmitter đã trở thành một
phần của lớp http.createServer();
Khi server nhận được một request từ trình duyệt, nó sẽ emit một sự kiện có tên
là “request”, sau đó một listener chính là server.on(); lắng nghe và hành động. Cụ thể
hành động ở đây là trả về một chuỗi text: “This is the response.”
– Bây giờ, bắt đầu ví dụ chính của chúng ta:
“Mình sẽ viết một module UserModel.js kế thừa module events, sau đó viết một
file index.js sử dụng chính module UserModel này mỗi khi lưu một user mới
vào database thì sẽ emit một sự kiện thông báo là đã lưu trữ user thành công.”
UserModel.js
const EventEmitter = require("events").EventEmitter;
// Fake database.
let database = {
users: [
{id: 1, name: "Trungquandev01", occupation: "developer"},
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 64
{id: 2, name: "Trungquandev02", occupation: "writer"},
{id: 3, name: "Trungquandev03", occupation: "designer"}
]
};
class UserModel extends EventEmitter {
constructor() {
super(); // Từ khóa super được sử dụng để gọi các hàm trên đối tượng cha, ở
đây đối tượng cha là EventEmitter
}
// Lưu user vào "database fake" ở trên.
saveUser(user) {
database.users.push(user);
this.emit("saved", user); // sử dụng hàm .emit của thằng EventEmitter
}
// Liệt kê toàn bộ user hiện tại.
allUser() {
return database.users;
}
}
module.exports = UserModel;
index.js
const UserModel = require("./UserModel");
let User = new UserModel();
// Vì đã kế thừa events nên class User có thể sử dụng method .on()
User.on("saved", (user) => {
console.log(`New user saved: ${user.name} - ${user.occupation}`);
});
// Lưu thêm 2 thằng user mới.
let trungquandev04 = {id: 4, name: "Trungquandev04", occupation: "Code xịn
(─‿‿─)"};
let trungquandev05 = {id: 5, name: "Trungquandev05", occupation: "Code lởm
(-.-)"};
User.saveUser(trungquandev04);
User.saveUser(trungquandev05);
// Lấy ra toàn bộ users
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 65
let allUser = User.allUser();
console.log(allUser);
Kết quả sau khi chạy:
Như vậy, có thể thấy, module UserModel của chúng ta sau khi kế
thừa EventEmitter, đã có thể tự phát và tự lắng nghe các sự kiện. Cool huh? =)) 😀
3.4 Phân biệt events với socket.io
Chắc có khá nhiều bạn khi mới làm quen với node.js thì cũng từng nghe qua cái
tên khá phổ biến đó là socket.io để làm các ứng dụng real-time. Khi mới tìm hiểu
Node.js mình từng cảm thấy chút mâu thuẫn giữa 2 thành phần events và
socket.io này.
Cả 2 module trên đều có chung một điểm là emit phát sự kiện rồi on để lắng
nghe sự kiện và gửi – nhận các tham số dữ liệu từ chúng.
Điểm khác quan trọng giữa 2 thành phần này đó là:
Socket.io chỉ cho phép phát và lắng nghe sự kiện qua lại giữa client và server.
Events chỉ cho phép phát và lắng nghe sự kiện trong nội bộ server.
Còn nếu muốn sử dụng socket.io phát và nhận sự kiện ngay trên server
luôn thì có một gói module khác là socket.io-client.
4. Socket.IO Broadcasting
Broadcasting nghĩa là gửi một tin nhắn đến tất cả người dùng đang kết nối.
Broadcasting có thể thực hiện ở nhiều cấp độ. Chúng ta có thể gửi tin nhắn đến tất cả
người dùng đang kết nối, khách hàng có tên bất kỳ và cả khách hàng ở trong một
phòng cụ thể. Để phát một sự kiện cho tất cả các khách hàng, chúng ta có thể sử dụng
phương thức io.sockets.emit
Lưu ý: Thao tác này sẽ phát ra sự kiện cho TẤT CẢ máy khách đang được kết
nối.
Trong ví dụ này tôi sẽ phát ra sự kiện cho tất cả máy khách đang kết nối. Thay
đổi nội dung file app.js như sau.
const express = require('express');
const app = express();
const server = require('http').Server(app);
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 66
const io = require('socket.io')(server)
app.get('/', function(request, response){
response.sendFile(__dirname + '/index.html');
});
var clients = 0;
io.on('connection', function(socket){
clients++;
io.sockets.emit('broadcast', {description: `${clients} người dùng đã
kết nối`})
socket.on('disconnect', function(){
clients--;
io.sockets.emit('broadcast', {description: `${clients} người dùng
đã kết nối`})
})
});
server.listen(8000, function(){
console.log(`Lắng nghe trên cổng 8000`);
});
Về phía máy khách, chúng ta tiến hành xử lý sự kiện broadcast.
File index.html
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
Hello World
Hello World
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 67
var socket = io();
socket.on('broadcast', function(data){
document.write(data.description)
})
Với ví dụ trên sẽ gửi tất cả sự kiện cho mọi người. Bây giờ nếu, nếu chúng ta
muốn gửi sự kiến tới mọi người nhưng trừ máy khách gây ra sự kiện chúng ta có thể
sử dụng socket.broadcast.emit .
Ví dụ dưới đây, tôi sẽ gửi cho người dùng vừa kết nối một thông điệp chào
mừng, và cập nhật cho các máy khách khách khác về việc có người vừa kết nối.
Trong tệp app.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server)
app.get('/', function(request, response){
response.sendFile(__dirname + '/index.html');
});
var clients = 0;
io.on('connection', function(socket){
clients++;
socket.emit('newclientconnect', {description: `Chào mừng bạn`})
socket.broadcast.emit('newclientconnect', {description: `${clients}
người dùng đã kết nối`})
socket.on('disconnect', function(){
clients--;
io.sockets.emit('broadcast', {description: `${clients} người dùng
đã kết nối`})
})
});
server.listen(8000, function(){
console.log(`Lắng nghe trên cổng 8000`);
});
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 68
Xử lý sự kiện ở client
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
Hello World
Hello World
var socket = io();
socket.on('newclientconnect', function(data){
document.body.innerHTML = '';
document.write(data.description)
})
Bây giờ khi có người mới kết nối sẽ có thông điệp chào mừng và những người đã
kết nối trước đó sẽ nhận được có bao nhiêu người đang kết nối với máy chủ.
5. Socket.IO Namspace
Socket.IO cho phép bạn tạo ra “namespace” riêng cho sockets của bạn, về cơ bản
có nghĩa là gán các điểm cuối hoặc các đường dẫn khác nhau.
Đây là một tính năng hữu ích để giảm thiểu số lượng tài nguyên (kết nối TCP) và
đồng thời các mối quan tâm riêng biệt của bạn trong ứng dụng bằng cách giới thiệu sự
khác biệt giữa các kênh.
Nhiều namspace thực sự chia sẻ cùng một kết nối Websockét do đó tiết kiệm cho
chúng ta các cổng trên máy chủ.
Namespace được tạo ra ở phía máy chủ. Tuy nhiên, chúng được máy khách tham
bằng cách gửi yêu cầu đến máy chủ.
Namespace mặc định
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 69
Namespace gốc „/‟ là namespace mặc định, được kết nối với máy khách nếu
namespace không được chỉ định bởi client trong khi kết nối với máy chủ. Tất cả các
kết nối đến máy chủ sử dụng phía máy khách ở đối tượng socket được thự hiện cho
namespace mặc định.
Ví dụ
var socket = io();
Điều này sẽ kết nối máy khách với namespace mặc định. Tất cả các sự kiện trên
kết nối namespace này sẽ được xử lý bởi đối tương io trên máy chủ. Tất cả các ví dụ
trước đây đã sử dụng các namespace mặc định để giao tiếp với máy chủ và ngược lại.
Namespace tùy chỉnh
Chúng ta có thể tạo ra các namespace tùy chỉnh của riêng mình. Để thiết lập
namespace tùy chỉnh, chúng ta có thể gọi phương thức of ở phía máy chủ.
Thay đổi nội dung app.js như sau
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server)
app.get('/', function(request, response){
response.sendFile(__dirname + '/index.html');
});
var clients = 0;
var namespace = io.of('/my-namespace')
namespace.on('connection', function(socket){
console.log('Một người đã kết nối')
namespace.emit('chao', 'Chào tất cả mọi người')
});
server.listen(8000, function(){
console.log(`Lắng nghe trên cổng 8000`);
});
Phạm Đình Nam – Trường Cao đẳng nghề Đà Lạt Trang 70
Tiếp theo để kết nối một máy khách với namespace này, bạn cần cung cấp
namespace làm tham số cho lời gọi hàm tạo io để tạo một kết nối và một đối tượng
socket ở phía máy khách.
Ví dụ để kết nối với namespace ở trên. Cập nhật tệp index.html
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
Hello World
Hello World
var socket = io('/my-namespace');
socket.on('chao', function(data){
document.body.innerHTML = '';
document.write(data)
})
Mỗi khi có một ai đó kết nối họ sẽ nhận được sự kiện “chao”
File đính kèm:
giao_trinh_mo_dun_phat_trien_ma_nguon_mo_voi_nodejs_thiet_ke.pdf

