我們經常使用組件,二次封裝或創造組件,在團隊內部使用; 可當我們想通過npm分享組件時,卻沒了之前的得心應手,本文旨在幫助大家在可以更輕鬆地發布組件

首先,把vue組件發布到npm這件事可以拆分成兩個部分:

  1. 在npm上發布一個包
  2. 將vue組件打包

npm發包

有人說,發包不是一行命令就搞定了麼

npm publish

是的,可是還忽略了以下幾點:

  1. 首先你要在npmjs上註冊一個賬號
  2. 查看你的.npmrc設置,確保你的registry是npmjs.com/, 而不是淘寶源
  3. 在終端npm login,登錄你的賬號

做好以上三點,纔可以通過npm publish簡單地發布一個包。若要遵循最佳實踐,還有一些準備工作要做好,下面將為你講述

完善基本信息

package.json的以下欄位需要注意

  • name
  • version
  • description
  • keywords
  • author
  • license
  • repository
  • main
  • unpkg
  • module
  • scripts
  • engines

name就是發布到npm上的包名,也即別人安裝時輸入的名字yarn add ${name}, 包名應該是kebab-case, 即英文單詞全小寫,中劃線分割(lower case and dash-separated)

version是語義化的,major.minor.patch. 如果是major變動,通常意味著不兼容的修改; 如果是minor,意味著添加向後兼容的新功能,如果是patch, 意味著bug的修復。詳情見semver.org

description是對包的描述,在npmjs.com上搜索時會顯示,有助於用戶在搜索時進行篩選

keywords 同樣也是幫助用戶查找到你的包

author的格式一般是${your name} ${email}, 當然也可以是一個github地址

license可能很多人會忽略,最好也寫上去。至於用哪個,vue的官方項目全是MIT,因此我也是MIT,不糾結

repository的格式參考如下:

"repository": {
"type": "git",
"url": "https://github.com/FEMessage/el-data-table.git"
}

這樣在npm包頁面就有會個github的入口

main定義了包的入口文件,在NodeJs環境,語句import pkg from package-name時,其實導入的就是main定義的文件,它可以是CommonJs格式的, 也可以是umd格式

需要注意的是,當你把一個包發布到npm上時,它同時也可以在unpkg上獲取到。也就是說,你的代碼既可能在NodeJs環境也可能瀏覽器環境執行。為此你需要用umd格式打包,並且在package.json定義unpkg欄位,一般而言此時它的命名為name.min.js

最後,別忘了定義modulejsnext:main欄位,它表示用ES6模塊格式打包,給Webpack 2+或Rollup等先進的構建工具來處理。

我們來看一下三個欄位的示例:

"main": "dist/el-data-table.js",
"unpkg": "dist/el-data-table.min.js",
"module": "dist/el-data-table.esm.js"

scripts 為了防止出現發包前忘記構建的烏龍事件,定義一下發布前的腳本, 這樣每次執行npm publish前都會先執行npm run build

"prepublishOnly": "npm run build"

engines 可以告訴用戶運行你的包對NodeJs版本的要求,這是非常重要的,不然你使用了NodeJs新版本特性,卻沒有定義該欄位,導致低版本NodeJs用戶運行報錯,讓人摸不著頭腦。

定義依賴

當你開發一個項目時,比如一個靜態網站或一個單面應用,dependenciesdevDependencies並沒有太多區別,因為你npm installyarn時,這些依賴都會下載下來,因為你是在開發。

但對於發布到npm的包則不同:

dependencies 是運行你的包必須安裝的依賴,即當用戶yarn add my-awesome-package時,這些依賴也會下載。

devDependencies 是開發你的包時需要安裝的依賴,比如eslint, jest等開發工具,當用戶yarn add my-awesome-package 時,這些依賴並不會下載!

peerDependencies 一般用於開發插件的場景,它要求用戶必須預先安裝了某些依賴。比如開發webpack的插件,如果你把對webpack的依賴定義成dependencies, 如果用戶安裝的webpackdependencies裏的minor版本不一致, 則用戶yarn add my-webpack-plugin時會把dependencies定義的webpack也下載下來,也即用戶會安裝兩個major版本相同的webpack, 這就不合適了。

所以說,定義好你的包的依賴,可以讓用戶安裝使用你的包時少點困惑,多些愉悅。

忽略文件

如果有 .gitignore文件,則發布時會忽略 .gitignore中定義的文件; 也即這些文件不需要在.npmignore重新定義。如果用.gitignore忽略了dist目錄,但發包時又需要發布dist目錄,此時可以在package.json定義files欄位,這是一個白名單,裡面的文件都會被發布出去

"files": [
"dist"
]

需要注意的是,子文件夾.gitignore.npmignore同樣有效,而它們會覆蓋files欄位

另外,有些文件無論如何設置,都不會發布出去:

  • node_modules
  • .git(包括.gitignore)

README.md

別忘了這個文件,寫下與包相關的更具體的信息,告訴用戶這個包有哪些功能,如何使用。這很重要,用戶不會使用一個沒有文檔說明的包!

發布

一個版本只能發布一次,為了避免每次手動修改package.json, 可以使用npm version [major | minor | patch]命令來更新package.json裏的版本

打標籤

假設你的包最新版本是1.0.0, 當用戶yarn add my-awesome-packageyarn add [email protected]時,其實是相當於yarn add my-awesome-package@latest, 即不指定標籤安裝時,默認安裝latest版本。

假設你正在開發2.0.0版本,它還不穩定,你想發布它讓用戶測試一波,此時又不能讓它變成latest版本,不然用戶yarn add my-awesome-package時就安裝了2.0.0了,那將讓用戶崩潰。這時該怎麼辦呢?標籤就用上場了。

可以這樣發布

npm publish --tag beta

則用戶yarn add my-awesome-package安裝的是1.0.0版本, yarn add my-awesome-package@beta時,安裝的是2.0.0版本,不影響老用戶,棒!??

記住,你只能對一個版本打一個標籤,使用npm dist-tag ls 可以查看npm包一共打了幾個標籤

打包Vue

腳手架

經過一番折騰,在Vue Conf上找到一個vue組件的打包腳手架(vue官方文檔也有說明),進行「本土化」修改完善後,已在github開源:github.com/FEMessage/vu

說明

我們以開源組件el-data-table為例,解釋目錄結構及文件

├── README.md
├── build
│ └── rollup.config.js
├── dist
│ ├── el-data-table.esm.js
│ ├── el-data-table.min.js
│ └── el-data-table.umd.js
├── docs
│ ├── build
│ └── index.html
├── package.json
├── src
│ ├── el-data-table.vue
│ └── index.js
├── styleguide.config.js
├── test
│ └── index.test.js
└── yarn.lock

先來看三個文件:

  • README.md
  • package.json
  • yarn.lock

README.mdpackage.json大家都懂,有yarn.lock因為是我們鼓勵大家使用yarn, 它比npm更快。雖然npm 6.0號稱提速17倍(可以想像6之前是得有多慢??),但經測試,還是不如yarn

接下來看build, dist, src 目錄

├── build
│ └── rollup.config.js
├── dist
│ ├── el-data-table.esm.js
│ ├── el-data-table.min.js
│ └── el-data-table.umd.js
├── src
│ ├── el-data-table.vue
│ └── index.js

build 目錄下放編譯時的配置文件,這個跟vue-cli 2.x生成的模板build目錄作用是一樣的,只不過這裡放置的是rollup.config.js。至於為什麼用Rollup, 一是因為配置更簡單,二是因為它更適合打包類庫,當源文件中有import lib from awesome-lib類似的代碼時,Rollup並不會把awesome-lib捆綁輸出,這正是開發類庫或組件需要的特性

dist是輸出目錄,也有叫lib的,我也糾結了好久。看了一些優秀的開源項目,發現叫dist的比較多,而webpack4默認的輸出目錄也是dist, 因此決定用dist。至於dist目錄下會有三個文件,前文已說過原因。而命名為何不是camelcase, 而是kebab-case, 後面風格指南會說到

src是輸入目錄。把index.js放在src目錄,也是經過一番考慮。也想把index.jspackage.json同級,最終參考了webpack4, 它默認輸入是src/index.js, 那就跟主流保持一致。該文件主要工作是把src目錄下的vue文件設置成vue的插件。同樣,vue文件的命名後面風格指南會說到

├── test
│ └── index.test.js

test目錄下是基於jestvue/test-utils的單元測試文件,具體教程可參考官方文檔

├── docs
│ ├── build
│ └── index.html
├── styleguide.config.js

docs存放的是組件的api文檔,包含props, slot, event等內容的說明,使用的是vue-styleguidist作為vue組件文檔生成工具。為啥叫 docs呢,因為Github Pages支持從master分支的docs目錄讀取文件,在倉庫Settings裏選擇Github PagesSource即可, 具體看官方文檔

風格指南

vue組件把template/script/style都放在一個vue文件裏,這個稱之為單文件組件,Single File Component,縮寫為SFC, 這就是vue-sfc-cli中sfc的寓意

通讀vue官方風格指南, 由於我們是kebab-case的重度用戶,因此我們更看重的是在多個項目中保持相同的大小寫規則,以下是摘取的適用於我們團隊協作習慣的指南:

  • 組件名,應該多個單詞

這樣做可以避免跟現有的以及未來的 HTML 元素相衝突,因為所有的 HTML 元素名稱都是單個單詞的

  • 單文件組件的文件名應該要麼始終是單詞大寫開頭 (PascalCase),要麼始終是橫線連接 (kebab-case)

我們選擇使用kebab-case

好例子:

components/
|- my-component.vue

  • 在 DOM 模板(template)中總是 kebab-case 的

好例子:

<!-- template -->
<my-component></my-component>

  • JS/JSX 中的組件名應該始終是 PascalCase 的,儘管在較為簡單的應用中只使用Vue.component 進行全局組件註冊時,可以使用 kebab-case 字元串

這裡我們選擇使用PascalCase, 因為在編程語言裏,kebab-case並不是最佳實踐。 也即,在非編程語言的範圍,我們能用kebab-case就用

好例子:

Vue.component(MyComponent, {
// ...
})

import MyComponent from ./my-component.vue

export default {
name: MyComponent,
// ...
}

綜上所述,就可以明白前文中el-data-table的文件命名風格為kebab-case的原因了

參考

  1. How to publish your package on npm
  2. module-best-practices
  3. npm-style-guide
  4. semver.org
  5. unpkg
  6. umd
  7. npm scripts
  8. .npmignore
  9. 2018 Vue Conf
  10. Vue Cookbook
  11. Vue Style Guide
  12. npm-vs-yarn
  13. Github Pages

推薦閱讀:

相關文章