NTM Solutions

Bài đăng nổi bật

🚀 Laravel 12 (2026) — Từ PHP Core đến Full Stack📘

Nếu cập nhật theo Laravel 12 LTS (2026) thì mình sẽ chia thành các phần như sau để người học đi từ PHP Core → Laravel → Triển khai dự án th...

Thứ Tư, 14 tháng 10, 2026

🚀Bài 04 — Module & Import / Export trong JavaScript


1. Module là gì?

Trong các bài trước, chúng ta thường viết JavaScript trong một file:

resources/
└── js/
    └── app.js

Cách này phù hợp với những ví dụ đơn giản.

Nhưng hãy tưởng tượng ứng dụng Laravel của chúng ta có các chức năng:

  • Quản lý bài viết.

  • Quản lý danh mục.

  • Tìm kiếm.

  • Đăng nhập.

  • Hiển thị thông báo.

  • Gọi Laravel REST API.

Nếu tất cả code đều nằm trong app.js, file này sẽ ngày càng dài và khó bảo trì.

Module cho phép chúng ta chia code thành nhiều file, mỗi file đảm nhiệm một nhiệm vụ riêng.

Ví dụ:

resources/
└── js/
    ├── app.js
    ├── modules/
    │   ├── posts.js
    │   ├── users.js
    │   ├── search.js
    │   └── notification.js
    └── services/
        └── api.js

Mỗi file có thể xuất những thành phần cần sử dụng và nhập các thành phần từ file khác.

Hai từ khóa quan trọng nhất là:

  • export: xuất dữ liệu, hàm hoặc class để module khác sử dụng.

  • import: nhập dữ liệu, hàm hoặc class từ module khác.


2. Export — xuất dữ liệu từ một module

Có hai cách xuất thường gặp:

  1. Named Export.

  2. Default Export.

2.1. Named Export

Tạo file resources/js/modules/characters.js:

export const doraemon = {
    name: 'Doraemon',
    type: 'Robot mèo',
    color: 'blue'
};

export const nobita = {
    name: 'Nobita',
    type: 'Học sinh',
    color: 'yellow'
};

export function sayHello(name) {
    return `Xin chào ${name}!`;
}

Trong file này, chúng ta đã xuất ba thành phần:

  • Biến doraemon.

  • Biến nobita.

  • Hàm sayHello().

Các module khác có thể nhập những thành phần này.

Lưu ý: export ở đây là Named Export vì mỗi thành phần được xuất bằng tên của nó.

2.2. Default Export

Một module cũng có thể xuất một thành phần mặc định.

Tạo file resources/js/modules/pikachu.js:

const pikachu = {
    name: 'Pikachu',
    type: 'Electric',
    power: 100
};

export default pikachu;

Ở đây, pikachu được xuất bằng export default.

Mỗi module chỉ có một default export, nhưng có thể có nhiều Named Export.


3. Import — nhập dữ liệu từ module khác

3.1. Import Named Export

Tạo file resources/js/app.js:

import {
    doraemon,
    nobita,
    sayHello
} from './modules/characters.js';

console.log(doraemon);
console.log(nobita);

console.log(sayHello('Nobita'));

Kết quả:

{
    name: 'Doraemon',
    type: 'Robot mèo',
    color: 'blue'
}

{
    name: 'Nobita',
    type: 'Học sinh',
    color: 'yellow'
}

Xin chào Nobita!

Cú pháp:

import { tenThanhPhan } from './duong-dan-module.js';

Tên được import phải khớp với tên Named Export.

Ví dụ, nếu module xuất doraemon, chúng ta không thể import trực tiếp bằng tên robotMeo nếu chưa đổi tên khi import.

3.2. Import Default Export

Để nhập Pikachu:

import pikachu from './modules/pikachu.js';

console.log(pikachu.name);
console.log(pikachu.power);

Kết quả:

Pikachu
100

Điểm khác biệt: Default Export có thể được nhập bằng một tên khác.

Ví dụ:

import pokemon from './modules/pikachu.js';

console.log(pokemon.name);

Code vẫn hoạt động vì pokemon chỉ là tên biến chúng ta chọn khi import Default Export.


4. Phân biệt Named Export và Default Export

Đặc điểmNamed ExportDefault Export
Số lượng trong một moduleCó thể nhiềuTối đa một
Khi importThường dùng {}Không cần {}
Tên khi importTheo tên export hoặc đổi tên rõ ràngCó thể tự đặt
Phù hợpNhiều hàm, biến liên quanMột thành phần chính

Ví dụ kết hợp cả hai cách.

File resources/js/modules/pokemon.js:

export const pokemonType = 'Electric';

export function attack() {
    return 'Thunderbolt!';
}

export default {
    name: 'Pikachu',
    power: 100
};

File resources/js/app.js:

import pikachu, {
    pokemonType,
    attack
} from './modules/pokemon.js';

console.log(pikachu.name);
console.log(pokemonType);
console.log(attack());

Kết quả:

Pikachu
Electric
Thunderbolt!

Chúng ta có thể import Default Export và Named Export trong cùng một câu lệnh.


5. Đổi tên khi Import

Giả sử module xuất hàm sayHello():

export function sayHello(name) {
    return `Xin chào ${name}!`;
}

Khi import, chúng ta có thể đổi tên:

import {
    sayHello as greeting
} from './modules/characters.js';

console.log(greeting('Doraemon'));

Kết quả:

Xin chào Doraemon!

Từ khóa as cho phép đổi tên thành phần khi import.

Cách này hữu ích khi hai module có các thành phần trùng tên hoặc khi muốn tên gọi rõ nghĩa hơn trong file đang sử dụng.


6. Import tất cả bằng dấu *

Giả sử file characters.js có nhiều Named Export:

export const doraemon = {
    name: 'Doraemon'
};

export const nobita = {
    name: 'Nobita'
};

export const shizuka = {
    name: 'Shizuka'
};

Chúng ta có thể nhập tất cả Named Export:

import * as characters from './modules/characters.js';

console.log(characters.doraemon.name);
console.log(characters.nobita.name);
console.log(characters.shizuka.name);

Kết quả:

Doraemon
Nobita
Shizuka

characters là một namespace chứa các Named Export của module.

Lưu ý: cú pháp import * as ... không nhập Default Export như một thuộc tính mặc định của namespace theo cách này; nếu cần, hãy import Default Export riêng.


7. Module có thể xuất Function, Object, Array và Class

Module không chỉ xuất biến đơn giản.

7.1. Xuất Function

File resources/js/modules/math.js:

export function add(a, b) {
    return a + b;
}

export function multiply(a, b) {
    return a * b;
}

Sử dụng:

import { add, multiply } from './modules/math.js';

console.log(add(10, 20));
console.log(multiply(10, 20));

7.2. Xuất Array

File resources/js/modules/characters.js:

export const characters = [
    'Doraemon',
    'Nobita',
    'Shizuka',
    'Pikachu',
    'Conan'
];

Sử dụng:

import { characters } from './modules/characters.js';

characters.forEach(character => {
    console.log(character);
});

7.3. Xuất Class

File resources/js/modules/Character.js:

export default class Character {
    constructor(name, power) {
        this.name = name;
        this.power = power;
    }

    introduce() {
        return `${this.name} có sức mạnh ${this.power}`;
    }
}

Sử dụng:

import Character from './modules/Character.js';

const pikachu = new Character('Pikachu', 100);

console.log(pikachu.introduce());

Kết quả:

Pikachu có sức mạnh 100

8. Tổ chức JavaScript trong Laravel 13

Đến đây, chúng ta sẽ áp dụng Module vào cấu trúc Laravel thực tế.

Thay vì để toàn bộ code trong app.js, hãy chia thành các file theo nhiệm vụ.

resources/
└── js/
    ├── app.js
    ├── modules/
    │   ├── characters.js
    │   ├── posts.js
    │   └── search.js
    └── services/
        └── api.js

Ý nghĩa:

  • app.js: điểm khởi đầu của JavaScript.

  • characters.js: dữ liệu minh họa nhân vật.

  • posts.js: xử lý danh sách bài viết.

  • search.js: chức năng tìm kiếm.

  • api.js: nơi tổ chức những hàm gọi API.

Trong ứng dụng thực tế, không nhất thiết phải tạo đủ tất cả file. Chỉ nên tách module khi có chức năng cần tái sử dụng hoặc cần quản lý độc lập.

Bước 1 — Tạo module dữ liệu bài viết

Tạo file resources/js/modules/posts.js:

export const posts = [
    {
        id: 1,
        title: 'Laravel 13',
        author: 'Doraemon',
        views: 120
    },
    {
        id: 2,
        title: 'JavaScript ES6+',
        author: 'Nobita',
        views: 80
    },
    {
        id: 3,
        title: 'Vue cơ bản',
        author: 'Pikachu',
        views: 200
    }
];

export function getPostById(id) {
    return posts.find(post => post.id === id);
}

export function getTotalViews() {
    return posts.reduce(
        (total, post) => total + post.views,
        0
    );
}

Module này xuất:

  • posts: danh sách bài viết.

  • getPostById(): tìm bài viết theo ID.

  • getTotalViews(): tính tổng lượt xem.

Bước 2 — Tạo module tìm kiếm

Tạo file resources/js/modules/search.js:

export function searchPosts(posts, keyword) {
    const normalizedKeyword = keyword.trim().toLowerCase();

    return posts.filter(post =>
        post.title.toLowerCase().includes(normalizedKeyword)
    );
}

