你的.vue檔案就已經是你的文件了
昨天釋出了vuese1.0,這是我的一個新的開源專案,用來解析Vue SFC並生成markdown文件,如下:
ofollow,noindex">HcySunYang/vuese這篇文章不會介紹如何使用,至於如何使用大家可以檢視 readme,這裡我們主要說一說實現的思路。
一、動機
你或者你的團隊也許會有一套自己的元件庫(或者是單純的一個元件),通常當你開發完一個元件之後,你需要手動的編寫markdown文件,從而讓其他人瞭解元件是如何使用的。這裡的問題在於: 假若元件不停的在更新,你就必須要不停的手動維護對應的文件,使其與元件的功能保持一致 。實際上這個過程是有些噁心 的,那麼有什麼更好的辦法嗎?基於此我才開發了 vuese 。
二、想法
我們知道一個 vue 元件所暴露的介面無非是 props 、 events 、 slots (或 scopedSlots ) 以及部分 methods 。那我們可不可以實現一個工具幫助我們分析一個vue元件並提取這些資訊呢?然後自動生成文件,這樣無論元件如何改動我們都不需要手動維護文件了,只需要使用該工具重新生成即可。
三、基本思路
對於一個 vue 元件,如果我們拋開 style 和自定義塊,那麼它由兩部分組成,即:模板 和 script 塊。甚至如果使用 render 函式代替模板的話,那麼就只剩一下一個 script 了。
對於 props,methods 它們只能在 scirpt 塊內定義,如:
props: { name: String } methods: { clear () {/*...*/} }
而對於 slots 則既可以在 script 塊內定義,也可以在模板中定義,如:
<!-- 在模板中定義 --> <div> <slot name="header" /> </div>
// 在 script 塊內定義了 slots render (h) { return h('div', this.$slots.header) }
以上兩種寫法是等價的,所以在提取 slots 資訊時即需要考慮模板中的slots,也要相容 script 塊內的 slots。
而且在函式式元件中,以下內容都應該作為 slots 處理:
// ctx.slots() render (h, ctx) { return h('div', ctx.slots().xxx) } // ctx.children render (h, ctx) { return h('div', ctx.children) }
這也是我們在提取slots資訊時需要考慮在內的。
同樣的,對於 events 而言也是既可以出現在模板中,又可以出現在 script 塊中,如下:
<!-- 模板中的 events --> <div @click="$emit('onclear')"></div>
// 定義在 script 中的事件 methods: { someMethod () { this.$meit('onclear') } }
所以在提取 events 資訊時也需要即考慮模板又考慮 script 塊。
對於模板我們預設是 html 語法,對於 script 塊我們預設為 js。我們要做的第一件事兒就是將 html 和 js 單獨提取出來並單獨分析,好在巨人的肩膀厚實,已經有了 @vue/component-compiler-utils 模組和 vue-template-compiler 模組。其中我們使用 @vue/component-compiler-utils 模組解析 vue SFC 並分別得到 html(模板) 和 JavaScript(script塊) 的原始碼。對於 html 原始碼我們可以再次使用 vue-template-compiler 模組將其解析為模板對應的 AST,然後通過編寫一個 traverse 函式對其進行分析,讀取slots相關的內容。
而對於 JavaScript 原始碼的處理,我選擇了使用 babel7,寫過 babel 外掛的同學或許已經猜到了實現的思路。我們將原始碼交由 @babel/travers 模組處理,然後通過編寫一些 helper 函式來輔助我們判斷出哪些是要真正處理的內容即可,如下原始碼段所示:
const mainTraveres = { ObjectProperty(path: any) { // Processing name if (isVueOption(path, 'name')) { if (onName) onName(path.node.value.value) } // Processing props if (isVueOption(path, 'props')) { // 一些邏輯 } // more... } }
其中 isVueOption 函式是我們自己編寫的 helper 函式,來輔助我們判斷一個物件的屬性是否是 Vue 選項物件中的特定屬性,如果是我們就進一步處理就可以了,對於 js 的處理基本都是這個思路,更多內容大家可以檢視原始碼: https:// github.com/HcySunYang/v uese/blob/master/src/parser/parseJavascript.ts
四、生成目標
經過上一步的處理,我們可以編寫出一個 parser 模組,解析並組裝出我們需要的內容,有了需要的內容之後,我們就可以根據這些資訊編寫一個 Render 模組,本質就是一個程式碼生成的過程,至於生成的內容是什麼這取決於你想要的目標,vuese 內建的 Render 會根據這些資訊為你生成 markdown 檔案,或者生成一個整合 docute 的文件。但實際上只要你腦洞夠大你可以生成任何東西,試想一下,如果我們的 parser 模組編寫的更加完善,對一個 vue 元件的分析足夠細緻,這樣我們就能拿到一個 vue 元件全部的資訊,然後在程式碼生成階段將其生成一個 ts compatible 的元件,這不就實現了一個將非js編寫的vue元件轉換為ts相容的vue元件的外掛了嗎?(備註:後來一哥們兒提供了一個更好的辦法來實現這件事兒,為了避免尷尬,我只能說這也是一個思路嘛......)
回到 vuese 內建的 Render 模組,Render 的結果就是markdown資源,本質就是一個字串拼接的過程,具體可以檢視原始碼 https:// github.com/HcySunYang/v uese/blob/master/src/render/index.ts ,並不複雜。
實際上 vuese 提供了很多有用的資訊和文件中沒有體現出來的功能 ,這多會在後續逐漸補充。舉個例子,使用 vuese 生成的markdown檔案大致如下:

你可能已經注意到了,在上面的 markdown 檔案中包含了很多諸如:
<!-- @vuese:CompName:props:start -->
<!-- @vuese:CompName:props:end -->
之類的註釋,它的作用是告訴 vuese 在生成文件時要將生成的 markdown 程式碼放置在什麼位置,如果一個元件還沒有對應的markdown文件,則新生成之,否則會在已有的文件基礎上更新。這麼做的目的是出於真正使用場景的考慮,因為一個元件的文件不可能僅僅包含上圖中展示的內容,它還可能包含開發者自己編寫的demo,和其他描述內容。這樣我們生成文件的時候就不會覆蓋掉開發者自己編寫的內容,而是將生成的內容插入到指定的位置。
五、規劃
目前 vuese 已經實現的特性如下:

規劃中要實現的特性如下:

另外目前還不支援 外掛系統 ,這也在未來的規劃當初。並且在我們團隊內部已經將其應用在內部的元件庫的文件維護,也計劃關注 vue3.0 併兼容之。最後 vuese 剛剛釋出有諸多不足之處,但是隨著後續的更新迭代,它會變得越來越好,也歡迎感興趣的同學共建。
其他規劃:模板支援 pug、外掛系統、還有啥??????