Cách viết JavaScript hiện đại: Phần 2: CommonJS module
Bài đăng này đã không được cập nhật trong 3 năm
Nguồn: 旧石器時代のJavaScriptを書いてる各位に告ぐ、現代的なJavaScript超入門 Section2 ~CommonJSモジュールと仲良くなろう~
Bài viết này là phần 2 của loạt bài dịch Cách viết JavaScript hiện đại.
Những người muốn theo dõi từ đầu có thể xem phần 1 ở đây:
Cách viết JavaScript hiện đại: Phần 1: Tổng hợp các điểm mới có thể thực hành ngay
Chuỗi bài viết này sử dụng cho các browser đáp ứng tiêu chuẩn ECMAScript5
nên không dùng được cho IE8 trở xuống.
Bài viết vẫn trung thành với quan điểm giải thích một cách dễ hiểu nhất.
Mục lục
Phần 1: Tổng hợp các điểm mới có thể thực hành ngay
Phần 2: Làm quen với CommonJS module
Phần 3: Master Browserify
Phần 4: Tự động hóa xử lý dùng Gulp
Phần 5: Nhớ cú pháp của ES2015
Lời mở đầu
Trong bài viết, cụm từ CommonJS module
được dùng để chỉ module specs và interface (require/exports.○○
/module.exports
) dùng Node.js
độc lập mở rộng CommonJS
từ cơ sở là specs của Modules 1.0 của CommonJS
.
Nó không có nghĩa là CommonJS
của dự án về đặc điểm kỹ thuật tiêu chuẩn phát triển hay specs của CommonJS Modules 1.0
.
Khi các bạn nhìn vào code của rất nhiều website, bạn có ngạc nhiên khi thấy các đoạn code sau không?
require…
var hoge = require("hoge");
var piyo = hoge.fuga();
// ...(Xử lý bất kỳ)
module.exports…
function Hoge(){
// ...(Xử lý bất kỳ)
}
module.exports = Hoge;
Có thể các bạn sẽ ngay lập tức phản ứng như sau:
"Theo tôi biết thì JavaScript
không có require
và module
mà?"
"Tôi đã thử copy paste và quả nhiên là không hoạt động trên trình duyệt."
Xin hãy yên tâm, đoạn code này có thể chạy trên trình duyệt. Tuy nhiên, để chạy được thì bạn cần biết vài điều.
Phần trình bày dưới đây sẽ giải thích cụ thể về khái niệm, nguồn gốc và cách để đưa các module này vào trình duyệt.
1. Việc phân chia thành các module
Khi một script lớn đến một mức nào đó, chúng ta có thể tổng hợp các chức năng hay dùng thành các module
.
Đến nay, chúng ta vẫn thường thực hiện việc này trên trình duyệt bằng cách tạo file .js, thông qua HTML có thể load nhiều lần.
Ví dụ, khi mọi người dùng jQuery (không dùng CDN), chắc chắn mọi người đã gọi jQuery file bằng script
tag.
Ví dụ sử dụng jQuery
module
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- Tóm lược -->
<script src="./jquery.min.js"></script>
<script>
// Có thể sử dụng jQuery vì nó được gọi ra với tư cách là một module bên ngoài.
console.log(typeof(window.jQuery)); // "function"
jQuery(function($){
// Viết xử lý ở đây
});
</script>
</head>
<body>
<!-- Tóm lược -->
</body>
</html>
Tuy nhiên, trong việc dùng module này có một số lỗ hổng chết người. Trong đó, vấn đề lớn nhất là giới hạn của namespace.
Chẳng hạn tôi tạo ra 1 module không hề liên quan đến jQuery
nhưng lại đặt tên là jQuery.
myjquery.js
!function(){
window.jQuery = jQuery;
window.$ = jQuery;
// Module để chuyển 1 string tiếng Nhật sang query string
function jQuery(obj){
var keys = Object.keys(obj);
if(!keys.length) return "";
return "?" + keys.map(function(key){
return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
}).join("&");
}
}();
/*
var text = jQuery({
name: "がお",
age: 18,
});
console.log(text); // ?name=%E3%81%8C%E3%81%8A&age=18
*/
Bây giờ cùng lúc gọi module này và jQuery
mọi người vẫn thường dùng thì sẽ xảy ra vấn đề.
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- Tóm lược -->
<script src="./jquery.min.js"></script>
<script src="./myjquery.js"></script>
<script>
// Có thể sử dụng jQuery vì nó được gọi ra với tư cách là một module bên ngoài.
console.log(typeof(window.jQuery)); // "function"
// Tuy nhiên, jQuery này không phải là jQuery mà chúng ta vẫn biết.
jQuery(function($){
// Dù có viết xử lý ở đây thì nó cũng không được thực thi.
// jQuery chúng ta tự viết được ưu tiên hơn.
});
</script>
</head>
<body>
<!-- Tóm lược -->
</body>
</html>
Đến đây thì quả là một rắc rối lớn.
Do đó, CommonJS module
đã được tạo ra.
(Hiện nay, CommonJS module
không còn được tích cực sử dụng, ngược lại còn đang có xu hướng mất dần đi. Tuy nhiên, đây là câu chuyện của ES2015 module
.)
Ở năm 2016 này, đáng tiếc là CommonJS module
vẫn còn hữu dụng và vẫn được dùng thường xuyên nên vẫn cần phải biết về cách xử lý các module này.
2. CommonJS module là gì
Về vấn đề này còn rất nhiều tranh cãi, bài này sẽ chỉ ra những điểm chung nhất.
CommonJS module
là các module thỏa mãn các điều kiện sau:
2.1. Khi tạo ra module thì dùng module.exports
hoặc exports.○○
Thực hiện CommonJS
của myjquery.js
/* bỏ cách gán trực tiếp cho window object
window.jQuery = jQuery;
window.$ = jQuery;
*/
// Thay vào đó dùng module.exports
module.exports = jQuery;
// Module để chuyển 1 string tiếng Nhật sang query string
function jQuery(obj){
var keys = Object.keys(obj);
if(!keys.length) return "";
return "?" + keys.map(function(key){
return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
}).join("&");
}
2.2. Khi gọi CommonJS module
, sử dụng hàm require
Gọi module dùng require
var jQuery = require("jQuery"); // jQuery mọi người thường dùng
var myJQuery = require("./myjquery.js"); // jQuery của bản thân
// Mỗi module được đưa vào 1 hàm
console.log(typeof(jQuery)); // "function"
console.log(typeof(myJQuery)); // "function"
// Theo nguyên tắc của CommonJS, không gán xuống window object nên không bị ảnh hưởng.
console.log(typeof(window.jQuery)); // "undefined"
// jQuery được dùng ở đây là jQuery chúng ta vẫn quen biết!
jQuery(function($){
// Viết xử lý ở đây});
//Cùng lúc đó chúng ta có thể dùng jQuery của bản thân mình
var text = myJQuery({
name: "がお",
age: 18,
});
console.log(text); // ?name=%E3%81%8C%E3%81%8A&age=18
Ở đây tác giả đưa ra việc thực hiện CommonJS module
jQuery
mà mọi người thường dùng nhưng đừng để ý gì cả.(Module nổi tiếng như jQuery
thì đã có các kỹ sư tài ba tạo ra).
Đến đây thì việc đổi một module
đã có sẵn thành CommonJS module
kết thúc.
Tuy nhiên, nếu chỉ đổi thôi thì nó vẫn chưa hoạt động trên trình duyệt.
Đã có Node.js
"Nhưng tại sao lại nhắc đến Node.js
khi đang nói về Web?"
Hãy yên tâm. Dù hơi mơ hồ nhưng thực sự có mối quan hệ ở đây.
Thực ra Node.js
đã được biết đến trong 5 năm gần đây và dần trở nên phổ biến nên ở đây chỉ giải thích sơ lược.
Node.js
là môi trường JavaScript
hướng server side hoạt động trong V8 engine.
Node.js
tuân thủ ECMAScript
nên đương nhiên không có DOM API
và hoạt động trên console
chứ không phải trên trình duyệt. Do đó, có thể nó ko quen thuộc với những người chuyên viết code hướng browser.
Điều tôi thực sự muốn nói ở đây không phải là về Node.js
mà chỉ muốn nhấn vào 2 điểm:
Node.js
đang ngày càng thịnh hành
Node.js
có thể dùng CommonJS module
Tức là Node.js
làm cho CommonJS module
trở nên phổ biến.
3. Áp dụng CommonJS module
vào Web
Đến đây những người tinh ý có thể đã nghĩ ra để dùng CommonJS module
trên Web browser thì có thể sử dụng Node.js
.
Và quả đúng là như vậy.
Về lý thuyết cũng có cách không cần dùng đến Node.js
nhưng không quen thuộc với các lập trình viên JS nên không đề cập ở đây.
Thực ra có một số module
dùng để đổi sang Web browser nhưng nổi tiếng nhất là Browserify
(sẽ giải thích thêm ở phần sau).
Việc sử dụng CommonJS module
trên Web browser được tổng hợp trong sơ đồ dưới đây:
Điểm cần chú ý ở đây là dù dùng cho Node.js
hay Web browser thì đều dùng chung việc require
. Chỉ có điều khi dùng trên Web browser thì có thêm bước xử lý chuyển đổi.
4. Browserify là gì?
Từ phần trên ta có thể hiểu đôi chút về Browserify
rồi. Đó là công cụ chuyển đổi để code được viết bằng CommonJS module
có thể chạy được trên trình duyệt.
(Tuy nhiên, ở bài viết này sẽ không nói về cách dùng Node.js và Browserify để xử lý)
Ví dụ dưới đây minh hoạ cách tạo file input.js
có require
myjquery.js
input.js
var jQuery = require("./myjquery.js");
var text = jQuery({
name: "がお",
age: 18,
});
console.log(text); // ?name=%E3%81%8C%E3%81%8A&age=18
Sử dụng Browserify
để chuyển đổi phần code này, ta sẽ có đoạn code sau:
output.js
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var jQuery = require("./myjquery.js");
var text = jQuery({
name: "がお",
age: 18,
});
console.log(text); // ?name=%E3%81%8C%E3%81%8A&age=18
},{"./myjquery.js":2}],2:[function(require,module,exports){
/* bỏ cách gán trực tiếp cho window object
window.jQuery = jQuery;
window.$ = jQuery;
*/
// Thay vào đó dùng module.exports
module.exports = jQuery;
// Module để chuyển 1 string tiếng Nhật sang query string
function jQuery(obj){
var keys = Object.keys(obj);
if(!keys.length) return "";
return "?" + keys.map(function(key){
return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
}).join("&");
}
},{}]},{},[1]);
Đoạn code đã được chuyển sang dạng khá dễ hiểu.
Đến đây, như đã nói ở phần 1, hãy thử nhấn F12, mở tool của developer, copy paste đoạn code của output.js
vào console window và chạy thử.
Đoạn code sẽ chạy không lỗi trên trình duyệt và string "?name=%E3%81%8C%E3%81%8A&age=18"
sẽ được xuất ra ở console window.
Và như vậy, nếu dùng Browserify
để xử lý chuyển đổi tự động CommonJS module
thì nó có thể dùng được trên trình duyệt.
Phần 2 đến đây là kết thúc.
Mặc dù cách chuyển đổi chưa được giới thiệu nhưng qua phần này các bạn có thể hiểu được khái niệm cơ bản của cách dùng module tương đối hiện đại.
Trong phần 3 sẽ giới thiệu về cách dùng Node.js
và Browserify
để chuyển đổi.
All rights reserved