開發一個高質量的前端組件,這些姿式必定要知道

前言

2009 年 11 月 8 日,在歐洲 JSConf 大會上,Ryan Dahl 第一次正式向業界宣佈了 Node.js 的面世,使 JS 語言書寫後端應用程序成爲了可能。在隨後的幾年裏,Node.js 受到了 JavaScript 社區的狂熱追捧,前端行業也所以進入了一個全新的工程化和全棧時代。回顧歷史,總會讓人心潮澎湃。在這股浪潮中,有無數的人和項目在這座豐碑中刻下了本身的名字:React、Vue、Yeoman、RequireJS、Backbone、Antd、Webpack、Mocha、Eslint 等等。在這些知名項目的熠熠光輝下,咱們可能會忽略爲 Node.js 生態的繁榮之下創建不世之功的 NPM,它纔是當之無愧的肱骨重臣。
NPM 生於 2010 年 1 月,它從出生就揹負了讓 Node.js 社區更加繁榮的使命。NPM 致力於讓 JS 程序員可以更加方便地發佈、分享 Node.js 類庫和源碼,而且簡化模塊的安裝、更新和卸載的體驗。

從今天(2019 年)這個時間節點來看,NPM 不管從知名度、模塊數量、社區的話題數量來看,都算得上是一騎絕塵,將其餘語言的模塊倉庫遠遠甩在了後面。前端

開發一個高質量的前端組件,這些姿式必定要知道

數據來源: moudlecountsvue

NPM 的生態既已如此成熟,按說開發者對於 NPM 包的發佈和維護應該很是熟悉纔是,但事實真的是這樣嗎?環顧身邊的 FE,沒有發過任何 NPM 包的同窗大有人在,已經發過包的同窗也有至關一部分並未考慮過如何纔算規範、高質量地發佈一個包。node

現在 NPM 的模塊數量已上升至 100W,在這樣一個 JavaScript 組件化開發時代,除了能找到好用的組件,咱們天然也須要了解如何才能成爲創造這個時代的一員。而第一步就是要知道並掌握如何規範地、負責任地發佈一個 NPM 包?react

這就是本文接下來的主要內容。git

開發一個高質量的前端組件,這些姿式必定要知道

《蛻變》 | 2019 年"十一",做者拍攝於雨岔峽谷。程序員

1、組件化思考github

發佈人生中第一個 NPM 組件雖然只是在終端命令行中瀟灑地敲下npm publish,靜等成功通知便可,但這從 0 到 1 的跨越卻並不是易事。這個行爲背後的始做俑者是開發者大腦中開始萌發組件化思惟方式,開始去思考何爲組件?爲何要發佈組件?這些更深層次的問題。web

組件的存在的終極意義是爲了複用,一個組件只要具有了被複用的條件,而且開始被複用,那麼它的價值纔開始產生。組件複用的次數越高、被傳播的越廣,其價值就越大。而要實現組件的價值最大化,須要考慮如下幾點:typescript

  1. 我要寫一個什麼組件?組件提供什麼樣的能力?
  2. 組件的適用範圍是什麼?某個具體業務系統內仍是整個團隊、公司或者社區?
  3. 組件的生產過程是否規範、健壯和值得信賴?
  4. 組件如何被開發者發現和認識?
以上四點中,前兩點是生產組件必需要思考的問題;第四點是組件如何推廣運營的問題,這是另一個話題,本文不展開探討;第三點是開發者的基本素養,它決定了開發者如何看待這個組件,也間接暴露了開發者的素養和可信賴程度。

2、組件開發的最佳姿式npm

一個優秀的組件除了擁有解決問題的價值,還應該具有如下三個特色:

  1. 生產和交付的規範性;
  2. 優秀的質量和可靠性;
  3. 較高的可用性。

只有三者都能知足才能夠稱其爲優秀組件,不然會給使用者帶來各類各樣的困惑:常常出 Bug、坑不少、不穩定、文檔太簡單、不敢用等等。

2.1 規範性

2.1.1 目錄結構

事實上,社區並無一個官方的或者全部人都認同的目錄結構規範,但從耳熟能詳的知名項目中進行統計和分析,能夠得出一個社區優秀開發者達成非官方共識的一個目錄結構清單:

├─ test // 測試相關  
├─ scripts // 自定義的腳本  
├─ docs // 文檔,一般文檔較多,有多個 md 文檔  
├─ examples // 能夠運行的示例代碼  
├─ packages // 要發佈的 npm 包,通常用在一個倉庫要發多個 npm 包的場景  
├─ dist|build // 代碼分發的目錄  
├─ src|lib // 源碼目錄  
├─ bin // 命令行腳本入口文件  
├─ website|site // 官方網站相關代碼,譬如 antd、react  
├─ benchmarks // 性能測試相關  
├─ types|typings// typescript 的類型文件  
├─ Readme.md // 倉庫介紹或者組件文檔  
└─ index.js // 入口文件

以上目錄清單是一個比較完整的清單,大多數組件只需根據本身的需求選擇性地使用一部分便可。一份幾乎適用於全部組件的最小目錄結構清單以下:

├─ test // 測試相關  
├─ src|lib // 源碼目錄  
├─ Readme.md // 倉庫介紹或者組件文檔  
└─ index.js // 入口文件

2.1.2 配置文件

這裏的配置文件主要指的是各類工程化工具所依賴的本地化的配置文件,以及在 GitHub 上開源所須要聲明的一些文件。一份比較全的配置文件清單以下:

├─ .circleci // 目錄。circleci 持續集成相關文件  
├─ .github // 目錄。github 擴展配置文件存放目錄  
│ ├─ CONTRIBUTING.md  
│ └─ ...  
├─ .babelrc.js // babel 編譯配置  
├─ .editorconfig // 跨編輯器的代碼風格統一  
├─ .eslintignore // 忽略 eslint 檢測的文件清單  
├─ .eslintrc.js // eslint 配置  
├─ .gitignore // git 忽略清單  
├─ .npmignore // npm 忽略清單  
├─ .travis.yml // travis 持續集成配置文件  
├─ .npmrc // npm 配置文件  
├─ .prettierrc.json // prettier 代碼美化插件的配置  
├─ .gitpod.yml // gitpod 雲端 IDE 的配置文件  
├─ .codecov.yml // codecov 測試覆蓋率配置文件  
├─ LICENSE // 開源協議聲明  
├─ CODE\_OF\_CONDUCT.md // 貢獻者行爲準則  
└─ ... // 其餘更多配置

以上配置能夠根據組件的實際狀況、適用範圍來進行刪減。一份在各類場景都比較通用的清單以下:

├─ .babelrc.js // babel 編譯配置  
├─ .editorconfig // 跨編輯器的代碼風格統一  
├─ .eslintignore // 忽略 eslint 檢測的文件清單  
├─ .eslintrc.js // eslint 配置  
├─ .gitignore // git 忽略清單  
├─ .npmignore // npm 忽略清單  
├─ LICENSE // 開源協議聲明  
└─ ... // 其餘更多配置

上述清單移除了只有在 GitHub 上才用獲得的配置,只關注倉庫管理、發包管理、靜態檢查和編譯這些基礎性的配置,適用於團隊內部、企業私有環境的組件開發。若是要在 GitHub 上維護,則還須要從大清單中繼續挑選更多的基礎配置,以即可以使用 GitHub 的衆多強大功能。

2.1.3 package.json

若是說NPM官方給出了一個發包規範的話,那麼這個規範就是package.json文件,這是發包時惟一不可或缺的文件。一個最精簡的 package.json 文件是執行npm init生成的這個版本:

{  
 "name": "npm-speci-test", // 組件名  
 "version": "1.0.0", // 組件當前版本  
 "description": "", // 組件的一句話描述  
 "main": "index.js", // 組件的入口文件  
 "scripts": { // 工程化腳本,使用 npm run xx 來執行  
 "test": "echo \\"Error: no test specified\\" && exit 1"  
 },  
 "author": "", // 組件的做者  
 "license": "ISC" // 組件的協議  
}

有這樣一個版本的 package.json 文件,咱們就能夠直接在該目錄下直接執行npm publish發佈操做了,若是 name 的名稱在 npm 倉庫中還沒有被佔用的話,就能夠看到發包成功的反饋了:

$ npm publish  
\+ npm-speci-test@1.0.0

但光有這些基礎信息確定是不夠的,做爲一個規範的組件,咱們還須要考慮:

  1. 個人代碼託管在什麼位置了;
  2. 別人能夠在倉庫裏經過哪些關鍵詞找到組件;
  3. 組件的運行依賴有哪些;
  4. 組件的開發依賴有哪些;
  5. 若是是命令行工具,入口文件是哪一個;
  6. 組件支持哪些 node 版本、操做系統等。

一份比較通用的 package.json 文件內容以下:

{  
 "name": "@scope/xxxx",  
 "version": "1.0.0",  
 "description": "description:xxx",  
 "keywords": "keyword1, keyword2,...",  
 "main": "./dist/index.js",  
 "bin": {},  
 "scripts": {  
 "lint": "eslint --ext ./src/",  
 "test": "npm run lint & istanbul cover \_mocha -- test/ --no-timeouts",  
 "build": "npm run lint & npm run test & gulp"  
 },  
 "repository": {  
 "type": "git",  
 "url": "http://github.com/xxx.git"  
 },  
 "author": {  
 "name": "someone",  
 "email": "someone@gmail.com",  
 "url": "http://someone.com"  
 },  
 "license": "MIT",  
 "dependencies": {},  
 "devDependencies": {  
 "eslint": "^5.2.0",  
 "eslint-plugin-babel": "^5.1.0",  
 "gulp": "^3.9.1",  
 "gulp-rimraf": "^0.2.0",  
 "istanbul": "^0.4.5",  
 "mocha": "^5.2.0"  
 },  
 "engines": {  
 "node": ">=8.0"  
 }  
}
  • name屬性要考慮的是組件是否爲 public 仍是 private,若是是 public,要先確認該名稱是否已經被佔用,若是沒有佔用,爲了穩妥起見,能夠先發一個空白的版本;若是是 private,則須要加上 @scope 前綴,一樣也須要確認名稱是否已被佔用。
  • version屬性必需要符合 semver 規範,簡單理解就是:
  • 第一個版本通常建議用 1.0.0;
  • 若是當前版本有破壞性變動,沒法向前兼容,則考慮升第一位;
  • 若是有新特性、新接口,但能夠向前兼容,則考慮升第二位;
  • 若是隻是 bug 修復,文檔修改等不影響兼容性的變動,則考慮升第三位。
  • keywords會影響在倉庫中進行檢索的結果。
  • main入口文件的位置最好能夠固定下來,若是組件須要構建,建議統一設置爲./dist/index.js, 若是不須要構建,能夠指定爲根目錄下的index.js。
  • scriptsscripts 一般會包含兩部分:通用腳本和自定義腳本。不管是我的仍是團隊,都應該爲通用腳本創建規範,避免過於隨意的命名 scripts;自定義腳本則能夠靈活定製,好比:
  • 通用 scripts:start、lint、test、build;
  • 自定義 scripts:copy、clean、doc 等。
  • repository屬性不管在私有環境仍是公共環境,都應該加上,以便經過組件能夠定位到源碼倉庫。
  • author 若是是一我的負責的組件,用 author,多我的就用contributors。

更詳細的 package.json 文件規範能夠參見 npm-package.json。

2.1.4 開發流程

不少同窗在開發組件時都會使用 master 分支直接進行開發,以爲差很少能夠發版了就直接手動執行一下npm publish,而後下一個版本,繼續在 master 上搞。

這樣作是很是不規範的,會存在不少問題,譬如:

  1. 正在開發一個比較大的版本,此時當前線上版本發現一個重要 bug 須要緊急修復;
  2. 沒有爲每個發佈的版本指定惟一的 tag 標籤以便回溯。

git 的 workflow 有不少種,各有適合的場景和優缺點。開發組件大多數時候是我的行爲,偶爾是 team 行爲,因此不太適合用比較複雜的流程。我的觀點是,若是是在 GitHub 上維護的開源組件,則參照 GitHub 流程;若是是我的或者公司內私有環境,只要能保障並行多個版本,而且每個發佈的版本可回溯便可,能夠在 GitHub 流程上精簡一下,不區分 feature 和 hotfix,統一採用分支開發,用 master 做爲線上分支和預發分支,開發分支要發版須要預先合併到 master 上,而後在 master 上 review 和單測後直接發佈,並打 tag 標籤,省略掉 pull request 的流程。

2.1.5 commit && changelog

一個組件從開發到發佈一般會經歷不少次的代碼 commit,若是能在一開始就瞭解 git commit 的 message 書寫規範,並經過工具輔助以便低成本地完成規範的實踐落地,則會爲組件的問題回溯、瞭解版本變動明細帶來很大的好處。咱們可能都見過 Node.js 項目的 changelog 文件:

開發一個高質量的前端組件,這些姿式必定要知道

很是規範地將當前版本的全部關鍵 Commit 記錄所有展現出來,每一條 commit 記錄的信息也很是完整,包含了:commit 的 hash 連接、修改範圍、修改描述以及修改人和pull request 地址。試想一下,若是前期 commit 階段沒有很好的規範和工具來約束,手工完成這個工做須要花多長時間才能搞定呢?

目前社區使用最普遍的 commit message 規範是:Conventional Commits,由 Angular Commit 規範演變而來,而且配備了很是全的工具:從 git commit 命令行工具 commitizen,到自動生成 Changelog 文件,以及 commitlint 規範驗證工具,覆蓋很是全面。

2.2 質量和可維護性

開發組件的出發點是爲了複用,其價值也體如今最大程度的複用上。團隊內部的組件可能會在整個團隊的多個系統間複用;公司內部通用的組件,能夠爲整個公司帶來開發成本的下降;像react、antd這樣的優秀開源組件,則會爲整個社區和行業帶來重大的價值。

組件是否能夠放心使用,一個最簡單直接的評判標準就是其質量的好壞。質量的好壞,除了上手試用之外,通常會經過幾個方面來造成判斷:

  1. 是否有高覆蓋率的單元測試用例?
  2. 源碼是否有規範的編碼風格和語法檢查?
  3. 源碼是否使用了類型系統?

這些都直接決定了開發者對這個組件的評價。試想一下,若是開發了一個公共組件,沒有規範的開發流程和編碼風格檢查,也沒有單元測試,隨手就發佈了帶 Bug 的版本。此時用戶第一次安裝使用時就報錯,這會讓開發者對組件產生強烈的不信任感,甚至這種不信任感會波及到做者自己。

所以,一個規範且合格的組件,至少要在保障組件的質量上作兩件事情:1)引入 JavaScript 代碼檢查工具來規範代碼風格和下降出錯機率;2)引入單元測試框架,對組件進行必要的單元測試。此外,類型系統(TypeScript)的加入,會幫助組件提升代碼質量和可維護性,是組件開發時的推薦選擇。

2.2.1 JavaScript 檢查工具

JavaScript 語言第一個檢查工具是由前端大神 Douglas Crockford 在 2002 年發佈的 JSLint,在後續前端行業高速發展的十幾年間逐漸演變出了 JSHint 和 ESLint 兩個檢查工具。關於這三個工具的演變歷史,能夠參考尚春同窗在知乎發表的一篇文章:《JS Linter 進化史》。本文再也不贅述,咱們能夠經過 Google trends 來簡單瞭解一下這三個工具的熱度,這裏還加上了一個 JSCS 的對比:

開發一個高質量的前端組件,這些姿式必定要知道

能夠看到,在過去一年內,全球範圍內用戶在 Google 搜索這些關鍵詞的熱度狀況,這個圖和身處在前端行業的感覺是一致的。所以在 JavaScript 檢查工具的選擇上,能夠絕不猶豫地選擇 ESLint。

實際使用 ESLint 時有幾點須要考慮:

  1. 不管團隊仍是我的,都須要就配置規範達成認知和共識,以即可以將配置沉澱下來,做爲通用的腳手架和規範。
  2. 對於不一樣的組件類型,譬如 react 或者 vue,各有本身獨特的語法,須要特定的 ESLint 插件才能夠支持,而和框架無關的組件,譬如lodash,則不須要這些插件。所以如何對配置進行分類和抽象,以便沉澱多套配置規範,而沒必要每次開發組件都須要從新對配置進行調整和修正。一個比較常規的作法是把組件按照應用的端(瀏覽器、Node、通用、Electron、...)和運行時依賴的框架(React、VUE、Angular 等)來進行配置的組合。
  3. 藉助 IDE 的插件來實現自動修復以便提升效率。
  4. 若是是團隊共同的規範,還須要造成一套規範變動的流程,以便組員對規範有爭議時,能夠有固定的渠道去討論、決議並最終落實到規範中。
  5. 引入了 ESLint,還須要考慮是否將 ESLint 加入到驗收流程中,以及如何加入驗收流程。

2.2.2 單元測試和覆蓋率

一直以來對於業務類的項目要不要寫單測這個問題,我的的選擇是能夠不寫。互聯網倡導敏捷開發,快速迭代上線試錯,需求變化太快,而爲前端代碼寫單測自己的成本可能並不亞於代碼自己。

可是組件的狀況就徹底不一樣了,組件是一組邊界清晰、效果可預期的接口和能力的集合。並且和業務類代碼相比,組件更具有通用性,也就是說不太會隨着業務的變動而變動。而且組件的升級一般會對依賴組件的系統形成潛在影響,每個版本的發佈都理應對功能進行詳盡的迴歸測試,以保障發佈版本的質量。因爲組件的測試一般依靠開發者本身保障,不會有專業的 QA 資源配備,所以單元測試就是最好的解決方案了。

JavaScript 的單元測試解決方案很是之多,呈百花齊放、百家爭鳴的態勢,耳熟能詳的譬如:Jasmine、Mocha、Jest、AVA、Tape等,每個測試框架都有其獨特的設計,有些是開箱即用的全套解決方案,有些自身很簡約,還須要配合其餘庫一塊兒使用。

事實上,這些框架並沒有絕對的好壞,如何選擇徹底取決於我的和團隊的喜愛。這有一篇測試框架評測的文章,不妨一讀:《JavaScript unit testing frameworks: Comparing Jasmine, Mocha, AVA, Tape and Jest [2018]》。

另外,咱們依然能夠經過 GitHub 上的 star 數和 Google trends 上的搜索量來略窺流行趨勢一二。

開發一個高質量的前端組件,這些姿式必定要知道

Google trends 在中國的數據:

開發一個高質量的前端組件,這些姿式必定要知道

Google trends 在美國的數據:

開發一個高質量的前端組件,這些姿式必定要知道

能夠看出 Jest 從 2014 年發佈以來,增加勢頭是最猛的,並在短短 3 年內超過了其餘老牌對手,成爲目前最煊赫一時的 Test Framwork。

除了測試框架選型之外,還有一個比較重要的指標要關注,就是測試覆蓋率。推薦使用 nyc, 不少同窗可能還用過一個名字比較特殊的庫:istanbul。這兩個庫以前的淵源能夠看下面這個 Issue 瞭解一下。

https://github.com/istanbuljs...

2.2.3 類型系統

現在的 JavaScript 已經不是原來那個在瀏覽器寫寫動效和交互的愣頭小子了,它已經在 Web、Server、Desktop、App、IoT 等衆多場景中證實了本身的價值,證實了本身能夠被用來解決複雜的問題。事實上,JavaScript 正是經過將衆多優秀的高質量組件、框架進行有機組合來提供這種能力的。

可是值得深思的是,JavaScript 採用了動態弱類型的設計,過於靈活的類型轉換每每會帶來一些很差的事情。試想這樣的場景:

  1. 調用一個組件的 API 函數,卻不清楚這個函數的參數類型,只能本身去擼代碼;
  2. 對一個組件重要函數的參數作了優化重構,卻沒法評估影響面。

這些問題在強類型語言中有很好的解決方案,不少可能的錯誤會在編譯期就被發現,不少改動的影響也會第一時間就被 IDE 告警。

事實上,愈來愈多的知名組件庫已經開始引入強類型系統來輔助提升代碼的質量和可維護性,好比 Vue.js、Angular、Yarn、Jest 等等。若是你想讓本身具有類型思惟,讓組件具有更好的質量和可維護性,能夠考慮把類型系統加到組件的腳手架中去。

目前可選的爲 JavaScript 增長強類型檢查的解決方案有 FaceBook 的 Flow 和 Microsoft 的 TypeScript,從當下的流行趨勢來看,TypeScript 是絕對的首選。

2.3 可用性

組件的可用性,主要指的是從組件的使用者角度來看待組件的使用體驗:

  • 組件的文檔是否完善且易於閱讀?
  • 組件暴露的 API 是否有詳細且規範的輸入輸出描述?
  • 是否有能夠直接運行或者借鑑的 Demo?
  • 文檔是否有考慮國際化?

2.3.1 文檔

一個好的組件文檔至少應該具有如下內容結構:

一句話描述組件是什麼,解決什麼問題

# Usage
// 如何安裝和使用,提供簡單而且一目瞭然的示例

# API 文檔
// 提供規範且詳細的 API 接口文檔,包括示例代碼或者示例連接

# 補充信息,譬如兼容性描述等
// 若是是瀏覽器端組件,最好補充一下兼容性的支持狀況;若是是 Node 端組件,也須要描述一下支持的 Node.js 版本範圍

# ChangeLog
// 描述各個版本的重要變動內容以及 commit 連接

# 貢獻、聯繫做者、License 等
// 若是組件但願他人一塊兒參與貢獻,須要有一個參與貢獻的指南;除此以外,最好再提供一個能夠直接聯繫上做者的方式

不少優秀的開發者能夠很好地駕馭代碼,但對如何寫好一份組件文檔卻有些苦惱,這是由於代碼是給本身看的,文檔是給用戶看的,這兩種思惟方式之間存在自然的差別。寫文檔時,須要換位思考,甚至能夠把用戶當小白,儘量爲小白考慮的多一些,如此能夠提升文檔的可讀性,下降上手難度和使用的挫敗感。

2.3.2 DEMO

對一個組件而言,Demo 的重要性不言而喻,還記得 Node.js 那個經典的幾行代碼建立一個 http server 的招牌式 demo 嗎?能夠說它幾乎成爲了 Node.js 的招牌和廣告。

組件的 Demo 和文檔都是爲了可用性負責,但應該互有側重,相得益彰。文檔側重於介紹關鍵信息、Demo 側重於交付具體應用場景中的用法。

對於比較小的組件,這二者能夠合二爲一;對於 demo 代碼量較多,且有多種使用方式和場景的狀況,建議在 examples 目錄下爲每一種場景寫一個能夠直接運行的 Demo。

結語

組件是開發者創造的產品,在這個產品的生命週期中,第一次發佈只是一個開始而已。如何讓更多用戶關注到,而且成爲它的忠實用戶,乃至參與貢獻纔是接下來要重點解決的問題。關於這個話題,本文就點到爲止了,歡迎你們在下面留言分享本身在組件推廣方面的經驗和技巧。

因本人能力的侷限性,文中不免有解讀不正確之處,盼望你們能夠交流指正!

想了解更多技術知識歡迎評論區留言或私信我!

歡迎關注公衆號:fkdcxy 瘋狂的程序員丶發現更多技術知識!
相關文章
相關標籤/搜索