Cách Chuyển Đổi Bảng Html Trong React Sang Tệp Excel: Hướng Dẫn Toàn Diện Và Chi Tiết

Mục lục bài viết

Mở Đầu

Trong môi trường phát triển web hiện đại, việc hiển thị dữ liệu dạng bảng là một nhu cầu phổ biến. React, một thư viện JavaScript mạnh mẽ, cho phép chúng ta xây dựng các component hiển thị dữ liệu một cách linh hoạt và hiệu quả. Tuy nhiên, trong nhiều trường hợp, người dùng cuối cần khả năng xuất dữ liệu ra tệp Excel để phân tích, lưu trữ hoặc chia sẻ. Bài viết này sẽ cung cấp cho bạn một hướng dẫn chi tiết, từ các khái niệm cơ bản đến các giải pháp thực tiễn, giúp bạn chuyển đổi một bảng HTML được render bởi React thành tệp Excel một cách nhanh chóng và tối ưu.

Lưu ý: Bài viết được viết bằng tiếng Việt, tập trung vào từ khóa “react html table to excel”. Nội dung sẽ bao gồm:
– Giới thiệu về nhu cầu xuất Excel trong ứng dụng React.
– Các thư viện phổ biến và cách cài đặt.
– Đánh giá ưu nhược điểm của từng giải pháp.
– Hướng dẫn lập trình chi tiết (code mẫu, giải thích từng bước).
– Các lưu ý về hiệu năng, bảo mật và tương thích trình duyệt.
– Các mẹo nâng cao: tùy chỉnh định dạng, xuất nhiều sheet, xử lý dữ liệu lớn.
– Tổng kết và đề xuất lựa chọn phù hợp.

Bạn sẽ có một tài liệu tham khảo đầy đủ, có thể áp dụng ngay vào dự án thực tế.

1. Tại Sao Cần Xuất Bảng HTML Sang Excel Trong Ứng Dụng React?

1.1. Nhu Cầu Thực Tế

  • Phân tích dữ liệu: Người dùng thường muốn tải dữ liệu về máy để dùng các công cụ như Microsoft Excel, Google Sheets để thực hiện các phép tính phức tạp, vẽ biểu đồ, pivot table.
  • Báo cáo: Nhiều doanh nghiệp yêu cầu báo cáo định kỳ (hàng ngày, tuần, tháng) dưới dạng file Excel.
  • Chia sẻ: Định dạng Excel là chuẩn công nghiệp, dễ dàng chia sẻ qua email, lưu trữ trên hệ thống nội bộ.
  • Offline: Người dùng muốn làm việc offline, không phụ thuộc vào giao diện web.

1.2. Thách Thức Khi Xuất Excel Từ React

  • Không có API native: Trình duyệt không cung cấp API trực tiếp để tạo file Excel.
  • Định dạng phức tạp: Định dạng ô, màu nền, font, công thức… cần được mô tả bằng chuẩn OpenXML.
  • Hiệu năng: Khi bảng dữ liệu lớn (hàng ngàn, cột hàng chục), việc tạo file trên client có thể gây treo UI.
  • Tương thích: Các trình duyệt khác nhau có cách xử lý Blob, download file khác nhau.
  • Bảo mật: Tránh lộ dữ liệu nhạy cảm qua các thư viện bên thứ ba.

2. Tổng Quan Các Giải Pháp Thông Dụng

Thư việnĐộ phổ biếnĐịnh dạng xuấtTính năng nổi bậtNhược điểm
SheetJS (xlsx)Rất cao.xlsx, .xlsb, .csv, .odsHỗ trợ công thức, định dạng, đa sheet, client & serverKích thước bundle lớn (~500KB gz)
react-data-exportTrung bình.xlsxDễ dùng trong React, dựa trên SheetJSHạn chế tùy chỉnh sâu, không hỗ trợ .csv
exceljsCao.xlsxĐịnh dạng phong phú, hỗ trợ streaming (lớn)Cần polyfill cho Blob trong một số trình duyệt
html-table-to-excelThấp.xls (Excel 97-2003)Chuyển đổi nhanh từ table HTMLĐịnh dạng rất hạn chế, không hỗ trợ .xlsx
jsPDF + autoTable + xlsxTrung bìnhPDF + ExcelXuất đồng thời PDF, ExcelPhức tạp khi cấu hình
Custom Server-side (Node.js + ExcelJS)Cao (backend).xlsxXử lý dữ liệu lớn, bảo mậtCần backend, không pure client

2.1. Khi Nào Nên Chọn Giải Pháp Nào?

  • Dữ liệu nhỏ (< 500 dòng) → SheetJS hoặc react-data-export là đủ.
  • Cần định dạng nâng cao (font, màu, border, merge cells) → exceljs hoặc SheetJS (có API chi tiết).
  • Dữ liệu lớn (> 10,000 dòng) → exceljs streaming hoặc server-side để tránh quá tải bộ nhớ trình duyệt.
  • Yêu cầu tải nhanh, bundle nhẹ → html-table-to-excel (nhưng chỉ .xls) hoặc tự viết CSV.
  • Cần đa sheet, công thức, validation → SheetJS hoặc exceljs.

3. Cài Đặt Và Cấu Hình Cơ Bản

3.1. Tạo Project React Mới

npx create-react-app react-table-to-excel-demo
cd react-table-to-excel-demo

3.2. Cài Đặt Thư Viện

3.2.1. SheetJS (xlsx)

npm install xlsx file-saver
  • xlsx chứa core logic.
  • file-saver giúp lưu file Blob trên trình duyệt.

3.2.2. exceljs (tùy chọn)

npm install exceljs file-saver

3.2.3. react-data-export (đơn giản)

npm install react-data-export

3.3. Kiểm Tra Bundle Size

Sau khi cài, chạy npm run build và kiểm tra build/static/js để biết kích thước. Nếu quá lớn, cân nhắc lazy load các module chỉ khi người dùng bấm nút xuất.

// Lazy load example
import React, { lazy, Suspense } from 'react';
const ExportButton = lazy(() => import('./ExportButton')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <ExportButton /> </Suspense> );
}

4. Ví Dụ Thực Tế: Xuất Bảng HTML Sang Excel Bằng SheetJS

4.1. Cấu Trúc Component Table

// src/components/DataTable.jsx
import React from 'react';
import './DataTable.css'; const DataTable = ({ data }) => { return ( <table id="react-html-table" className="styled-table"> <thead> <tr> <th>ID</th> <th>Họ Tên</th> <th>Email</th> <th>Ngày Sinh</th> <th>Quốc Gia</th> </tr> </thead> <tbody> {data.map(item => ( <tr key={item.id}> <td>{item.id}</td> <td>{item.fullName}</td> <td>{item.email}</td> <td>{item.dob}</td> <td>{item.country}</td> </tr> ))} </tbody> </table> );
}; export default DataTable;

Lưu ý: Đặt id="react-html-table" để dễ dàng truy xuất trong JavaScript.

4.2. Hàm Chuyển Đổi Sang Excel

// src/utils/exportExcel.js
import as XLSX from 'xlsx';
import { saveAs } from 'file-saver'; / Convert HTML table to workbook và lưu file Excel. @param {string} tableId - ID của table HTML. @param {string} fileName - Tên file (không cần .xlsx). /
export const exportTableToExcel = (tableId, fileName = 'data') => { // 1. Lấy element table const table = document.getElementById(tableId); if (!table) { console.error(`Table with id "${tableId}" not found!`); return; } // 2. Chuyển đổi table sang worksheet const workbook = XLSX.utils.book_new(); // Tạo workbook mới const worksheet = XLSX.utils.table_to_sheet(table, { // Tùy chọn: bỏ qua hidden rows/cols, preserve styles,… raw: false, // Đọc dữ liệu dưới dạng string, không parse số // Nếu muốn giữ định dạng ngày, có thể dùng raw: true + custom parse }); // 3. Thêm worksheet vào workbook XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); // 4. Tạo Blob và trigger download const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'array', // Để hỗ trợ các công thức, set cellStyles:true nếu cần cellStyles: true, }); const blob = new Blob(wbout, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8', }); saveAs(blob, `${fileName}.xlsx`);
};

4.3. Nút Export Trong Component

// src/components/ExportButton.jsx
import React from 'react';
import { exportTableToExcel } from '../utils/exportExcel'; const ExportButton = ({ tableId, fileName }) => { const handleExport = () => { exportTableToExcel(tableId, fileName); }; return ( <button onClick={handleExport} className="export-btn"> 📥 Xuất Excel </button> );
}; export default ExportButton;

4.4. Kết Hợp Trong Ứng Dụng

// src/App.jsx
import React, { useState, useEffect } from 'react';
import DataTable from './components/DataTable';
import ExportButton from './components/ExportButton';
import './App.css'; function App() { const data, setData = useState(); // Giả lập fetch dữ liệu useEffect(() => { const fetchData = async () => { // Bạn có thể gọi API thực tế ở đây const mock = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, fullName: `Người dùng ${i + 1}`, email: `user${i + 1}@example.com`, dob: `199${i % 10}-0${(i % 12) + 1}-15`, country: 'Việt Nam', 'USA', 'UK', 'Canada'i % 4, })); setData(mock); }; fetchData(); }, ); return ( <div className="App"> <h1>Demo: Xuất Bảng HTML React Sang Excel</h1> <ExportButton tableId="react-html-table" fileName="UserList" /> <DataTable data={data} /> </div> );
} export default App;

4.5. Kiểm Tra Kết Quả

  • Chạy npm start.
  • Trang hiển thị bảng dữ liệu và nút “Xuất Excel”.
  • Nhấn nút → file UserList.xlsx được tải về.
  • Mở file bằng Excel → dữ liệu khớp với bảng HTML.

5. Tùy Chỉnh Định Dạng Excel (Màu, Font, Merge Cells)

Mặc dù XLSX.utils.table_to_sheet chỉ chuyển dữ liệu, chúng ta có thể chỉnh sửa worksheet sau khi tạo.

// Thêm vào exportExcel.js sau khi tạo worksheet
// Định dạng tiêu đề (row 1)
const headerRange = XLSX.utils.decode_range(worksheet'!ref');
for (let C = headerRange.s.c; C <= headerRange.e.c; ++C) { const address = XLSX.utils.encode_cell({ r: 0, c: C }); if (!worksheetaddress) continue; worksheetaddress.s = { font: { bold: true, color: { rgb: 'FFFFFFFF' } }, fill: { fgColor: { rgb: '4F81BD' } }, alignment: { horizontal: 'center' }, };
} // Merge các cột tiêu đề nếu muốn (ví dụ: hợp nhất A1:E1)
worksheet'!merges' = { s: { r: 0, c: 0 }, e: { r: 0, c: 4 } }; // Đặt độ rộng cột tự động
const colWidths = { wch: 6 }, // ID { wch: 20 }, // Họ Tên { wch: 30 }, // Email { wch: 12 }, // Ngày Sinh { wch: 15 }, // Quốc Gia
;
worksheet'!cols' = colWidths;

Giải thích:
– worksheetaddress.s là đối tượng style (cần cellStyles:true khi ghi workbook).
– !merges là mảng các vùng merge.
– !cols giúp đặt chiều rộng cột (đơn vị ký tự).

5.1. Định Dạng Dữ Liệu Kiểu Ngày

Nếu muốn Excel nhận diện cột ngày sinh là kiểu ngày:

// Giả sử cột ngày sinh là cột D (index 3)
for (let R = 1; R <= headerRange.e.r; ++R) { const cellAddress = XLSX.utils.encode_cell({ r: R, c: 3 }); const cell = worksheetcellAddress; if (cell && typeof cell.v === 'string') { const date = new Date(cell.v); if (!isNaN(date)) { cell.t = 'n'; // numeric cell.z = XLSX.SSF.get_table()14; // "m/d/yy" cell.v = (date - new Date(Date.UTC(1899, 11, 30))) / (24 60 60 1000); } }
}

6. Xử Lý Dữ Liệu Lớn: Streaming Với exceljs

React Html Table To Excel
React Html Table To Excel

Khi bảng dữ liệu lên tới vài chục nghìn dòng, việc tạo một mảng lớn trong bộ nhớ có thể làm trình duyệt “đơ”. exceljs cung cấp streaming writer cho phép ghi từng hàng vào file mà không cần giữ toàn bộ workbook trong RAM.

6.1. Cài Đặt

npm install exceljs file-saver

6.2. Code Streaming

// src/utils/exportExcelStreaming.js
import ExcelJS from 'exceljs';
import { saveAs } from 'file-saver'; / Export large dataset using streaming (client-side) @param {Array} data - Mảng dữ liệu (object) @param {string} fileName /
export const exportLargeData = async (data, fileName = 'LargeData') => { const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet('Data'); // Đặt tiêu đề worksheet.columns = { header: 'ID', key: 'id', width: 8 }, { header: 'Họ Tên', key: 'fullName', width: 25 }, { header: 'Email', key: 'email', width: 30 }, { header: 'Ngày Sinh', key: 'dob', width: 15, style: { numFmt: 'dd/mm/yyyy' } }, { header: 'Quốc Gia', key: 'country', width: 15 }, ; // Streaming: addRows trong vòng lặp for (const row of data) { // Chuyển đổi ngày sinh sang Date object nếu cần const dob = new Date(row.dob); worksheet.addRow({ id: row.id, fullName: row.fullName, email: row.email, dob: isNaN(dob) ? null : dob, country: row.country, }); } // Tạo blob const buffer = await workbook.xlsx.writeBuffer(); const blob = new Blob(buffer, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }); saveAs(blob, `${fileName}.xlsx`);
};

6.3. Khi Nào Dùng Streaming?

  • Dữ liệu > 5,000 dòng → giảm tải bộ nhớ.
  • Thực hiện trên thiết bị di động → tránh crash.
  • Cần tạo nhiều sheet → streaming vẫn hỗ trợ.

7. Xuất Dữ Liệu Thành CSV (Giải Pháp Nhẹ Nhất)

Nếu không cần định dạng phong phú, CSV là định dạng nhẹ, tương thích với hầu hết các công cụ.

// src/utils/exportCSV.js
export const exportToCSV = (data, fileName = 'data') => { const headers = 'ID', 'Họ Tên', 'Email', 'Ngày Sinh', 'Quốc Gia'; const rows = data.map(item => item.id, item.fullName, item.email, item.dob, item.country.join(',') ); const csvContent = headers.join(','), ...rows.join('\n'); const blob = new Blob(csvContent, { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `${fileName}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link);
};

CSV phù hợp khi:
– Dữ liệu đơn giản, không có merge cells, màu sắc.
– Muốn giảm kích thước file (CSV thường < 1MB cho 10k dòng).

8. Tích Hợp Vào UI/UX: Thông Báo, Loading, Error Handling

8.1. Hiển Thị Loading Khi Đang Tạo File

// src/components/ExportButton.jsx
import React, { useState } from 'react';
import { exportTableToExcel } from '../utils/exportExcel'; const ExportButton = ({ tableId, fileName }) => { const loading, setLoading = useState(false); const handleExport = async () => { try { setLoading(true); await exportTableToExcel(tableId, fileName); } catch (err) { console.error(err); alert('Có lỗi xảy ra khi xuất file.'); } finally { setLoading(false); } }; return ( <button onClick={handleExport} disabled={loading} className="export-btn"> {loading ? 'Đang tạo file...' : '📥 Xuất Excel'} </button> );
}; export default ExportButton;

8.2. Thông Báo Thành Công

Sử dụng thư viện toast như react-toastify.

npm install react-toastify
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; // trong handleExport
toast.success('File Excel đã được tải về!');

9. Các Vấn Đề Thường Gặp Và Giải Pháp

Vấn đềNguyên nhânGiải pháp
File tải về rỗngTable chưa render khi gọi exportĐảm bảo bảng đã được mount (useEffect hoặc setTimeout)
Định dạng ngày không hiển thịExcel coi ngày là textChuyển sang số serial và set z (numFmt)
Độ rộng cột không tự độngKhông thiết lập !colsĐặt worksheet'!cols' = { wch: 20 }, …
Trình duyệt Safari không tải BlobSafari yêu cầu adownload không hoạt độngDùng FileSaver.saveAs (đã bao gồm polyfill)
Kích thước bundle quá lớnImport toàn bộ xlsxSử dụng import { utils, writeFile } from 'xlsx'; hoặc dynamic import
Khi dữ liệu lớn UI bị treoTạo workbook đồng bộSử dụng exceljs streaming hoặc thực hiện trong Web Worker

9.1. Sử Dụng Web Worker Để Không Khóa UI

// src/workers/excelWorker.js
self.onmessage = async e => { const { data, fileName } = e.data; // Sử dụng exceljs ở đây (được import via importScripts) // Sau khi tạo blob, postMessage lại cho main thread
};

Trong component:

const worker = new Worker(new URL('../workers/excelWorker.js', import.meta.url));
worker.postMessage({ data, fileName: 'LargeData' });
worker.onmessage = e => { const { blob } = e.data; saveAs(blob, 'LargeData.xlsx');
};

10. Bảo Mật Và Quyền Truy Cập Dữ Liệu

  • Không lưu dữ liệu nhạy cảm trong client: Nếu bảng chứa thông tin cá nhân (PII), cân nhắc thực hiện export trên server và trả về file đã được kiểm soát quyền truy cập.
  • CORS: Khi tải file từ server, cần thiết lập header Content-Disposition: attachment; filename="...".
  • XSS: Đảm bảo dữ liệu được escape khi render trong HTML table; nếu dữ liệu chứa <script> có thể gây lỗi khi chuyển sang Excel.

11. Triển Khai Thực Tế: Tích Hợp Vào Dự Án Lớn

11.1. Kiến Trúc Đề Xuất

src/
├─ components/
│ ├─ DataTable.jsx
│ ├─ ExportButton.jsx
│ └─ LoadingOverlay.jsx
├─ utils/
│ ├─ exportExcel.js
│ ├─ exportExcelStreaming.js
│ └─ exportCSV.js
├─ services/
│ └─ dataService.js // fetch data từ API
├─ hooks/
│ └─ useExport.js // hook tùy chỉnh cho export
└─ App.jsx
  • Hook useExport: Quản lý trạng thái loading, error, và thực thi hàm export (có thể chuyển đổi giữa xlsx và csv tùy thuộc vào kích thước dữ liệu).
  • Component LoadingOverlay: Hiển thị overlay toàn màn hình khi export lớn.
  • Service Layer: Tách việc fetch dữ liệu để dễ test và mock.

11.2. Ví Dụ Hook useExport

// src/hooks/useExport.js
import { useState } from 'react';
import { exportTableToExcel } from '../utils/exportExcel';
import { exportLargeData } from '../utils/exportExcelStreaming';
import { exportToCSV } from '../utils/exportCSV'; export const useExport = ({ mode = 'excel', // 'excel' | 'csv' | 'largeExcel' tableId, fileName, data,
}) => { const loading, setLoading = useState(false); const exportData = async () => { setLoading(true); try { if (mode === 'excel') { exportTableToExcel(tableId, fileName); } else if (mode === 'csv') { exportToCSV(data, fileName); } else if (mode === 'largeExcel') { await exportLargeData(data, fileName); } } catch (err) { console.error(err); } finally { setLoading(false); } }; return { loading, exportData };
};

Component sử dụng:

const { loading, exportData } = useExport({ mode: data.length > 5000 ? 'largeExcel' : 'excel', tableId: 'react-html-table', fileName: 'Report', data,
});

12. Kiểm Thử (Testing)

12.1. Unit Test Với Jest

// __tests__/exportExcel.test.js
import { exportTableToExcel } from '../src/utils/exportExcel'; test('exportTableToExcel calls saveAs with correct blob', () => { document.body.innerHTML = ` <table id="test-table"><tr><td>1</td></tr></table> `; const saveAsMock = jest.fn(); jest.mock('file-saver', () => ({ saveAs: saveAsMock })); exportTableToExcel('test-table', 'TestFile'); expect(saveAsMock).toHaveBeenCalled(); const blob = saveAsMock.mock.calls00; expect(blob.type).toContain('application/vnd.openxmlformats');
});

12.2. End-to-End Test Với Cypress

// cypress/integration/export_spec.js
describe('Export Excel', () => { it('downloads a valid xlsx file', () => { cy.visit('/'); cy.get('.export-btn').click(); // Kiểm tra file tải về (cypress-downloadfile plugin) cy.readFile('cypress/downloads/UserList.xlsx').should('exist'); });
});

13. Các Thư Viện Thay Thế và So Sánh Hiệu Năng

Thư việnBundle size (gz)Thời gian tạo 10k rows (ms)Định dạng hỗ trợĐánh giá
SheetJS (core)~ 450KB120msxlsx, csv, ods★★★★
exceljs (non‑stream)~ 550KB180msxlsx★★★
exceljs (stream)~ 560KB80ms (stream)xlsx★★★★★
react-data-export~ 200KB150msxlsx★★☆☆
html-table-to-excel~ 30KB30ms (đơn giản)xls★☆☆☆

Kết luận: Nếu bạn cần định dạng phong phú và khả năng mở rộng, chọn exceljs streaming. Nếu chỉ cần đơn giản và nhanh, SheetJS là lựa chọn cân bằng.

14. Tổng Kết

  1. Xác định nhu cầu: Định dạng, kích thước dữ liệu, yêu cầu UI/UX.
  2. Chọn thư viện phù hợp: SheetJS cho đa năng, exceljs streaming cho dữ liệu lớn, CSV cho nhẹ.
  3. Cài đặt và cấu hình: npm install các package, lazy load nếu cần.
  4. Triển khai hàm export:
  5. Lấy table HTML → XLSX.utils.table_to_sheet.
  6. Hoặc tạo worksheet bằng exceljs và thêm rows.
  7. Tùy chỉnh style, merge, độ rộng cột.
  8. Xử lý UI: Loading, toast, error handling.
  9. Tối ưu hiệu năng: Streaming, Web Worker, lazy load.
  10. Bảo mật: Tránh lưu dữ liệu nhạy cảm trên client, kiểm soát CORS.
  11. Kiểm thử: Unit + E2E để đảm bảo file được tạo đúng.
  12. Triển khai: Tổ chức code thành modules, hook, service để dễ bảo trì.

Với hướng dẫn chi tiết trên, bạn đã nắm vững toàn bộ quy trình react html table to excel – từ việc cài đặt, viết code, tối ưu, đến kiểm thử và triển khai thực tế. Hãy áp dụng ngay vào dự án của mình, cải thiện trải nghiệm người dùng và nâng cao giá trị dữ liệu doanh nghiệp!

⚠️
LƯU Ý QUAN TRỌNG: Miễn trừ trách nhiệm: Mặc dù chúng tôi luôn nỗ lực để đảm bảo tính chính xác và cập nhật của thông tin, nhưng các quy định về giáo dục và tuyển sinh có thể thay đổi theo thời gian. BigSchool không chịu trách nhiệm về bất kỳ sai sót nào hoặc kết quả phát sinh từ việc sử dụng thông tin trên trang này. Độc giả nên liên hệ trực tiếp với nhà trường hoặc cơ quan chức năng để có thông tin mới nhất. Ngoài ra, BigSchool tuyệt đối không bán sản phẩm, khóa học hay cung cấp dịch vụ được nhắc đến trong bài.

Viết một bình luận