Lưu đọng dữ liệu Iconify để dùng offline

Là một nhà phát triển web, bạn có thể đã biết đến Iconify, một kho dữ liệu icon dành cho web cực kỳ phong phú, tổng hợp từ nhiều bộ khác nhau (FontAwesome, Material Design v.v...) để đưa về một cách sử dụng chung. Tôi cũng không ngoại lệ, cũng sử dụng Iconnify trong nhiều dự án, vì ngoài việc tích hợp dễ dàng vào dự án, Iconify còn cho phép trộn lẫn nhiều bộ, vì nhiều khi hình ảnh mà tôi cần không tồn tại trong bộ này mà chỉ có ở bộ kia (bạn có thể lo ngại việc trộn lẫn khiến phong cách hình ảnh không thống nhất, đành chịu, vì với người không có khả năng thiết kế thì phải chấp nhận thôi).

Để có được ưu điểm kể trên, Iconify không bắt bạn phải đóng gói hết nội dung icon của các bộ cần dùng, mà dùng cơ chế gọi API để lấy về nội dung của icon khi cần (và nội dung này cũng được lưu trữ tạm để khỏi gọi API lại nhiều lần).

icon api

Với phần lớn trường hợp thì cách làm này là ổn, nhưng một số trường hợp thì nó lại gây băn khoăn. Ví dụ với một sản phẩm IoT, khi mà website đó chạy trong mạng nội bộ, để có thể tự hành khi rớt Internet, hoặc được truy cập ở vùng sâu vùng xa nơi điều kiện Internet không tốt, khi việc gọi API lấy nội dung icon có thể thất bại và ảnh hưởng xấu đến giao diện website. Với những trường hợp như thế thì tôi phải tìm cách thu thập nội dung icon và đính kèm theo website, để không phải gọi API nữa.

Control

Sau đây là cách làm với Vue 3. Cách này không được chỉ dẫn trong tài liệu của Iconify nên tôi phải tự mò.

Nội dung icon dành cho việc sử dụng offline được Iconify phân phối trên NPM Registry, nên bạn có thể kèm nó dưới dạng dependency của ứng dụng JavaScript. Ví dụ icon sau ri:shield-user-line có tên ri:shield-user-line, thuộc bộ Remix Icon thì bạn cần thêm gói @iconify-icons/ri vào danh sách dependency:

$ yarn add @iconify-icons/ri

Tiếp đến, trong code Vue, trong component nào mà bạn dùng đến icon này, thêm vào các dòng sau:

import { addIcon } from '@iconify/vue'
import iShieldUser from '@iconify-icons/ri/shield-user-line'

addIcon('ri:shield-user-line', iShieldUser)

Trên phần <template></template>, bạn vẫn sử dụng icon như khi không cần offline:

<Icon icon='ri:shield-user-line' />

Khi gọi addIcon(), bạn có thể truyền chuỗi bất kì vào tham số thứ nhất, để đặt tên khác cho icon, miễn sao khi sử dụng trong <template/> thì bạn phải truyền tên tương ứng vào <Icon icon='' />. Tôi thì vẫn dùng tên cũ, để thuận tiện cho việc tra tìm cũng như không gây đứt gãy nếu sau này muốn bỏ việc đính kèm offline.

Như vậy là xong, khi bạn build bằng Vite, nội dung của icon ri:shield-user-line sẽ được kèm vào source website và không bị phụ thuộc vào API nữa. Ngoài ra, nhờ được Iconify tổ chức sao cho hỗ trợ tree-shaking nên chỉ có nội dung của icon kia được kèm thêm, thay vì cả trăm icon của bộ Remix Icon. Điều bất ngờ là trong tài liệu của Iconify, trang Icon bundles for Iconify for Vue lại hướng dẫn cách làm khác và hơi khó thực hiện (có lẽ tài liệu cũ không được cập nhật).