你的.vue文件就已是你的文檔了

昨天發佈了vuese1.0,這是個人一個新的開源項目,用來解析Vue SFC並生成markdown文檔,這裏: github.com/HcySunYang/…html

這篇文章不會介紹如何使用,至於如何使用你們能夠查看 readme,這裏咱們主要說一說實現的思路。vue

1、動機

你或者你的團隊也許會有一套本身的組件庫(或者是單純的一個組件),一般當你開發完一個組件以後,你須要手動的編寫markdown文檔,從而讓其餘人瞭解組件是如何使用的。這裏的問題在於:倘若組件不停的在更新,你就必需要不停的手動維護對應的文檔,使其與組件的功能保持一致。實際上這個過程是有些噁心🤢的,那麼有什麼更好的辦法嗎?基於此我纔開發了 vuesenode

2、想法

咱們知道一個 vue 組件所暴露的接口無非是 propseventsslots(或scopedSlots) 以及部分 methods。那咱們可不能夠實現一個工具幫助咱們分析一個vue組件並提取這些信息呢?而後自動生成文檔,這樣不管組件如何改動咱們都不須要手動維護文檔了,只須要使用該工具從新生成便可。git

3、基本思路

對於一個 vue 組件,若是咱們拋開 style 和自定義塊,那麼它由兩部分組成,即:模板 和 script 塊。甚至若是使用 render 函數代替模板的話,那麼就只剩一下一個 script 了。github

對於 propsmethods 它們只能在 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。咱們要作的第一件事兒就是將 htmljs 單獨提取出來並單獨分析,好在巨人的肩膀厚實,已經有了 @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/…

4、生成目標

通過上一步的處理,咱們能夠編寫出一個 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,和其餘描述內容。這樣咱們生成文檔的時候就不會覆蓋掉開發者本身編寫的內容,而是將生成的內容插入到指定的位置。

5、規劃

目前 vuese 已經實現的特性以下:

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

另外目前還不支持插件系統,這也在將來的規劃當初。而且在咱們團隊內部已經將其應用在內部的組件庫的文檔維護,也計劃關注 vue3.0 併兼容之。最後 vuese 剛剛發佈有諸多不足之處,可是隨着後續的更新迭代,它會變得愈來愈好,也歡迎感興趣的同窗共建。

其餘規劃:模板支持 pug、插件系統、還有啥??????

相關文章
相關標籤/搜索