Hàm tìm kiếm nhận danh sách và từ khóa, sau đó trả về danh sách phù hợp.

Hàm không phụ thuộc vào một Array cố định nên có thể tái sử dụng ở nhiều nơi.

Bước 3 — Import các module trong app.js

Mở resources/js/app.js:

import {
    posts,
    getPostById,
    getTotalViews
} from './modules/posts.js';

import {
    searchPosts
} from './modules/search.js';

console.log('Danh sách bài viết:', posts);

console.log('Bài viết số 2:', getPostById(2));

console.log('Tổng lượt xem:', getTotalViews());

console.log('Kết quả tìm kiếm:', searchPosts(posts, 'javascript'));

Khi Laravel nạp app.js thông qua Vite, các module được import sẽ được xử lý theo quan hệ phụ thuộc.

Chúng ta không cần đưa từng file module vào Blade bằng một thẻ script riêng.


9. Thực hành: tạo trang tìm kiếm bài viết bằng Module

Bây giờ, hãy xây dựng một ví dụ hoàn chỉnh.

Bước 1 — Tạo file posts.js

Đường dẫn:

resources/js/modules/posts.js

export const posts = [
    { id: 1, title: 'Laravel 13', author: 'Doraemon', views: 120 },
    { id: 2, title: 'JavaScript hiện đại', author: 'Nobita', views: 80 },
    { id: 3, title: 'Vue cơ bản', author: 'Shizuka', views: 200 },
    { id: 4, title: 'React cơ bản', author: 'Pikachu', views: 150 },
    { id: 5, title: 'REST API', author: 'Conan', views: 90 }
];

export function getTotalViews(items) {
    return items.reduce((total, post) => total + post.views, 0);
}

Bước 2 — Tạo file search.js

Đường dẫn:

resources/js/modules/search.js

export function searchPosts(posts, keyword) {
    const query = keyword.trim().toLowerCase();

    return posts.filter(post => {
        const title = post.title.toLowerCase();
        const author = post.author.toLowerCase();

        return title.includes(query) || author.includes(query);
    });
}

Hàm tìm kiếm theo cả tiêu đề và tên nhân vật.

Bước 3 — Tạo file app.js

Đường dẫn:

resources/js/app.js

import { posts, getTotalViews } from './modules/posts.js';
import { searchPosts } from './modules/search.js';

const searchInput = document.querySelector('#searchInput');
const postList = document.querySelector('#postList');
const totalViews = document.querySelector('#totalViews');

function renderPosts(items) {
    if (!postList) {
        return;
    }

    postList.replaceChildren();

    if (items.length === 0) {
        const li = document.createElement('li');
        li.textContent = 'Không tìm thấy bài viết.';
        postList.append(li);
        return;
    }

    items.forEach(post => {
        const li = document.createElement('li');
        const title = document.createElement('strong');
        const author = document.createElement('span');

        title.textContent = post.title;
        author.textContent = ` — Tác giả: ${post.author} — ${post.views} lượt xem`;

        li.append(title, author);
        postList.append(li);
    });
}

function updateTotalViews(items) {
    if (totalViews) {
        totalViews.textContent = getTotalViews(items);
    }
}

function handleSearch() {
    const keyword = searchInput?.value ?? '';
    const filteredPosts = searchPosts(posts, keyword);

    renderPosts(filteredPosts);
    updateTotalViews(filteredPosts);
}

if (searchInput) {
    searchInput.addEventListener('input', handleSearch);
}

renderPosts(posts);
updateTotalViews(posts);

Bước 4 — Tạo Blade View

Tạo file:

resources/views/javascript.blade.php

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>JavaScript Modules - Laravel 13</title>

    @vite(['resources/js/app.js'])
</head>
<body>
    <h1>Thư viện bài học</h1>

    <label for="searchInput">Tìm kiếm:</label>

    <input
        type="search"
        id="searchInput"
        placeholder="Nhập tên bài học hoặc nhân vật..."
    >

    <h2>Danh sách bài viết</h2>

    <ul id="postList"></ul>

    <p>
        Tổng lượt xem:
        <strong id="totalViews">0</strong>
    </p>
</body>
</html>

Bước 5 — Khai báo Route

Trong routes/web.php:

use Illuminate\Support\Facades\Route;

Route::view('/javascript', 'javascript');

Nếu đã có Route /javascript từ Bài 02 hoặc Bài 03, hãy sử dụng lại Route đó, không khai báo trùng.

Bước 6 — Chạy ứng dụng

Mở terminal thứ nhất:

npm run dev

Mở terminal thứ hai:

php artisan serve

Truy cập:

http://127.0.0.1:8000/javascript

Thử nhập:

  • Laravel

  • Doraemon

  • Vue

  • Pikachu

