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