昨天發佈了vuese1.0,這是個人一個新的開源項目,用來解析Vue SFC並生成markdown文檔,這裏: github.com/HcySunYang/… 。html
這篇文章不會介紹如何使用,至於如何使用你們能夠查看 readme,這裏咱們主要說一說實現的思路。vue
你或者你的團隊也許會有一套本身的組件庫(或者是單純的一個組件),一般當你開發完一個組件以後,你須要手動的編寫markdown文檔,從而讓其餘人瞭解組件是如何使用的。這裏的問題在於:倘若組件不停的在更新,你就必需要不停的手動維護對應的文檔,使其與組件的功能保持一致。實際上這個過程是有些噁心🤢的,那麼有什麼更好的辦法嗎?基於此我纔開發了 vuese 。node
咱們知道一個 vue
組件所暴露的接口無非是 props
、events
、slots
(或scopedSlots
) 以及部分 methods
。那咱們可不能夠實現一個工具幫助咱們分析一個vue組件並提取這些信息呢?而後自動生成文檔,這樣不管組件如何改動咱們都不須要手動維護文檔了,只須要使用該工具從新生成便可。git
對於一個 vue
組件,若是咱們拋開 style
和自定義塊,那麼它由兩部分組成,即:模板 和 script
塊。甚至若是使用 render
函數代替模板的話,那麼就只剩一下一個 script
了。github
對於 props
,methods
它們只能在 scirpt
塊內定義,如:babel
props: {
name: String
}
methods: {
clear () {/*...*/}
}
複製代碼
而對於 slots 則既能夠在 script 塊內定義,也能夠在模板中定義,如:markdown
<!-- 在模板中定義 -->
<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
信息時須要考慮在內的。ui
一樣的,對於 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 的處理基本都是這個思路,更多內容你們能夠查看源碼:github.com/HcySunYang/…
通過上一步的處理,咱們能夠編寫出一個 parser
模塊,解析並組裝出咱們須要的內容,有了須要的內容以後,咱們就能夠根據這些信息編寫一個 Render
模塊,本質就是一個代碼生成的過程,至於生成的內容是什麼這取決於你想要的目標,vuese 內置的 Render
會根據這些信息爲你生成 markdown
文件,或者生成一個集成 docute
的文檔。但實際上只要你腦洞夠大你能夠生成任何東西,試想一下,若是咱們的 parser
模塊編寫的更加完善,對一個 vue
組件的分析足夠細緻,這樣咱們就能拿到一個 vue
組件所有的信息,而後在代碼生成階段將其生成一個 ts compatible 的組件,這不就實現了一個將非js編寫的vue組件轉換爲ts兼容的vue組件的插件了嗎?(備註:後來一哥們兒提供了一個更好的辦法來實現這件事兒,爲了不尷尬,我只能說這也是一個思路嘛......)
回到 vuese 內置的 Render 模塊,Render 的結果就是markdown資源,本質就是一個字符串拼接的過程,具體能夠查看源碼 github.com/HcySunYang/… ,並不複雜。
實際上 vuese 提供了不少有用的信息和文檔中沒有體現出來的功能,這多會在後續逐漸補充。舉個例子,使用 vuese 生成的markdown文件大體以下:
你可能已經注意到了,在上面的 markdown 文件中包含了不少諸如:
<!-- @vuese:CompName:props:start -->
<!-- @vuese:CompName:props:end -->
複製代碼
之類的註釋,它的做用是告訴 vuese 在生成文檔時要將生成的 markdown 代碼放置在什麼位置,若是一個組件尚未對應的markdown文檔,則新生成之,不然會在已有的文檔基礎上更新。這麼作的目的是出於真正使用場景的考慮,由於一個組件的文檔不可能僅僅包含上圖中展現的內容,它還可能包含開發者本身編寫的demo,和其餘描述內容。這樣咱們生成文檔的時候就不會覆蓋掉開發者本身編寫的內容,而是將生成的內容插入到指定的位置。
目前 vuese 已經實現的特性以下:
規劃中要實現的特性以下:另外目前還不支持插件系統,這也在將來的規劃當初。而且在咱們團隊內部已經將其應用在內部的組件庫的文檔維護,也計劃關注 vue3.0 併兼容之。最後 vuese 剛剛發佈有諸多不足之處,可是隨着後續的更新迭代,它會變得愈來愈好,也歡迎感興趣的同窗共建。
其餘規劃:模板支持 pug、插件系統、還有啥??????