最近在項目中遇到了一個相似Collapse的交互需求,所以到github上找了一圈關於Vue Collapse的相關輪子,可是多少都有些問題。有的是實現問題,例如vue2-collapse,伸縮部分採用max-height指定動畫,存在缺陷;還有的是擴展性問題,遇到定製場景比較棘手。所以,決定本身擼一個Collapse組件。從項目中的一個需求,到目前已將它開源併發布到npm,仍是踩了許多坑的。代碼雖然簡單,可是過程卻不太容易。所以這篇文章不是安利這款組件r-collapse-vue,僅僅是想記錄一下整個開發生命週期,須要作什麼,以及遇到什麼問題。固然了,若是這個組件或是這篇文章對你有幫助,勞煩點進去給個star,萬分感謝~javascript
咱們的整個開發流程,能夠簡單的總結以下:html
咱們來詳細聊一聊每一個過程是如何實施的,且遇到了哪些問題。前端
腳手架咱們直接使用Vue CLI來搭建便可,其已經提供了豐富的功能,而且能夠經過vue.config.js擴展webpack的能力。可是要注意的是,咱們的構建產物是一個模塊,而不是咱們平時在項目中構建出一個應用。咱們但願構建出來的模塊是一個兼容CommonJs或是UMD,以便於使用者在不一樣的環境中引用。所幸,Vue CLI3也給我提供了這樣一個功能,詳細可參考文檔。vue
其次,本次開發我選擇了TypeScript,腳手架默認集成了vue-property-decorator。使用以後直觀的感覺就是,Vue的整個生態對TS的支持還不夠完善,但總體仍是比較爽的,期待官方在3.0中可以完全支持TS。本文主題不是討論TS,所以簡單羅列下使用時遇到的問題:java
@Prop({ required: true }) public value!: String;
在平常寫業務的時候,咱們可能會在組件當中耦合不少的業務邏輯。可是做爲一個通用組件,咱們在開發的時候要儘量保證它的擴展性,所以咱們但願達到的一個目標就是:在保證開發體驗的前提下提升擴展性。對於Collapse組件,UI方面通常都是按照各自的設計稿來自行編寫的,所以咱們只須要提供功能便可。更好的方式是提供默認的UI,但又能夠支持徹底定製,這個是目前r-collapse-vue能夠完善的一個點。
在進行功能設計的過程當中,咱們要先肯定咱們須要支持哪些功能,以r-collapse-vue舉例,須要提供的功能包括:webpack
在實現的過程當中,咱們也須要思考不少細節,舉幾個例子:git
最簡單的想法是傳遞一個相似叫作status的prop,在每個Collapse內部去維護這個狀態。可是這樣會有一個問題,咱們如何去支持手風琴模式,即一個展開另外的都須要收起。按照這種作法,須要用一個父組件包裹,去獲取每個Collapse子組件的實例,調用實例方法去控制。這樣作不是不行,vue2-collapse就是這麼作的,可是我認爲不夠優雅。所以咱們從新整理思路,每個Collapse之間的狀態可能會互相影響,咱們經常使用的解決方法是狀態提高,所以個人作法是抽象兩個組件,Collapse和CollapsePanel,Collapse便是父組件,提供狀態控制,將狀態傳遞給其內部嵌套的CollapsePanel,在內部消化掉全部的邏輯,這更加符合單向數據流的思想,站在使用者角度來看,寫法也可以相對統一,使用時咱們只需這麼寫:github
<r-collapse v-model="activeKeys"> <r-collapse-panel name="a">xxxx</r-collapse-panel> <r-collapse-panel name="b">xxxx</r-collapse-panel> </r-collapse>
見上面的代碼,咱們在CollapsePanel中傳入了一個name屬性做爲惟一標識,此時使用者可結合activeKeys自行判斷當前panel是否展開:web
<r-collapse v-model="activeKeys"> <r-collapse-panel name="a" :class="activeKeys.includes('a') ? 'active': ''" > xxxx </r-collapse-panel> <r-collapse-panel name="b">xxxx</r-collapse-panel> </r-collapse>
這種方法雖然能夠,可是存在兩個問題:vue-cli
所以,能夠提供一個activeClass的prop,讓使用者能夠自定義展開狀態的類名,就能夠避免以上的問題。
這些細節問題看似簡單,可是做爲一個通用組件的開發者,咱們應該常常站在使用者的角度看問題,才能不斷地提高組件的開發體驗。
一個優秀的開源組件必定少不了單元測試,例如Ant Design等開源庫都有着很高的單測覆蓋率。一開始寫單測可能會以爲耗時、沒有必要,但其實單測可以帶來諸多的好處:
所以,單測必不可少,目前前端常見的選擇包括:
Vue當中已經給咱們提供了單測相關的工具Vue Test Utils,它提供了不少功能,如組件掛載,獲取實例等等,使用它配合Jest或者Mocha可以比較方便的完成單測,詳情參考文檔。
在編寫單測時,咱們須要注意,對於UI組件來講,不該一味追求行級覆蓋率,應當只關注輸入輸出,避免涉及過多的實現細節,從而避免瑣碎的測試。例如,咱們測試展開功能,只須要觸發click,檢測status是否爲true便可,無需關注過程當中是觸發了xxx事件仍是發生了其餘事情,這樣當咱們的邏輯修改後可以保證單測還能有效。同時,在用TS編寫單測時,經過Vue Test Utils建立的wrapper是普通的Vue類型,所以自定義的Vue組件沒法進行類型推導,此時要獲取實例屬性時須要經過(wrapper.vm as any).xxx來獲取。經查閱資料,官方表示目前無法解決這個問題,只能使用這種方式。
一個好的文檔可以方便使用者明白你的設計理念,所以咱們想要的文檔不只須要有完整的API描述,而且在展現demo時可以同時展現源碼,相似於在Ant Design或Element中那樣。咱們這邊使用的是Vue Styleguidist。
它經過vue-docgen-api,可以將註釋轉換成屬性描述展示在頁面上。所以咱們只須要寫註釋,就可以生成組件屬性相關的文檔。而咱們的另外一個需求,在展現demo時可以同時展現源碼,它也可以作到。咱們能夠經過兩種方式:
咱們選用第二種方式,可是又遇到了許多坑。好比寫入md的Vue代碼不支持TS,試了不少的方法都沒有解決,後來仍是改爲了JS寫法;還有SCSS使用嵌套時,嵌套的內容未被正確解析,後改爲了CSS。其實這個東西的實現難度並不高,在md中寫Vue無非就是寫個webpack插件解析.md格式的文件,取出Vue的部分經過vue-loader處理,鑑於bug這麼多且樣式我認爲不夠美觀,以後有時間能夠再造個輪子玩一玩。
在Vue CLI3中使用Vue Styleguidist十分方便,只要運行:
vue add styleguidist
而後在package.json的scripts中添加:
"serve:doc": "vue-cli-service styleguidist", "build:doc": "vue-cli-service styleguidist:build"
就能夠拆箱即用了。
文檔編寫完成,咱們執行yarn build:doc構建文檔,發現輸出的是一個html文件,此時咱們能夠選擇使用Github Pages來做爲咱們的靜態資源服務器展現文檔,由於它方便部署且免費。過程以下:
這樣每次更新docs會自動部署更新文檔,相似於這樣https://danceonbeat.github.io/r-collapse-vue/,惟一的缺點就是國內打開有點慢。
說完文檔,咱們還須要編寫在Github上展現的README,這裏推薦一個生成README的庫,readme-md-generator,格式很是簡潔且美觀。在README中,咱們能夠添加以下的小圖標:
這個可使用shields生成,它能關聯你的NPM、Github等等,實時更新icon信息,有了它文檔逼格瞬間高多了。
要將包發佈到NPM,咱們須要作以下的準備工做:
npm login --registry=https://registry.npmjs.org
注意,這邊加上registry爲了防止在全局或當前環境覆寫.npmrc,致使登陸的不是NPM源。
{ "name": "r-collapse-vue", "version": "1.0.0", "description": "a collapse component for VueJs", "author": { "name": "Ray", "email": "zhurui0904@gmail.com" }, "main": "dist/r-collapse-vue.common.js", "files": [ "dist" ], "keywords": [ "Vue", "collapse" ], "publishConfig": { "registry": "https://registry.npmjs.org" }, "repository": { "type": "git", "url": "git@github.com:DanceOnBeat/r-collapse-vue.git" } }
每次發佈新版本以前,咱們能夠經過
npm version major/minor/patch -m 'xxx'
來修改版本號而且打上tag,此tag非NPM的dist-tag,而是Git的tag。一個版本對應一個tag,並經過
git push origin master --tags
將tag也推到遠程倉庫,這樣在倉庫中咱們就能清楚地看到發佈的記錄,方便往後回滾之類的操做。具體的版本規則能夠參考semver規範。
當開發結束後,咱們須要跑測試,測試經過後,還須要構建生成dist目錄,最後發佈到NPM。每次修改都作這樣一套操做實在繁瑣,而且容易遺漏步驟,這時候咱們就須要使用CI將咱們的流程自動化,我在這邊選擇了TravisCI。同時,咱們還能夠經過Codecov,將咱們的單測報告上傳至Codecov服務器,這樣就能同步更新Codecov的icon。
在配置CI時,我本來將生成docs的步驟也添加了進去,此時咱們在deploy中會有兩個步驟,以下:
deploy: - provider: npm email: zhurui0904@gmail.com api_key: $AUTH_TOKEN on: tags: true branch: master skip_cleanup: true - provider: pages skip_cleanup: true github_token: $GITHUB_TOKEN keep_history: true target_branch: master on: branch: master
這會形成一個問題是provider: pages會將CI服務器生成的新的docs目錄push到咱們的Github倉庫,這又會觸發一次CI,以致於無限循環。後來也沒找到合適的解決方案,又考慮到文檔不常常更新,就將文檔部署相關的部分從CI中移除了。若是你們有合適的解決方案,能夠留言告訴我一下,不勝感激。
以前咱們提到一個NPM發佈版本對應一個tag,所以咱們能夠在配置中添加
if: tag IS present
限定只在提交了tag才觸發一次自動化構建,這樣基本上就大功告成了。
這是一次很是有趣的造輪體驗,代碼雖然不難,可是過程當中又學習到了不少新的東西,包括單元測試、文檔編寫等等,但願這篇文章能給準備造輪或想要造輪的小夥伴提供一點幫助。