Leaflet Awesome Markers là một tiện ích mở rộng phổ biến dành cho thư viện bản đồ Leaflet, cho phép hiển thị các marker với biểu tượng vector từ các bộ icon như Font Awesome, Ionicons hoặc Bootstrap Glyphicons. Khi triển khai trên các bản đồ có hàng trăm — thậm chí hàng nghìn — điểm đánh dấu, hiệu năng dễ bị suy giảm do chi phí tạo DOM, tính toán CSS và hoạt ảnh không kiểm soát. Dưới đây là sáu kỹ thuật tối ưu hóa đã được kiểm chứng thực tế nhằm giảm thời gian khởi tạo bản đồ, hạn chế tải CPU và nâng cao độ mượt trong tương tác.
1. Loại bỏ cấu hình thừa khi khởi tạo biểu tượng
Thay vì truyền toàn bộ danh sách thuộc tính vào L.AwesomeMarkers.icon(), chỉ nên khai báo những giá trị khác biệt so với mặc định:
// Trước: cấu hình đầy đủ, gây dư thừa
const markerA = L.AwesomeMarkers.icon({
icon: 'map-marker',
prefix: 'fa',
markerColor: 'darkblue',
iconColor: 'white',
spin: false,
extraClasses: ''
});
// Sau: lược bỏ thuộc tính có giá trị mặc định
const markerB = L.AwesomeMarkers.icon({
icon: 'map-marker',
prefix: 'fa',
markerColor: 'darkblue'
});
Các thuộc tính như iconColor (mặc định 'white') hay spin (false) không cần khai báo rõ ràng — việc lược bỏ giúp giảm kích thước đối tượng JavaScript và tốc độ khởi tạo marker hàng loạt.
2. Hạn chế sử dụng hiệu ứng xoay (spin)
Thuộc tính spin: true áp dụng lớp CSS fa-spin, kích hoạt animation liên tục trên từng phần tử — điều này đặc biệt nguy hại khi render nhiều marker cùng lúc. Thay vào đó, nên:
- Dùng
spinchỉ cho các marker chiến lược (ví dụ: vị trí đang theo dõi thực thời), - Hoặc ghi đè CSS để giới hạn tần suất làm mới:
.fa-spin { animation-duration: 2s !important; }.
3. Nhóm marker trước khi thêm vào bản đồ
Thay vì gọi .addTo(map) cho từng marker riêng lẻ — dẫn đến nhiều lần reflow/layout — hãy sử dụng L.featureGroup như một container trung gian:
// Cách không tối ưu (100 lần thao tác DOM)
data.forEach(point => {
L.marker(point.latlng, { icon: baseIcon }).addTo(map);
});
// Cách tối ưu (1 lần gắn DOM)
const group = L.featureGroup();
data.forEach(point => {
L.marker(point.latlng, { icon: baseIcon }).addTo(group);
});
group.addTo(map);
Kỹ thuật này giảm đáng kể số lần trình duyệt phải tái tính toán bố cục và vẽ lại (repaint), đặc biệt hữu ích trên thiết bị di động.
4. Thay thế inline style bằng class CSS toàn cục
Thay vì dùng iconColor để sinh thuộc tính style="color: #fff" nội tuyến, hãy định nghĩa sẵn các lớp CSS và áp dụng qua extraClasses:
/* file styles.css */
.awesome-marker i {
font-size: 14px;
}
.awesome-marker-icon-primary i {
color: #2563eb;
}
.awesome-marker-icon-alert i {
color: #dc2626;
}
const alertIcon = L.AwesomeMarkers.icon({
icon: 'exclamation-triangle',
prefix: 'fa',
markerColor: 'orange',
extraClasses: 'awesome-marker-icon-alert'
});
Việc tách biệt logic hiển thị khỏi JavaScript giúp trình duyệt cache hiệu quả hơn và giảm chi phí tính toán style.
5. Tùy chọn bộ biểu tượng tối thiểu
Thay vì nhúng toàn bộ tệp font-awesome.min.css (khoảng 25–30 KB nén), hãy tạo phiên bản rút gọn chỉ chứa các icon thực sự dùng — ví dụ bằng công cụ IcoMoon hoặc Fontello. Kết quả thường giảm dung lượng CSS tới 70–90%, đồng thời loại bỏ khả năng xung đột với các font khác trong dự án.
6. Tiền tạo và chia sẻ đối tượng biểu tượng
Mỗi lần gọi L.AwesomeMarkers.icon(...) đều tạo một đối tượng mới — bao gồm cả DOM node ẩn. Với dữ liệu dạng phân nhóm (ví dụ: marker theo loại dịch vụ), hãy tiền khai báo các kiểu biểu tượng và tái sử dụng:
// Khởi tạo một lần, dùng nhiều lần
const serviceIcons = {
restaurant: L.AwesomeMarkers.icon({ icon: 'cutlery', prefix: 'fa', markerColor: 'green' }),
hospital: L.AwesomeMarkers.icon({ icon: 'heartbeat', prefix: 'fa', markerColor: 'red' }),
school: L.AwesomeMarkers.icon({ icon: 'graduation-cap', prefix: 'fa', markerColor: 'blue' })
};
// Gắn marker theo loại
data.forEach(item => {
const marker = L.marker(item.coords, { icon: serviceIcons[item.type] });
marker.addTo(map);
});
Cách tiếp cận này giảm số lượng node DOM được tạo, tiết kiệm bộ nhớ và tăng tốc độ vòng lặp xử lý dữ liệu.