Chi tiết về JavaScript Modules ES6

Chi tiết về JavaScript Modules ES6

Mục tiêu của Modules:

Trước khi ES6 Modules ra đời, JavaScript không có hệ thống module tích hợp sẵn. Các nhà phát triển thường phải dựa vào các thư viện của bên thứ ba như CommonJS (sử dụng trong Node.js) hoặc AMD để quản lý code thành các phần độc lập. ES6 Modules được giới thiệu để cung cấp một giải pháp tiêu chuẩn, giúp:

  • Tính đóng gói (Encapsulation): Giữ cho code bên trong một module được cô lập, tránh xung đột tên biến và hàm với các phần code khác.
  • Tổ chức code tốt hơn: Chia ứng dụng thành các module logic, dễ dàng quản lý, bảo trì và tái sử dụng.
  • Quản lý dependencies (phụ thuộc): Dễ dàng xác định và import các module cần thiết cho một module cụ thể.
  • Khả năng tái sử dụng code: Các module có thể được import và sử dụng lại trong nhiều phần khác nhau của ứng dụng hoặc thậm chí trong các dự án khác.

Các khái niệm cốt lõi của ES6 Modules:

export (Xuất khẩu):

  • Từ khóa export được sử dụng để đánh dấu các biến, hàm, lớp (classes), hoặc bất kỳ thực thể JavaScript nào khác mà bạn muốn module này cung cấp cho các module khác sử dụng.
  • Có hai loại export chính:
  • Named Exports (Xuất khẩu có tên): Cho phép bạn xuất khẩu nhiều thực thể từ một module, mỗi thực thể có một tên riêng.
  • Default Export (Xuất khẩu mặc định): Cho phép bạn xuất khẩu một giá trị duy nhất (thường là một hàm hoặc một lớp) làm giá trị mặc định của module.

import (Nhập khẩu):

  • Từ khóa import được sử dụng trong một module để sử dụng các thực thể đã được export từ các module khác.
  • Cú pháp import phải chỉ định đường dẫn đến module mà bạn muốn nhập khẩu. Đường dẫn này có thể là:
  • Đường dẫn tương đối: Bắt đầu bằng ./ (thư mục hiện tại) hoặc ../ (thư mục cha). Thường được sử dụng cho các module trong cùng dự án.
  • Đường dẫn tuyệt đối (tùy thuộc vào môi trường): Trong một số môi trường (ví dụ: với module bundler như Webpack hoặc Parcel), bạn có thể cấu hình để sử dụng đường dẫn tuyệt đối hoặc tên module trực tiếp (ví dụ: import React from ‘react’).

Chi tiết về export:

a) Named Exports:

Bạn có thể xuất khẩu nhiều thực thể bằng cách thêm từ khóa export trước khai báo của chúng.

// moduleA.js
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export class Calculator {
  constructor() {
    this.result = 0;
  }
  add(num) {
    this.result += num;
  }
  getResult() {
    return this.result;
  }
}

const message = "Hello from module A"; // Biến này không được export, nên chỉ có thể sử dụng trong moduleA.js
JavaScript

Bạn cũng có thể xuất khẩu các thực thể đã khai báo trước đó bằng cách sử dụng cú pháp xuất khẩu theo danh sách:

// moduleB.js
const E = 2.71828;
function multiply(a, b) {
  return a * b;
}

export { E, multiply };
JavaScript

b) Default Export:

  • Mỗi module chỉ có thể có một default export.
  • Thường được sử dụng khi bạn muốn xuất khẩu một thành phần chính của module (ví dụ: một hàm, một lớp, hoặc một đối tượng).
  • Khi import một default export, bạn có thể đặt cho nó bất kỳ tên biến nào bạn muốn.
// moduleC.js
const greeting = (name) => {
  return `Chào bạn, ${name}!`;
};

export default greeting;

// moduleD.js
class User {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    return `Xin chào, tôi là ${this.name}`;
  }
}

export default User;

// moduleE.js
const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
};

export default config;
JavaScript

Chi tiết về import:

a) Importing Named Exports:

Sử dụng dấu ngoặc nhọn {} để import các thực thể cụ thể bằng tên mà chúng đã được export.

// main.js
import { PI, add, Calculator } from './moduleA.js';

console.log(PI); // Output: 3.14159
console.log(add(5, 3)); // Output: 8
const calc = new Calculator();
calc.add(10);
console.log(calc.getResult()); // Output: 10

import { E as EulerNumber, multiply as product } from './moduleB.js'; // Sử dụng 'as' để đổi tên khi import

console.log(EulerNumber); // Output: 2.71828
console.log(product(2, 4)); // Output: 8
JavaScript

b) Importing Default Export:

Không sử dụng dấu ngoặc nhọn {} khi import một default export. Bạn có thể đặt bất kỳ tên biến nào cho giá trị được import.

// app.js
import greet from './moduleC.js'; // 'greet' là tên biến do chúng ta đặt
import UserClass from './moduleD.js'; // 'UserClass' là tên biến do chúng ta đặt
import appConfig from './moduleE.js'; // 'appConfig' là tên biến do chúng ta đặt

