Hiểu các module và các câu lệnh nhập và xuất trong JavaScript
Trong những ngày đầu của Web, các trang web chủ yếu bao gồm HTML và CSS . Nếu bất kỳ JavaScript nào được tải vào một trang, nó thường ở dạng các đoạn mã nhỏ cung cấp các hiệu ứng và tương tác. Do đó, các chương trình JavaScript thường được viết hoàn toàn trong một file và được tải vào thẻscript
. Một nhà phát triển có thể chia JavaScript thành nhiều file , nhưng tất cả các biến và hàm vẫn sẽ được thêm vào phạm vi global . Nhưng khi các trang web phát triển với sự ra đời của các khung công tác như Angular , React và Vue , và với các công ty tạo ra các ứng dụng web nâng cao thay vì các ứng dụng máy tính để bàn, JavaScript hiện đóng một role quan trọng trong trình duyệt. Do đó, nhu cầu sử dụng mã của bên thứ ba cho các việc phổ biến hơn nhiều, để chia nhỏ mã thành các file module và tránh làm ô nhiễm không gian tên chung.
Đặc tả ECMAScript 2015 đã giới thiệu các module cho ngôn ngữ JavaScript, cho phép sử dụng các câu lệnh import
và export
. Trong hướng dẫn này, bạn sẽ tìm hiểu module JavaScript là gì và cách sử dụng import
và export
để tổ chức mã của bạn.
Lập trình module
Trước khi khái niệm module xuất hiện trong JavaScript, khi một nhà phát triển muốn tổ chức mã của họ thành các phân đoạn, họ sẽ tạo nhiều file và liên kết đến chúng dưới dạng các tập lệnh riêng biệt. Để chứng minh điều này, hãy tạo một index.html
và hai file JavaScript, functions.js
và script.js
.
Tệp index.html
sẽ hiển thị tổng, hiệu, tích và thương của hai số và liên kết đến hai file JavaScript trong thẻ script
. Mở index.html
trong editor và thêm mã sau:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>JavaScript Modules</title> </head> <body> <h1>Answers</h1> <h2><strong id="x"></strong> and <strong id="y"></strong></h2> <h3>Addition</h3> <p id="addition"></p> <h3>Subtraction</h3> <p id="subtraction"></p> <h3>Multiplication</h3> <p id="multiplication"></p> <h3>Division</h3> <p id="division"></p> <script src="functions.js"></script> <script src="script.js"></script> </body> </html>
HTML này sẽ hiển thị giá trị của các biến x
và y
trong tiêu đề h2
và giá trị của các phép toán trên các biến đó trong p
phần tử sau. Thuộc tính id
của các phần tử được đặt cho thao tác DOM , điều này sẽ xảy ra trong file script.js
; file này cũng sẽ đặt các giá trị của x
và y
. Để biết thêm thông tin về HTML, hãy xem loạt bài Cách tạo trang web bằng HTML của ta .
Tệp functions.js
sẽ chứa các hàm toán học sẽ được sử dụng trong tập lệnh thứ hai. Mở file functions.js
và thêm thông tin sau:
function sum(x, y) { return x + y } function difference(x, y) { return x - y } function product(x, y) { return x * y } function quotient(x, y) { return x / y }
Cuối cùng, file script.js
sẽ xác định giá trị của x
và y
, áp dụng các hàm cho chúng và hiển thị kết quả:
const x = 10 const y = 5 document.getElementById('x').textContent = x document.getElementById('y').textContent = y document.getElementById('addition').textContent = sum(x, y) document.getElementById('subtraction').textContent = difference(x, y) document.getElementById('multiplication').textContent = product(x, y) document.getElementById('division').textContent = quotient(x, y)
Sau khi cài đặt và lưu các file này, bạn có thể mở index.html
trong trình duyệt để hiển thị trang web với tất cả kết quả:
Đối với các trang web có một vài tập lệnh nhỏ, đây là một cách hiệu quả để phân chia mã. Tuy nhiên, có một số vấn đề liên quan đến cách tiếp cận này, bao gồm:
- Gây ô nhiễm không gian tên chung : Tất cả các biến bạn đã tạo trong tập lệnh của bạn -
sum
,difference
, v.v. - hiện tồn tại trên đối tượngwindow
. Nếu bạn đã cố gắng sử dụng một biến khác được gọi làsum
trong một file khác, sẽ rất khó để biết giá trị nào sẽ được sử dụng tại bất kỳ thời điểm nào trong các tập lệnh, vì tất cả chúng sẽ sử dụng cùng một biếnwindow.sum
. Cách duy nhất một biến có thể là riêng tư là đặt nó trong một phạm vi hàm. Thậm chí có thể có xung đột giữa mộtid
trong DOM có tên làx
vàvar x
. - Quản lý phụ thuộc : Các tập lệnh sẽ phải được tải theo thứ tự từ trên xuống dưới đảm bảo có các biến chính xác. Việc lưu các tập lệnh dưới dạng các file khác nhau tạo ra ảo giác về sự tách biệt, nhưng về cơ bản nó giống như việc có một
<script>
nội tuyến duy nhất trong trang trình duyệt.
Trước khi ES6 thêm các module root vào ngôn ngữ JavaScript, cộng đồng đã cố gắng đưa ra một số giải pháp. Các giải pháp đầu tiên được viết bằng JavaScript vani, chẳng hạn như viết tất cả mã trong các đối tượng hoặc ngay lập tức gọi các biểu thức hàm (IIFE) và đặt chúng trên một đối tượng duy nhất trong không gian tên chung. Đây là một cải tiến đối với phương pháp tiếp cận nhiều tập lệnh, nhưng vẫn có những vấn đề tương tự khi đặt ít nhất một đối tượng vào không gian tên chung và không làm cho vấn đề chia sẻ mã nhất quán giữa các bên thứ ba trở nên dễ dàng hơn.
Sau đó, một số giải pháp module đã xuất hiện: CommonJS , một cách tiếp cận đồng bộ đã được triển khai trong Node.js , Định nghĩa module không đồng bộ (AMD) , là một cách tiếp cận không đồng bộ và Định nghĩa module chung (UMD) , được dự định là một cách tiếp cận đã hỗ trợ cả hai kiểu trước đó.
Sự ra đời của các giải pháp này đã giúp các nhà phát triển dễ dàng chia sẻ và sử dụng lại mã dưới dạng các gói , module có thể được phân phối và chia sẻ, chẳng hạn như các mã được tìm thấy trên npm . Tuy nhiên, vì có nhiều giải pháp và không có giải pháp nào có nguồn root từ JavaScript, nên các công cụ như Babel , Webpack hoặc Browserify phải được triển khai để sử dụng các module trong trình duyệt.
Do có nhiều vấn đề với cách tiếp cận nhiều file và sự phức tạp của các giải pháp được đề xuất, các nhà phát triển đã quan tâm đến việc đưa cách tiếp cận lập trình module vào ngôn ngữ JavaScript. Do đó, ECMAScript 2015 hỗ trợ việc sử dụng các module JavaScript.
Mô-đun là một gói mã hoạt động như một giao diện để cung cấp chức năng cho các module khác sử dụng, cũng như có thể dựa vào chức năng của các module khác. Mô-đun xuất để cung cấp mã và nhập để sử dụng mã khác. Các module rất hữu ích vì chúng cho phép các nhà phát triển sử dụng lại mã, chúng cung cấp một giao diện ổn định, nhất quán mà nhiều nhà phát triển có thể sử dụng và chúng không làm ô nhiễm không gian tên global .
Các module (đôi khi được gọi là module ECMAScript hoặc Mô-đun ES) hiện có sẵn trong JavaScript và trong phần còn lại của hướng dẫn này, bạn sẽ khám phá cách sử dụng và triển khai chúng trong mã của bạn .
Mô-đun JavaScript root
Các module trong JavaScript sử dụng các từ khóa import
và export
:
-
import
: Dùng để đọc mã được xuất từ module khác. -
export
: Dùng để cung cấp mã cho các phân hệ khác.
Để trình bày cách sử dụng, hãy cập nhật file functions.js
của bạn thành một module và xuất các chức năng. Bạn sẽ thêm export
vào trước mỗi chức năng, điều này sẽ làm cho chúng có sẵn cho bất kỳ module nào khác.
Thêm mã được đánh dấu sau vào file của bạn:
export function sum(x, y) { return x + y } export function difference(x, y) { return x - y } export function product(x, y) { return x * y } export function quotient(x, y) { return x / y }
Bây giờ, trong script.js
, bạn sẽ sử dụng import
để truy xuất mã từ module functions.js
ở đầu file .
Lưu ý : import
phải luôn ở đầu file trước bất kỳ mã nào khác và cũng cần phải bao gồm đường dẫn tương đối ( ./
trong trường hợp này).
Thêm mã được đánh dấu sau vào script.js
:
import { sum, difference, product, quotient } from './functions.js' const x = 10 const y = 5 document.getElementById('x').textContent = x document.getElementById('y').textContent = y document.getElementById('addition').textContent = sum(x, y) document.getElementById('subtraction').textContent = difference(x, y) document.getElementById('multiplication').textContent = product(x, y) document.getElementById('division').textContent = quotient(x, y)
Lưu ý các hàm riêng lẻ được nhập bằng cách đặt tên chúng trong dấu ngoặc nhọn.
Để đảm bảo mã này được tải dưới dạng module và không phải là tập lệnh thông thường, hãy thêm type="module"
vào các thẻ script
trong index.html
. Bất kỳ mã nào sử dụng import
hoặc export
đều phải sử dụng thuộc tính này:
... <script type="module" src="functions.js"></script> <script type="module" src="script.js"></script>
Đến đây, bạn có thể reload trang với các bản cập nhật và trang web bây giờ sẽ sử dụng các module . Hỗ trợ trình duyệt là rất cao, nhưng caniuse có sẵn để kiểm tra mà các trình duyệt hỗ trợ nó. Lưu ý nếu bạn đang xem file dưới dạng liên kết trực tiếp đến file local , bạn sẽ gặp phải lỗi này:
OutputAccess to script at 'file:///Users/your_file_path/script.js' from origin 'null' has been blocked by CORS policy: Cross-origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
Do chính sách CORS , Mô-đun phải được sử dụng trong môi trường server mà bạn có thể cài đặt local với server http hoặc trên internet với nhà cung cấp dịch vụ lưu trữ.
Mô-đun khác với các tập lệnh thông thường theo một số cách:
- Mô-đun không thêm bất cứ thứ gì vào phạm vi global (
window
). - Các module luôn ở chế độ nghiêm ngặt .
- Việc tải cùng một module hai lần trong cùng một file sẽ không có hiệu lực, vì các module chỉ được thực thi một lần.
- Mô-đun yêu cầu một môi trường server .
Các module vẫn thường được sử dụng cùng với các gói như Webpack để tăng cường hỗ trợ trình duyệt và các tính năng bổ sung, nhưng chúng cũng có sẵn để sử dụng trực tiếp trong các trình duyệt.
Tiếp theo, bạn sẽ khám phá thêm một số cách mà cú pháp import
và export
được dùng .
Xuất khẩu được đặt tên
Như đã trình bày trước đó, sử dụng cú pháp export
sẽ cho phép bạn nhập từng giá trị đã được xuất theo tên của chúng. Ví dụ: lấy version đơn giản này của functions.js
:
export function sum() {} export function difference() {}
Điều này sẽ cho phép bạn nhập sum
và difference
theo tên bằng cách sử dụng dấu ngoặc nhọn:
import { sum, difference } from './functions.js'
Cũng có thể sử dụng alias để đổi tên hàm. Bạn có thể làm điều này để tránh xung đột đặt tên trong cùng một module . Trong ví dụ này, sum
sẽ được đổi tên để add
và difference
sẽ được đổi tên để subtract
.
import { sum as add, difference as subtract } from './functions.js' add(1, 2) // 3
Gọi add()
ở đây sẽ mang lại kết quả của hàm sum()
.
Sử dụng cú pháp *
, bạn có thể nhập nội dung của toàn bộ module vào một đối tượng. Trong trường hợp này, sum
và difference
sẽ trở thành các phương thức trên đối tượng mathFunctions
.
import * as mathFunctions from './functions.js' mathFunctions.sum(1, 2) // 3 mathFunctions.difference(10, 3) // 7
Tất cả các giá trị nguyên thủy, biểu thức và định nghĩa hàm , hàm không đồng bộ , lớp và lớp khởi tạo đều có thể được xuất, miễn là chúng có mã định danh:
// Primitive values export const number = 100 export const string = 'string' export const undef = undefined export const empty = null export const obj = { name: 'Homer' } export const array = ['Bart', 'Lisa', 'Maggie'] // Function expression export const sum = (x, y) => x + y // Function definition export function difference(x, y) { return x - y } // Asynchronous function export async function getBo