我有一個專案叫 browser-ai
,在裡面有個功能是可以使用 Vue 的 directive
為 DOM 綁上標籤
<template>
<h1 v-ai="{ id: 'title', description: 'Search Product In this page' }">...</h1>
</template>
<!-- 上述內容會轉換成 -->
<h1 data-ai-id="title" data-ai-description="Search Product In this page">...</h1>
若沒有設定 description,則該 DOM 節點的 textContent
會成為 description
。
<template>
<h1 v-ai="{ id: 'title'}"> Search Product In this page</h1>
</template>
<!-- 上述內容會轉換成 -->
<h1 data-ai-id="title" data-ai-description="Search Product In this page">Search Product In this page</h1>
id
, description
是用來作為 LLM 的 prompt 資訊,並且透過 Vue 的能力,我也可以記錄加上這些標籤的 DOM 節點,在之後進行操作。
接著我需要做到的是「在 build time 蒐集所有的這些節點,最好還可以標註他在哪一個路由上(SPA),可以不需要 DOM 物件」。理想中的實作結果如下(假設使用 rollup 或是 vite 打包):
// virtual:vai-elements
export const elementList = [
{ id: "product-title", description: "Search Product In this page", data: {route: "/product"} },
{ id: "book-title", description: "Search Book In this page", data: {route: "/book"} },
]
import { elementList } from "virtual:vai-elements"
// do something with elementList
有這個需求的原因有二:
- 若一開始我就知道,在全站之中,某一個 element 有我要的資訊,也知道他在哪一個頁面,那我就可以一口氣導航過去
- 打包階段沒有真的 DOM 物件不是問題,這可以想辦法在 runtime 解決
- 若一開始我就有所有站內的文本資訊,AI 就可以有更完整的資訊進行回答
此外,我希望將這個功能製作成 unplugin
,讓這一切變得方便一點。在原本的構想中,他要可以相容於 Vite+Vue 以及 Nuxt3。
我嘗試想出了幾個解決方向:
- 使用 Vue SSR 功能,在打包的階段接收 router 以及元件樹,實際渲染每個路由後,解析 SSR render 出來的 DOM。這個作法是我認為比較確實的,但是在做成 plugin 的時候有一些問題:
- 由於是在 build time 運行,且要直接渲染使用者的 APP,因此我實際上需要做一個跟使用者的打包設定幾乎相同的打包。這會導致打包時間變成至少 2 倍,萬一想不開想支援 hot-reload,那效能肯定不會太好。
- 使用 ast:
- 我認為不是很可行,因為框架有自己渲染模板、解析變數的機制,如果 id 或 description 的值是一個變數的話,就只有框架有辦法判斷那個值是多少了。
- 使用 tailwind 跟 unocss 的靜態解析
- 這個做法概念是像這兩個工具一樣,看到特定的 class name,就把對應的內容加到模組之中。
- 但是這個做法會限制使用者不可以連接字串,該字串必須是靜態,並且為了判斷要抽取哪些內容,可能要規定使用者使用特定語法。
- 還有個問題是,這種作法沒有考慮 route 跟元件樹,因此 route 需要使用者手動加入
- 無法支援
textContent
作為 description
簡單來說,第一個作法是最完善的,可是效能很差; 第二個不可行; 第三個效能應該會不錯,但是會對使用者有一些限制。
我想請教的是,是否還有其他的可能的作法?或是上述做法之中還有什麼改進的可能?