Danh sách và tổng lượt xem sẽ được cập nhật theo kết quả tìm kiếm.

Điều quan trọng: Blade chỉ cần nạp app.js. Các module còn lại được app.js import. Đây là cách tổ chức JavaScript mà chúng ta sẽ tiếp tục sử dụng trong những ứng dụng Laravel lớn hơn.


10. Module và Vite hoạt động như thế nào?

Trong Laravel, Vite giúp xử lý các file JavaScript và quan hệ import giữa chúng.

javascript.blade.php
        │
        │ @vite()
        ▼
      app.js
        │
        ├── posts.js
        │
        └── search.js
                │
                ▼
          JavaScript chạy
          trong trình duyệt

Trong chế độ phát triển, Vite hỗ trợ cập nhật nhanh khi chúng ta chỉnh sửa code. Khi build production, Vite xử lý các module để tạo tài nguyên frontend phục vụ triển khai.

Không cần tự nối nội dung tất cả file JavaScript vào một file bằng tay.

Có thể import trực tiếp trong trình duyệt không?

Có. JavaScript hiện đại hỗ trợ ES Modules với type="module":

<script type="module" src="/js/app.js"></script>

Tuy nhiên, trong project Laravel sử dụng Vite, hãy ưu tiên:

@vite(['resources/js/app.js'])

Vite sẽ đảm nhiệm việc phục vụ và build tài nguyên theo cấu hình của dự án.


11. Một số lỗi thường gặp

Lỗi 1 — Sai đường dẫn import

import { posts } from './post.js';

Nhưng file thực tế là posts.js.

Hãy kiểm tra đúng tên file và đường dẫn tương đối tính từ file đang import.

Lỗi 2 — Quên dấu ngoặc nhọn

Nếu module xuất:

export const doraemon = {
    name: 'Doraemon'
};

Thì cách import đúng là:

import { doraemon } from './modules/characters.js';

Lỗi 3 — Dùng sai chữ hoa, chữ thường

import { Doraemon } from './modules/characters.js';

Nếu tên export là doraemon, đoạn trên không đúng vì JavaScript phân biệt chữ hoa và chữ thường.

Lỗi 4 — Hai module import lẫn nhau

Ví dụ:

posts.js → search.js
search.js → posts.js

Đây là quan hệ phụ thuộc vòng. Một số trường hợp vẫn hoạt động, nhưng có thể gây lỗi khởi tạo hoặc làm code khó hiểu. Nên thiết kế module theo hướng mỗi module có nhiệm vụ rõ ràng, tránh phụ thuộc vòng không cần thiết.

Lỗi 5 — Import nhưng không Export

Nếu file khác không xuất thành phần mà bạn đang import, JavaScript sẽ báo lỗi module.

Hãy kiểm tra tên và loại export trong file nguồn.


12. Bài tập cuối bài

Bài tập 1 — Tạo module nhân vật

Tạo file characters.js và xuất ba nhân vật:

  • Doraemon

  • Nobita

  • Pikachu

Mỗi nhân vật có name và power.

Bài tập 2 — Import và hiển thị

Trong app.js, import danh sách nhân vật và in tên từng nhân vật ra Console.

Bài tập 3 — Tạo module toán học

Tạo file math.js, xuất hai hàm:

  • add(a, b)

  • multiply(a, b)

Import vào app.js và kiểm tra kết quả.

Bài tập 4 — Tách chức năng tìm kiếm

Tách hàm tìm kiếm bài viết khỏi app.js thành search.js. Sau đó import lại và đảm bảo chức năng vẫn hoạt động.

Bài tập 5 — Tự tổ chức project

Tạo cấu trúc:

resources/js/
├── app.js
├── modules/
│   ├── characters.js
│   └── posts.js
└── services/
    └── api.js

Chưa cần gọi API thật. Hãy tập trung vào việc xuất và nhập dữ liệu giữa các file.


13. Tổng kết

Trong bài này, chúng ta đã học:

Kiến thứcCông dụng
exportXuất thành phần từ module
export defaultXuất thành phần mặc định
importNhập thành phần từ module khác
asĐổi tên khi import/export
import * asGom Named Export vào namespace
ES ModulesChia code thành nhiều file
ViteHỗ trợ phát triển và build frontend trong Laravel

Ghi nhớ: app.js không nhất thiết phải chứa toàn bộ logic của ứng dụng. Nó có thể đóng vai trò điểm khởi đầu, nhập các module cần thiết rồi khởi tạo chức năng.

Khi chuyển sang Vue và React, chúng ta vẫn sử dụng import và export để tổ chức Component, hàm xử lý dữ liệu và các module dùng chung.

Bài 05 — Promise & Async/Await: học cách xử lý các công việc bất đồng bộ, chờ kết quả API và quản lý lỗi khi giao tiếp giữa JavaScript với Laravel.

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS