国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

解析如何自動(dòng)化生成vue組件文檔

發(fā)布時(shí)間:2021-08-17 12:16 來(lái)源: 閱讀:0 作者:vivo互聯(lián)網(wǎng)技術(shù) 欄目: JavaScript 歡迎投稿:712375056

目錄

      一、現狀

      Vue框架在前端開(kāi)發(fā)中應用廣泛,當一個(gè)多人開(kāi)發(fā)的Vue項目經(jīng)過(guò)長(cháng)期維護之后往往會(huì )沉淀出很多的公共組件,這個(gè)時(shí)候經(jīng)常會(huì )出現一個(gè)人 開(kāi)發(fā)了一個(gè)組件而其他維護者或新接手的人卻不知道這個(gè)組件是做什么的、該怎么用,還必須得再去翻看源碼,或者壓根就沒(méi)注意到這個(gè)組件 的存在導致重復開(kāi)發(fā)。這個(gè)時(shí)候就非常需要維護對應的組件文檔來(lái)保障不同開(kāi)發(fā)者之間良好的協(xié)作關(guān)系了。

      但是傳統的手動(dòng)維護文檔又會(huì )帶來(lái)新問(wèn)題:

      • 效率低,寫(xiě)文檔是個(gè)費時(shí)費力的體力活,好不容易抽時(shí)間把組件開(kāi)發(fā)完了回頭還要寫(xiě)文檔,想想都頭大。
      • 易出錯,文檔內容容易出現差錯,可能與實(shí)際組件內容不一致。
      • 不智能,組件更新迭代的同時(shí),需要手動(dòng)將變更同步到文檔中,消耗時(shí)間還容易遺漏。

      而理想中的文檔維護方式則是:

      • 工作量小,能夠結合Vue組件自動(dòng)獲取相關(guān)信息,減少從頭開(kāi)始寫(xiě)文檔的工作量。
      • 信息準確,組件的關(guān)鍵信息與組件內容一致,不出錯。
      • 智能同步,Vue組件迭代升級時(shí),文檔內容可以自動(dòng)的同步更新,無(wú)需人工校驗信息是否一致。

      二、社區解決方案

      2.1、業(yè)務(wù)梳理

      為了能實(shí)現上述理想效果,我搜索并研究了一下社區中的解決方案,目前Vue官方提供了Vue-press可以用于快速搭建Vue項目文檔, 而且也已經(jīng)有了可以自動(dòng)從Vue組件中提取信息的庫了。

      但是已有的第三方庫并不能完全滿(mǎn)足需求,主要存在以下兩個(gè)問(wèn)題:

      • 信息不全面,一些重要內容無(wú)法獲取例如不能處理v-model,不能解析屬性的修飾符sync,不能獲取methods中函數入參的詳細信息等。
      • 比如下面的例子,value屬性與input事件可以合起來(lái)構成一個(gè)v-model屬性,但是這個(gè)信息在生成的文檔中沒(méi)有體現出來(lái),要文檔讀者自行理解判斷。而且生成的文檔中沒(méi)有展示是否支持sync。

      有較多的自定義標識,而且標識的命名過(guò)于個(gè)性化,對原有的代碼侵入還是比較大的。例如下圖中的代碼,為了標記注釋?zhuān)枰谠械?業(yè)務(wù)代碼中額外添加"@vuese" "@arg"等標識,使得業(yè)務(wù)代碼多出了一些業(yè)務(wù)無(wú)關(guān)內容。

      三、技術(shù)方案

      針對以上文中提到的問(wèn)題以及社區方案的不足,我們團隊內沉淀出了一個(gè)小工具專(zhuān)門(mén)用于Vue組件信息獲取并輸出組件文檔,大致效果如下:

      上圖中左邊是一個(gè)常見(jiàn)的Vue單文件組件,右邊是生成的文檔。我們可以看到我們從組件中成功的提取到了以下一些信息:

      • 組件的名稱(chēng)。
      • 組件的說(shuō)明。
      • props,slot,event,methods等。
      • 組件的注釋內容。

      接下來(lái)我們將詳細的講解如何從組件中提取這些信息。

      3.1、Vue文件解析

      既然是要從Vue組件中提取信息,那么首先的問(wèn)題就是如何解析Vue組件。Vue官方開(kāi)發(fā)了Vue-template-compiler庫專(zhuān)門(mén)用于Vue解析, 這里我們也可以用同樣的方式來(lái)處理。通過(guò)查閱文檔可知Vue-template-compiler提供了一個(gè)parseComponent方法可以對原始的Vue文件進(jìn)行處理。

      import { parseComponent } from 'Vue-template-compiler'
      const result = parseComponent(VueFileContent, [options])

      處理后的結果如下,其中template和script分別對應Vue文件中的template和script的文本內容。

      export interface SFCDescriptor {
        template: SFCBlock | undefined;
        script: SFCBlock | undefined;
        styles: SFCBlock[];
        customBlocks: SFCBlock[];
      }

      當然僅僅是得到文本是不夠的,還需要對文本進(jìn)行更進(jìn)一步的處理來(lái)獲取更多的信息。得到script后,我們可以用babel把js編譯成js的AST(抽象語(yǔ)法樹(shù)),這個(gè)AST是一個(gè)普通的js對象,可以通過(guò)js進(jìn)行遍歷和讀取 有了Ast之后我們就可以從中獲取到我們想到詳細的組件信息了。

      import { parse } from '@babel/parser';
      const jsAst = parse(script, [options]);

      接著(zhù)我們來(lái)看template,繼續查找Vue-template-compiler的文檔我們找到compile方法,compile是專(zhuān)門(mén)用于將template編譯成AST的, 正好可以滿(mǎn)足需求。

      import { compile } from 'Vue-template-compiler'
      const templateAst = compile(template, [options]);

      得到結果中的ast則為template的編譯結果。

      export interface CompiledResult {
        ast: ASTElement,
        render: string,
        staticRenderFns: Array<string>,
        errors: Array<string>
      }

      通過(guò)第一步的文件解析工作,我們成功獲取到了Vue的模板ast和script中的js的AST,下一步我們就可以從中獲取我們想要的信息了。

      3.2、信息提取

      根據是否需要約定,信息可以分為兩種:

      • 一種是可以直接從Vue組件中獲取,例如props、events等。
      • 另一種是需要額外約定格式的,例如:組件的說(shuō)明注釋?zhuān)琾rops的屬性說(shuō)明等,這部分可以放到注釋里,通過(guò)對注釋進(jìn)行解析獲取。

      為了方便的從ast中讀取信息,這里先簡(jiǎn)單介紹一個(gè)工具@babel/traverse,這個(gè)庫是babel官方提供的專(zhuān)門(mén)用于遍歷js AST的。使用方式如下;

      import traverse from '@babel/traverse'
      
      traverse(jsAst, options);

      通過(guò)在options中配置對應內容的回調函數,可以獲得想要的ast節點(diǎn)。具體的使用可以參考

      3.2.1、可直接獲取的信息

      可以從代碼中直接獲取的信息可以有效的解決信息同步問(wèn)題,無(wú)論代碼怎么變動(dòng),文檔的關(guān)鍵信息都可以自動(dòng)同步,省去了人工校對的麻煩。

      可以直接獲取的信息有:

      • 組件屬性props
      • 提供外部調用的方法methods
      • 事件events
      • 插槽slots

      1、2都可以利用traverse在js AST上直接遍歷名稱(chēng)為props和methods的對象節點(diǎn)獲取。

      事件的獲取稍微麻煩一點(diǎn),可以通過(guò)查找$emit函數來(lái)定位到事件的位置,而$emit函數可以在traverse中監聽(tīng)MemberExpress(復雜類(lèi)型節點(diǎn)), 然后通過(guò)節點(diǎn)上的屬性名是否是'$emit'判斷是否是事件。如果是事件,那么在$emit父級中讀取arguments字段, arguments的第一個(gè)元素就是事件名稱(chēng),后面的元素為事件傳參。

      this.$emit('event', arg);
      traverse(jsAst, {
       MemberExpression(Node) {
        // 判斷是不是event
        if (Node.node.property.name === '$emit') {
        // 第一個(gè)元素是事件名稱(chēng)
          const eventName = Node.parent.arguments[0];
        }
       }
      });

      在成功獲取到Events后,那么結合Events和props,就可以進(jìn)一步的判斷出props中的兩個(gè)特殊屬性:

      • 是否存在v-model:查找props中是否存在value屬性并且Events中是否存在input事件來(lái)確定。
      • props的某個(gè)屬性是否支持sync:判斷Events的時(shí)間名中是否存在有update開(kāi)頭的事件,并且事件名稱(chēng)與屬性名相同。

      插槽slots的信息保存在上文的template的AST中,遞歸遍歷template AST找到名為slots的節點(diǎn),進(jìn)而還可以在節點(diǎn)上查找到name。

      3.2.2、需要約定的信息

      為什么除了可直接獲取的組件信息之外,還會(huì )需要額外的約定一部分內容呢?其一是因為可直接獲取的信息內容比較單薄,還不足以支撐起一個(gè)相對完善的組件文檔;其二是我們日常開(kāi)發(fā)組件時(shí)本身就會(huì )寫(xiě)很多的注釋?zhuān)绻苤苯訉⒉糠肿⑨屘崛〕鰜?lái)放到文檔中,可以大大降低文檔維護的工作量;

      整理一下可以約定的內容有以下幾條:

      • 組件名稱(chēng)。
      • 組件的整體介紹。
      • props、Events、methods、slots文字說(shuō)明。
      • Methods標記和入參的詳細說(shuō)明。這些內容都可以放在注釋中進(jìn)行維護,之所以放在注釋中進(jìn)行維護是因為注釋可以很容易從上文提到的js AST以及template AST中獲取到, 在我們解析Vue組件信息的同時(shí)就可以把這部分針對性的說(shuō)明一起解析到。

      接下來(lái)我們著(zhù)重講解如何將提取注釋和注釋與被注釋的內容是如何對應起來(lái)的。

      js中的注釋根據位置不同可以分為頭部注釋(leadingComments)和尾部注釋(trailingComments),不同位置的注釋會(huì )存放在對應的字段中, 代碼展示如下:

      // 頭部注釋export default {} // 尾部注釋

      解析結果

      const exportNode = {
        type: "ExportDefaultDeclaration",
        leadingComments: [{
          type: 'CommentLine',
          value: '頭部注釋'
        }],
        trailingComments: [{
          type: 'CommentLine',
          value: '尾部注釋'
        }]
      }

      在同一個(gè)位置上,根據注釋格式的不同又分為單行注釋(CommentLine)和塊級注釋(CommentBlock),兩種注釋的區別會(huì )反應在注釋節點(diǎn)的type字段中:

      /** * 塊級注釋 */ // 單行注釋 export default {}

      解析結果

      const exportNode = {
        type: "ExportDefaultDeclaration",
        leadingComments: [
          {
            type: 'CommentBlock',
            value: '塊級注釋'
          },
          {
            type: 'CommentLine',
            value: '單行注釋'
          }
        ]
      }

      另外,從上面的解析結果我們也可以看到,注釋節點(diǎn)是掛載在被注釋的export節點(diǎn)里面的,這也解決我們上面提到的另一個(gè)問(wèn)題:注釋與被注釋的關(guān)聯(lián)關(guān)系怎么獲取的--其實(shí)babel在編譯代碼的時(shí)候已經(jīng)替我們做好了。

      template查找注釋與被注釋內容的方法不同。template中注釋節點(diǎn)與其他節點(diǎn)一樣是作為dom節點(diǎn)存在的, 在遍歷節點(diǎn)的時(shí)候通過(guò)判斷isComment字段的值是否為true來(lái)確定是否是注釋節點(diǎn)。而被注釋的內容的位置在兄弟節點(diǎn)的后一位:

      <!--template的注釋--> <slot>被注釋的節點(diǎn)</slot>

      解析結果

      const templateAst = [
        {
          isComment: true,
          text: "template的注釋",
          type: 3
        },
        {
          tag: "slot",
          type: 1
        }
      ]

      知道了如何處理注釋內容,那么我們還可以利用注釋做更多的事情。例如可以通過(guò)在methods的方法的注釋中約定一個(gè)標記@public來(lái)區分是私有方法還是公共方法,如果更細節一點(diǎn)的話(huà), 還可以參考另一個(gè)專(zhuān)門(mén)用于解析js注釋的庫js-doc的格式,對方法的入參進(jìn)行更進(jìn)一步的說(shuō)明,豐富文檔的內容。

      我們只需要在獲取到注釋內容之后對文本進(jìn)行切割讀取即可,例如:

      export default {
        methods: {
          /**
           * @public
           * @param {boolean} value 入參說(shuō)明
           */
          show(value) {}
        }
      }

      當然了為了避免對代碼侵入過(guò)多,我們還是需要盡量少的添加額外的標識。而入參說(shuō)明采用了與js-doc相同的格式,主要還是因為這套方案 使用比較普遍,而且代碼編輯器都自動(dòng)支持方便編輯。

      四、總結

      編寫(xiě)組件文檔是一個(gè)可以很好的提升項目?jì)雀鱾€(gè)前端開(kāi)發(fā)成員之間協(xié)作的事情,一份維護良好的文檔會(huì )極大的改善開(kāi)發(fā)體驗。而如果能進(jìn)一步的使用工具把維護文檔的過(guò)程自動(dòng)化的話(huà),那開(kāi)發(fā)的幸福感還能得到再次提升。

      經(jīng)過(guò)一系列的摸索和嘗試,我們成功的找到了 自動(dòng)化提取Vue組件信息的方案,大大減輕了維護Vue組件文檔的工作量,提升了文檔信息的準確度。具體實(shí)現上,先用vue-template-compiler對Vue文件進(jìn)行處理,獲得template的AST和js的AST,有了這兩個(gè)AST后就可以去獲取更加詳細的信息了, 梳理一下到目前為止我們生成的文檔里可以獲取到的內容及獲取方式:

      至于獲取到內容之后是以Markdown的形式輸出還是json文件的形式輸出,就取決于實(shí)際的開(kāi)發(fā)情況了。

      五、展望

      這里我們所討論的是直接從單個(gè)Vue文件去獲取信息并輸出,但是像很多第三方組件庫里例如elementUI的文檔,不僅有組件信息還有展示實(shí)例。如果一個(gè)組件庫維護的相對完善的話(huà),一個(gè)組件應該會(huì )有對應的測試用例,那么是否可以將組件的測試用例也提取出來(lái), 實(shí)現組件文件中示例部分的自動(dòng)提取呢?這也是值得研究的問(wèn)題。

      以上就是解析如何自動(dòng)化生成vue組件文檔的詳細內容,更多關(guān)于自動(dòng)化生成vue組件文檔的資料請關(guān)注腳本之家其它相關(guān)文章!

      免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

      啦啦啦WWW高清在线观看视频| 99久久婷婷国产一区二区| 免费三级网站| 亚洲精品无码AAAAAA片| 亚洲 日韩 激情 无码 中出| 小蜜被两老头吸奶头在线观看|