defer và async là 2 thuộc tính trên thẻ <script> kiểm soát thời điểm trình duyệt thực thi JS mà không chặn parse HTML. Chọn sai thuộc tính gây lỗi script hoặc làm FCP không cải thiện được.
Vấn đề với script mặc định
Thẻ <script src="..."> không có thuộc tính chặn parser HTML theo mặc định. Trình duyệt dừng parse, tải JS, thực thi xong rồi mới tiếp tục parse — gây trễ FCP và LCP trực tiếp.
Một file JS 100KB qua mạng 4G có thể chặn hiển thị 500–800ms. defer và async giải quyết điểm chặn này — mỗi thuộc tính theo cách riêng.
defer — thực thi sau khi DOM parse xong
Trình duyệt tải JS song song với parse HTML. Thực thi SAU khi DOM parse xong, ngay trước sự kiện DOMContentLoaded.
Nhiều script defer thực thi theo đúng thứ tự khai báo trong HTML.
Phù hợp cho: script cần truy cập DOM, script có phụ thuộc lẫn nhau, plugin và theme JS trong WordPress.
async — thực thi ngay khi tải xong
Trình duyệt tải JS song song với parse HTML. Thực thi NGAY KHI tải xong — bất kể HTML đã parse xong chưa.
Nhiều script async thực thi không theo thứ tự (ai tải xong trước thực thi trước).
Phù hợp cho: script độc lập, không cần DOM, không có phụ thuộc với script khác.
So sánh defer và async — 7 khía cạnh
| Khía cạnh | defer | async |
|---|---|---|
| Tải HTML | Song song, không chặn | Song song, không chặn |
| Thời điểm thực thi | Sau khi DOM parse xong | Ngay khi tải xong |
| Thứ tự nhiều script | Theo thứ tự khai báo | Ngẫu nhiên (ai xong trước chạy trước) |
| Sự kiện DOMContentLoaded | Thực thi trước sự kiện này | Có thể thực thi sau sự kiện này |
| Truy cập DOM an toàn | Có — DOM đã sẵn sàng | Không — DOM có thể chưa parse xong |
| Hỗ trợ phụ thuộc script | Có (theo thứ tự) | Không |
| Hỗ trợ trình duyệt 2026 | 99%+ | 99%+ |
Dòng thời gian thực thi — hình dung rõ hơn
| Giai đoạn | Mặc định | defer | async |
|---|---|---|---|
| Parse HTML | Dừng khi gặp script | Tiếp tục bình thường | Tiếp tục bình thường |
| Tải JS | Tuần tự, chặn | Song song với parse | Song song với parse |
| Thực thi JS | Ngay, chặn parse | Sau khi parse xong | Ngay khi tải xong |
| DOMContentLoaded | Sau khi script xong | Sau defer scripts | Có thể trước async |
Khuyến nghị theo loại script cho shop WordPress
| Loại script | Thuộc tính | Lý do |
|---|---|---|
| jQuery (phụ thuộc theme/plugin) | Mặc định (không dùng) | Theme/plugin phụ thuộc jQuery cần thực thi theo thứ tự |
| Theme JS tùy biến (slider, menu) | defer | Cần DOM sẵn sàng, không cần thực thi ngay |
| Plugin WooCommerce (mini cart AJAX) | defer | Phụ thuộc container DOM của giỏ hàng |
| Google Analytics / GTM | async | Độc lập, không cần DOM, không ảnh hưởng thứ tự |
| Facebook Pixel | async | Script theo dõi độc lập |
| Chat widget (Tawk.to, Crisp) | defer hoặc async | defer nếu cần tạo DOM, async nếu hoàn toàn độc lập |
| Công cụ A/B test (Optimizely) | Mặc định (chặn) | Phải thực thi trước khi hiển thị để tránh giật giao diện |
| Bộ tải font web | async | Độc lập, font swap không chặn hiển thị |
Triển khai trong WordPress
WordPress core không hỗ trợ defer/async trong hàm wp_enqueue_script(). Cần thêm thuộc tính qua hook script_loader_tag hoặc dùng plugin.
4 phương pháp triển khai
| Phương pháp | Thiết lập | Phù hợp khi nào |
|---|---|---|
| Thủ công trong functions.php | Thêm filter custom | Lập trình viên tùy chỉnh theme |
| Plugin “Async JavaScript” | Miễn phí, có giao diện cài đặt | Trang WordPress thông thường |
| WP Rocket | $59/năm, 1 click | Shop trên 100 đơn/ngày |
| Plugin “Defer Js” | Miễn phí | Chỉ cần defer, không cần async |
Ví dụ filter thủ công
// Thêm defer/async thủ công vào functions.php
add_filter( 'script_loader_tag', function( $tag, $handle, $src ) {
$defer_scripts = [ 'theme-slider', 'theme-menu', 'wc-cart-fragments' ];
$async_scripts = [ 'google-analytics', 'facebook-pixel' ];
if ( in_array( $handle, $defer_scripts ) ) {
return str_replace( '<script ', '<script defer ', $tag );
}
if ( in_array( $handle, $async_scripts ) ) {
return str_replace( '<script ', '<script async ', $tag );
}
return $tag;
}, 10, 3 );
Kết hợp với preload để tăng hiệu quả
defer/async loại bỏ điểm chặn — nhưng trình duyệt vẫn phát hiện script muộn trong quá trình parse. Preload script quan trọng giúp tải song song từ sớm hơn.
Pattern preload kết hợp defer
<!-- Preload để tải sớm, defer để không chặn -->
<link rel="preload" href="/theme.js" as="script">
<script src="/theme.js" defer></script>
Đọc thêm về preload và prefetch resource hint và cách kết hợp với CSS quan trọng để loại bỏ toàn bộ điểm chặn hiển thị.
Checklist triển khai defer/async cho shop WordPress
- Mở DevTools → Network → lọc JS — xem script nào load ở thứ tự cao nhất.
- Liệt kê tất cả script đang enqueue:
wp_print_scripts()trong theme hoặc dùng Query Monitor. - Phân loại từng script: cần DOM không? Có phụ thuộc với script khác không?
- Script theo dõi độc lập (Analytics, Pixel) — phân loại riêng để thêm async.
- Giữ jQuery mặc định (không thêm defer/async) nếu có plugin phụ thuộc.
- Thêm defer cho toàn bộ theme JS tùy biến và plugin cần DOM.
- Thêm async cho script theo dõi (Analytics, Pixel) và font loader.
- Test kỹ trên staging trước: kiểm tra console không có lỗi
X is not defined. - Đo lại FCP và LCP qua Google PageSpeed Insights sau khi triển khai.
- Kết hợp với tách bundle cho frontend Next.js nếu có.
Bài liên quan
- Preload và prefetch — tải tài nguyên sớm hơn
- CSS quan trọng — loại bỏ điểm chặn CSS
- Tách bundle theo route — giảm JS tải ban đầu
- FCP vs LCP — sự khác biệt và cách tối ưu
Câu hỏi thường gặp
Đặt cả defer lẫn async cùng lúc — trình duyệt xử lý thế nào?
async thắng nếu cả hai cùng được đặt. Trình duyệt xử lý theo ưu tiên: async > defer > mặc định.
Nên chọn một trong hai, không đặt cả hai cùng lúc để tránh nhầm lẫn.
Script nhúng trực tiếp (không có src) có dùng defer/async được không?
Không. defer và async chỉ hoạt động với script có thuộc tính src (script ngoài). Script nhúng trực tiếp trong HTML thực thi ngay khi parser gặp — chặn parse.
Giải pháp: chuyển code ra file ngoài, hoặc đặt code nhúng trong listener DOMContentLoaded.
jQuery có thể thêm defer không?
Về lý thuyết được, nhưng rủi ro cao. Rất nhiều theme và plugin dùng thẳng biến $ hoặc jQuery mà không khai báo phụ thuộc đúng cách.
Defer jQuery nhưng không defer đúng cách các script phụ thuộc gây lỗi jQuery is not defined. Giữ jQuery mặc định là an toàn nhất — defer các script khác.
WordPress 6.3+ có hỗ trợ defer native chưa?
WordPress 6.3 (tháng 8/2023) thêm hàm wp_enqueue_script_module() hỗ trợ ES module với defer tự động. Theme và plugin mới có thể dùng API này thay vì filter thủ công.
Theme cũ dùng wp_enqueue_script() vẫn cần filter script_loader_tag để thêm defer/async.
Web22 audit toàn bộ script đang load, phân loại và triển khai defer/async đúng chuẩn — bao gồm test staging và đo FCP trước/sau. Xem dịch vụ tối ưu hiệu năng Core Web Vitals để bắt đầu.


