VueJS: Plugin vue-meta và cách sử dụng
Giải phóng thời gian, khai phóng năng lực
Mô tả
vue-meta
là một plugin Vue 2.0 cho phép quản lý thông tin meta của app, nó giống như react-helmet
của React. Tuy nhiên, thay vì cài đặt dữ liệu như là các props được truyền tới component duy nhất thì ta đơn giản là export nó như là một của dữ liệu của component sử dụng thuộc tính metaInfo
.
Những thuộc tính này khi thiết lập cho component lồng thì sẽ ghi đè metaInfo
của component cha, điều này sẽ cho phép tùy chỉnh thông tin cho mỗi view ở top-level giống như việc ghép thông tin trực tiếp cho các component con lồng sâu bên trong nhằm mục đích dễ bảo trì hơn.
Cài đặt
Yarn
$ yarn add vue-meta
NPM
$ npm install vue-meta --save
CDN
Nếu bạn sử dụng phiên bản thấp hơn bạn sử dụng link sau:
Không nén:
<script src="https://unpkg.com/vue-meta@1.5.8/lib/vue-meta.js
"></script>
Bản nén:
<script src="https://unpkg.com/vue-meta@1.5.8/lib/vue-meta.min.js
"></script>
Sử dụng
Bước 1: Chuẩn bị plugin
Bạn có thể bỏ qua bước này nếu bạn không cần SSR và Vue có sẵn như một biến global.
vue-meta
sẽ tự cài đặt trong trường hợp này.
Để sử dụng plugin thì trước tiên ta cần truyền nó tới Vue.use
- nếu bạn không render trên server-side thì file JS đầu vào của bạn sẽ không cần sửa gì, ngược lại thì ta sẽ đặt nó trong file mà chạy cả ở server và client trước khi đối tượng root được mount. Nếu ta đang dùng vue-router
thì file router.js
sẽ trông như sau:
router.js:
Các tùy chọn
vue-meta
cho phép các tùy chọn sau:
Nếu ta không quan tâm đến server-side rendering ta có thể bỏ qua và chuyển sang bước 3, ngược lại thì ta tiếp tục chuyển sang bước 2.
Bước 2: Server Rendering (tùy chọn)
Nếu ta có một webapp dạng isomorphic/universal thì ta cần render metadata trên server side. Cách thức như sau:
Bước 2.1: Hiển thị $meta
ở bundleRenderer
Ta sẽ cần hiển thị các kết quả của phương thức $meta
mà vue-meta
đã thêm vào đối tượng Vue ở ngữ cảnh bundle render trước khi có thể bắt đầu đưa vào thông tin meta. Bạn cần làm điều này trong file server-entry:
server-entry.js:
Bước 2.2: Điền thông tin meta bằng inject()
Điều tiếp theo bạn cần làm trước khi bắt đầu sử dụng các tùy chọn của metaInfo
trong các component là hãy đảm bảo chúng làm việc trên server bằng cách nạp
chúng để bạn có thể gọi text()
trên mỗi item nhằm hiển thị những thông tin cần thiết. Ta có 2 phương thức sau đây:
Hiển thị đơn giản với renderToString()
Xét phương thức dễ nhất để gộp phần head nếu Vue server được hiển thị ở dạng chuỗi:
server.js:
Nếu ta đang sử dụng file template riêng thì hãy sửa thẻ <head> thành:
Lưu ý là sử dụng {{{
thay vì {{, hãy cực kỳ thận trọng khi sử dụng {{{
với __dangerouslyDisableSanitizers
.
Truyền kết xuất với renderToStream()
Phương thức này phức tạp hơn một chút nhưng tốt hơn, đó là thay thế việc việc truyền response. Ở đây vue-meta
hỗ trợ việc truyền không hề mất chi phí dựa vào ngữ cảnh bundleRenderer
thông mình của Vue:
server.js
Bước 3: Định nghĩa metaInfo
Trong bất kỳ component nào thì việc định nghĩa thuộc tính metaInfo
sẽ có dạng như sau:
App.vue:
Home.vue
About.vue
Các thuộc tính chuẩn của metaInfo
title
(String)
Thuộc tính này sẽ điền giá trị text vào giữa thẻ mở và đóng của thẻ <title>
.
Kết quả:
<title>Foo Bar</title>
titleTemplate
(String | Function)
Giá trị của thuộc tính title
ở trên sẽ được thế vào phần %s
của titleTemplate
trước khi được render. Tiêu đề gốc sẽ có sẵn ở metaInfo.titleChunk
.
Kết quả:
Từ phiên bản v1.2.0 thì titleTemple cũng có thể là hàm:
htmlAttrs
(Object)
Mỗi cặp key:value sẽ tương ứng với attribute:value của phần tử <html>
.
Kết quả:
headAttrs
(Object)
Mỗi cặp key:value sẽ tương ứng với attribute:value của phần tử <head>
.
Kết quả:
bodyAttrs
(Object)
Mỗi cặp key:value sẽ tương ứng với attribute:value của phần tử <body>
.
Kết quả:
base
(Object)
Tương ứng với thẻ <base>
:
Kết quả:
meta
([Object])
Mỗi item trong mảng ứng với một phần tử <meta>
:
Kết quả:
Từ phiên bản v1.5.0 ta có thể thiết lập template cho meta để nó có thể làm việc tương tự như titleTemplate:
Kết quả:
link
([Object])
Mỗi item trong mảng ứng với một thẻ <link>
:
Kết quả:
style
([Object])
Mỗi item trong mảng ứng với môt một phần tử <style>
:
Kết quả:
script
([Object])
Mỗi item trong mảng ứng với một phần tử <script>
:
Kết quả:
Nếu trình duyệt không hỗ trợ defer
hay một lý do nào đó thì ta cần đặt <script>
trước </body>
sử dụng body
.
noscript
([Object])
Mỗi item trong mảng ứng với một phần tử <noscript>
:
Kết quả:
__dangerouslyDisableSanitizers
([String])
Mặc định thì vue-meta
sẽ đặt mỗi thành phần HTML với một thuộc tính. Ta có thể bỏ hành vi này bằng cách sử dụng __dangerouslyDisableSantizers
. Hãy truyền cho nó các thuộc tính ta muốn thiết đặt:
Kết quả:
Lưu ý: Sử dụng tùy chọn này khi ta biết chính xác ta đang làm gì. Việc bỏ tính năng thiết đặt này sẽ làm tăng nguy cơ bị tấn công như SQL injection và Cross-Site Scripting (XSS).
__dangerouslyDisableSanitizersByTagID
({[String]})
Cung cấp tính năng giống như __dangerouslyDisableSanitizers
nhưng có thể chỉ định thuộc tính nào của tagIDKeyName cần được
disabled. Một đối tượng tạo ra cần có vmid là key và một mảng các thuộc tính:
Kết quả:
Lưu ý: Sử dụng tùy chọn này khi ta biết chính xác ta đang làm gì. Việc bỏ tính năng thiết đặt này sẽ làm tăng nguy cơ bị tấn công như SQL injection và Cross-Site Scripting (XSS).
changed
(Function)
Được gọi khi metaInfo
phía client thực hiện việc update hoặc change. Hàm nhận các tham số sau:
newInfo
(Object) - Trạng thái mới của đối tượngmetaInfo
.addedTags
([HTMLElement]) - một danh sách các phần tử được thêm vào.removedTags
([HTMLElement]) - một danh sách các phần tử bị xóa bỏ.
Ngữ cảnh this
là một đối tượng component changed
được định nghĩa.
Cách thực hiện metaInfo
Ta có thể định nghĩa metaInfo
tại bất kỳ component nào. Các component con mà có metaInfo
gộp đệ quy metaInfo
của chúng vào ngữ cảnh cha và sẽ ghi đè bất kỳ thuộc tính giống nhau nào. Hãy xét cấu trúc component như sau:
Nếu cả <parent>
và <child>
đều định nghĩa thuộc tính title
trong metaInfo
thì title
được định nghĩa trong <child>
sẽ được sử dụng.
Danh sách các thẻ
Khi chỉ định một mảng trong metaInfo
thì giống như ví dụ dưới đây, hành vi mặc định sẽ đơn giản là nối danh sách.
Input:
Output:
Đây không phải điều ta muốn vì thẻ meta description
phải là thẻ duy nhất trong trang web. Ta chỉnh sửa điều này bằng cách sau đây:
Input:
Output:
Trong khi các giải pháp như là react-helmet
quản lý thứ tự xảy ra và hợp nhất hành vi cho ta một cách tự động thì nó phát sinh nhiều code hơn và vì vậy dễ gây lỗi nhiều hơn, trong khi cách thức trên gần như là an toàn tuyệt đối vì tính linh hoạt của nó; với chi phí một lần đánh đổi là: các thuộc tính vmid
này sẽ được render trong lần đánh dấu cuối cùng (vue-meta
sử dụng trình khách này để ngăn cản việc sao chép hoặc ghi đè). Nếu ta đang phục vụ cho nội dung GZIP'ped của ta thì việc tăng nhẹ tải trọng HTTP sẽ là không đáng kể.
Hiệu năng
Ở phía trình khách, vue-meta
phân tách các cập nhật DOM sử dụng requestAnimationFrame
. Nó cần phải làm điều này vì nó đăng ký một Vue mixin để đăng ký tới vòng đời hook beforeMount
trên tất cả các component theo hướng được thông báo rằng các render đã đang xảy ra và dữ liệu đang sẵn sàng. Nếu vue-meta
không phân tách các cập nhật thì thông tin meta DOM sẽ được tính toán lại và sẽ được cập nhật cho mỗi component trên trang một cách liên tiếp.
Nhờ vào việc phân tách cập nhật mà việc cập nhật sẽ chỉ xảy ra một lần - ngay cả khi thông tin meta chính xác được biên dịch bởi server. Nếu bạn không muốn hành vi này thì hãy xem bên dưới.
Cách ngăn chặn việc update trên trang kết xuất ban đầu
thêm thuộc tính data-vue-meta-server-rendered
vào thẻ <html>
trên phái máy chủ:
vue-meta
sẽ kiểm tra thuộc tính này mỗi khi nó cố cập nhật DOM - nếu nó tồn tại thì vue-meta
sẽ xóa nó và không cho update. Nếu nó không có sẵn thì vue-meta
sẽ cho phép update.
Lưu ý: Trong khi điều này có thể là dài dòng thì đây là điều có chủ ý.
vue-meta
xử lý điều này cho ta một cách tự động sẽ giúp hạn chế khả năng tương tác với các ngôn ngữ lập trình server-side khác. Ví dụ, nếu bạn sử dụng PHP cho máy chủ thì bạn có thể có meta info được xử lý trên server và muốn ngăn chặn bản cập nhật không liên quan này.
FAQ
Dưới đây là một vài câu trả lời cho một số câu hỏi phổ biến.
Tôi sử dụng prop và data component trong metaInfo
thế nào?
Trả lời: Thay vì định nghĩa metaInfo
như là một đối tượng thì hãy định nghĩa nó như là một hàm và truy cập this
bình thường:
Post.vue:
PostContainer.vue:
Tôi xử lý metaInfo
từ kết quả của một action bất đồng bộ thế nào?
vue-meta
sẽ làm điều này cho bạn một cách tự động khi state component của bạn thay đổi.
Hãy đảm bảo rằng ban đang sử dụng dạng function của metaInfo
:
Vì sao vue-meta
không hỗ trợ jsnext:main
?
Ban đầu thì có, tuy nhiên nó phát sinh vấn đề. Về cơ bản thì Vue không hỗ trợ jsnext:main
, và nó không hướng nội cho thuộc tính default
thể hiện từ bạn ES2015, do đó nó phá bỏ độ phân giải module.
jsnext:main
là một thuộc tính không chuẩn và nó sẽ bị bỏ, và vue-meta
được đóng gói thành một file không có phần bên trong module động cũng như thực tế là nếu bạn đang sử dụng vue-meta
thì khả năng 99.9% bạn không sử dụng nó có điều kiện, vì vậy nó hoàn toàn không được hỗ trợ.
Nếu đây không phải là điều bạn muốn thì bạn phải hướng Babel để chuyển các import default
thành cấu trúc module phổ biến với một plugin, điều này không được lý tưởng vì nhiều người dùng Vue viết code bằng TypeScript mà không phải là Babel.
Giải phóng thời gian, khai phóng năng lực