console.log(greet('Alice')); // Output: Chào bạn, Alice!
const user = new UserClass('Bob');
console.log(user.sayHello()); // Output: Xin chào, tôi là Bob
console.log(appConfig.apiUrl); // Output: https://api.example.com
console.log(appConfig.timeout); // Output: 5000
JavaScript

c) Importing cả Named và Default Exports:

Bạn có thể import cả named và default exports từ cùng một module. default export phải được liệt kê trước, không nằm trong dấu ngoặc nhọn.

// moduleF.js
export const message = "Hello from F";
const mainFunction = () => {
  console.log("This is the main function");
};
export default mainFunction;

// mainApp.js
import myFunction, { message as msg } from './moduleF.js';

myFunction(); // Output: This is the main function
console.log(msg); // Output: Hello from F
JavaScript

d) Importing toàn bộ module vào một object:

Bạn có thể import tất cả các exports (cả named và default) của một module vào một object bằng cách sử dụng toán tử * và từ khóa as.

// moduleG.js
export const version = '1.0';
export function log(text) {
  console.log(`LOG: ${text}`);
}
const utility = {
  double: (n) => n * 2
};
export default utility;

// mainUtil.js
import * as utils from './moduleG.js';

console.log(utils.version); // Output: 1.0
utils.log('Application started'); // Output: LOG: Application started
console.log(utils.default.double(5)); // Output: 10 (default export được truy cập qua thuộc tính 'default')
JavaScript

Lưu ý quan trọng về đường dẫn module:

  • Khi sử dụng đường dẫn tương đối (./ hoặc ../), trình duyệt hoặc module bundler sẽ tìm kiếm file module dựa trên vị trí hiện tại của file đang import.
  • Khi làm việc với các thư viện bên ngoài (ví dụ: react, lodash), bạn thường chỉ cần sử dụng tên module trực tiếp (import React from ‘react’). Các module bundler sẽ được cấu hình để tìm kiếm các thư viện này trong thư mục node_modules (nếu bạn đang sử dụng npm hoặc yarn).

Môi trường thực thi của ES6 Modules:

Trình duyệt: Để sử dụng ES6 Modules trực tiếp trong trình duyệt, bạn cần thêm thuộc tính type=”module” vào thẻ <script>.

<!DOCTYPE html>
<html>
<head>
  <title>ES6 Modules in Browser</title>
</head>
<body>
  <script type="module" src="main.js"></script>
</body>
</html>
HTML

Node.js: Node.js đã hỗ trợ ES6 Modules từ phiên bản 13.2.0 (ổn định từ phiên bản 14). Bạn có thể sử dụng ES6 Modules bằng cách: Lưu file JavaScript với đuôi .mjs (thay vì .js). Hoặc, trong file package.json của dự án, thêm “type”: “module”. Sau đó, các file .js sẽ được hiểu là ES6 Modules.

Ví dụ tổng hợp:

mathUtils.js:

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
export default multiply; // Multiply là default export
JavaScript

calculator.js:

import multiply, { add, subtract } from './mathUtils.js';

class Calculator {
  constructor(initialValue = 0) {
    this.value = initialValue;
  }

  add(num) {
    this.value = add(this.value, num);
  }

  subtract(num) {
    this.value = subtract(this.value, num);
  }

  multiply(num) {
    this.value = multiply(this.value, num);
  }

  getResult() {
    return this.value;
  }
}

export default Calculator;
JavaScript

main.js (hoặc index.mjs nếu dùng Node.js với “type”: “module”):

import Calculator from './calculator.js';
import { add as sum } from './mathUtils.js';

const myCalculator = new Calculator(10);
myCalculator.add(5);
myCalculator.subtract(2);
myCalculator.multiply(3);
console.log('Result:', myCalculator.getResult()); // Output: Result: 39

console.log('Sum of 7 and 8:', sum(7, 8)); // Output: Sum of 7 and 8: 15
JavaScript

Bài tập thực hành:

  • Tạo ba file: greetings.js, farewell.js, và app.js.
  • Trong greetings.js, export một hàm có tên sayHello nhận vào một tên và trả về chuỗi “Xin chào, [tên]!”.
  • Trong farewell.js, export một biến hằng số có tên GOODBYE_MESSAGE với giá trị “Tạm biệt!”. Cũng export một hàm mặc định (default export) có tên sayGoodbye nhận vào một tên và in ra “Chúc [tên] mọi điều tốt lành!”.
  • Trong app.js, import hàm sayHello và hằng số GOODBYE_MESSAGE từ greetings.js và farewell.js. Gọi hàm sayHello với một tên bất kỳ và in ra kết quả. In ra giá trị của GOODBYE_MESSAGE. Sau đó, import hàm mặc định sayGoodbye (đặt tên cho nó là bye) từ farewell.js và gọi nó với một tên khác.

Lời kết:

ES6 Modules là một phần không thể thiếu của JavaScript hiện đại. Việc nắm vững cách sử dụng export và import sẽ giúp bạn tổ chức code một cách hiệu quả, tái sử dụng code dễ dàng hơn và làm việc tốt hơn với các thư viện và framework phổ biến như React. Hãy thực hành các ví dụ và bài tập để củng cố kiến thức nhé! Nếu bạn có bất kỳ câu hỏi nào khác, đừng ngần ngại hỏi.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *