Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📦vue组件发布npm最佳实践 #2

Open
levy9527 opened this issue Apr 14, 2019 · 0 comments
Open

📦vue组件发布npm最佳实践 #2

levy9527 opened this issue Apr 14, 2019 · 0 comments

Comments

@levy9527
Copy link
Owner

levy9527 commented Apr 14, 2019

前言

我们经常使用组件,二次封装或开发新组件,在团队内部使用; 可当我们想通过npm分享组件时,却没了之前的得心应手,本文旨在帮助大家在可以更轻松地发布组件

首先,把vue组件发布到npm这件事可以拆分成两个部分:

  1. 在npm上发布一个包
  2. 将vue组件打包

npm发包

有人说,发包不是一行命令就搞定了么

npm publish

是的,可是还忽略了以下几点:

  1. 首先你要在npmjs上注册一个账号
  2. 查看你的.npmrc设置,确保你的registry是https://www.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 my-awesome-package@1.0.0时,其实是相当于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开源:https://github.com/FEMessage/vue-sfc-cli

说明

我们以开源组件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 元素名称都是单个单词的

我们选择使用kebab-case

好例子:

components/
|- my-component.vue

好例子:

<!-- template -->
<my-component></my-component>
  • [JS/JSX 中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用Vue.component 进行全局组件注册时,可以使用 kebab-case 字符串](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

本文最早发布在掘金,现迁移至github。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant