使用npm发布一个vue组件

9,004 阅读10分钟

前言

这是一篇包教不包会的npm发布教程,为了避免很多文章中的分享,跟着一步步做,最后发包失败。。时间没了,搞得心情都不好了。

因此,在写这篇文章的同时[2019-08-18]我重新建立一个npm包,按照以下的流程,发布了一个npm包,也测试可用,所以童鞋们可以放心跟着一步步走,学会发布npm包。发包的项目代码在这里已经发布上线的npm包地址在这里

​ 相信前端er对npm包不会陌生,npm 本来是 Node.js 的包管理工具,但随着 JS 这几年的蓬勃发展,现在的 npm 已经成了几乎所有跟 JS 相关的工具和软件包的管理工具了,这次也会从0到1的走一遍发包的流程,

1、本文是以vue项目为例子进行讲解,流程可通用,只是具体项目的安装有些许区别,不管vue还是react,相信对大家都有帮助

2、⚠️提示: 为了让童鞋们理解发包的流程和配置,所以还是建议按照步骤(1)-(10)的顺序来操作,为了突出整个发包流程,所以封装的测试组件非常简单。待理解所有文件的内容和具体原理,并成功发出一个测试npm包后,童鞋们可自行更换成自己的组件,发布属于自己的npm包,发布包的名字设置为our-btn

初始化webpack项目 + 组件封装 === 一气呵成

​ 在项目中,我们都希望安装的插件体积尽可能的小,因此,我们在发包的时候也要尽量控制我们的依赖,所以我选择自己创建一个打包项目,而不是用笨重的vue和react脚手架。 话不多说,下面跟着做👇

  • (1)下方是推荐的目录结构,根据以下结构建立项目文件

但是由于本文组件过于简单,所以删减assetsutils文件夹(有没有这两个文件夹不影响npm发包,仅仅方便你自己整理项目代码)

├── src
│ ├── assets # 本地静态资源(本文没有)
│ ├── components # 业务通用组件
│ │ ├── btnDemo.vue # 本文需要封装的简单button组件
│ ├── utils # 工具库(本文没有)
│ └── index.js # 应用入口 
├── .npmignore // 用于忽略不需要上传到npm的文件
├── README.md
├── package-lock.json
├── package.json
└── webpack.config.js # webpack配置

// 注意
# utils 文件夹一般存放的是你组件经常使用的一些工具函数
# assets 文件夹一般存放的是你组件用到的样式、图片、icon等静态资源
  • (2)button组件
// btnDemo.vue
<template>
  <div class="btn">
    <button>{{text}}</button>
  </div>
</template>

<script>
export default {
  name: 'btn', // 组件的name属性(后面有提到这里有个坑)
  props: {
    text: { // 文本
      type: String,
      default () {
        return ''
      }
    }
  },
  data () {
    return {
    }
  }
}
</script>
  • (3)index.js

该文件是为了将我们的组件暴露出去

// src/index.js
// 这里import 的 btn和btnDemo.vue的name属性名相同 !

import btn from './btnDemo.vue'

btn.install = Vue => Vue.component(btn.name, btn) // 给组件配置install方法 

export default btn;

⚠️(这里有个坑):由于Vue.component(btn.name, btn)btn.name是指向btnDemo.vuename属性,因此,建议在这里最好用组件的name属性值作为import别名

  • (4)webpack.config.js

webpack.config.js配置如下,由于我们使用的版本是webpack4,所有的配置都在webpack.config.js里面,在我们这里仅仅需要把我们的资源打包,写的比较简单,具体webpack配置细节可以学习官方文档

这里配置是用于将/src中的内容打包到/dist(打包时会自动生成/dist文件夹)中的btn.jsbtn.js其实就相当于我们的插件(后面在package.jsonmain字段也是指向这里)

// webpack.config.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  entry: {
    index: path.join(__dirname, "/src/index.js") // 入口文件(就是刚才用于暴露组件的index.js)
  }, 
  output: {
    path: path.join( __dirname, "/dist"), // 打包后的文件存放在dist文件夹
    publicPath: '/dist/', // 设置公共路径
    filename: "btn.js", // 打包后输出文件的文件设置为btn.js
    libraryTarget: 'umd' // 这个选项会尝试把库暴露给前使用的模块定义系统,这使其和CommonJS、AMD兼容或者暴露为全局变量
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(scss|sass)$/,
        use: ['style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.(png|jpg|svg|gif)$/,
        use: ['url-loader']
      },
      {
        test: /\.js$/,
        exclude: /node_modules|vue\/dist|vue-router\/|vue-loader\/|vue-hot-reload-api\//,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}
  • (5)package.json

package.json字段如下,要强调的是,字段devDependencies中的依赖项不唯一,你可以根据自己情况去写,但是此时此刻,请务必和我一样,这也是为了保证你可以按照我的操作,成功发包

// 具体字段后面详细说明
{
  "name": "our-btn", // 发布npm包的名字
  "version": "1.0.0", // 你的npm包版本
  "description": "A test button", // 包的描述
  "main": "dist/btn.js", // *重点*:指定你组件的主入口文件
  "scripts": {
    "build": "webpack --mode production",
    "dev": "webpack-dev-server --open --mode development"
  },
  "keywords": [
    "our-btn",
    "button"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/thomaszhou63/test-npm.git"
  },
  "author": "thomaszhou",
  "license": "MIT",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "css-loader": "^1.0.0",
    "file-loader": "^2.0.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "node-sass": "^4.12.0",
    "sass-loader": "^7.2.0",
    "vue": "^2.5.2",
    "vue-hot-reload-api": "^2.2.4",
    "vue-html-loader": "^1.2.4",
    "vue-loader": "^15.4.2",
    "vue-style-loader": "^3.0.3",
    "vue-template-compiler": "^2.5.9",
    "webpack": "^4.19.0",
    "webpack-cli": "^3.1.0"
  }
}
  • 参数说明
    • name - 包名(包名应该是kebab-case, 即英文单词全小写或者中划线分割)
    • version - 包的版本号(第一版建议1.0.0,具体命名规则看这里
    • description - 包的描述。在npmjs.com上搜索时会显示,有助于用户在搜索时进行筛选)
    • keywords - 关键字(在npm网站上搜索你这个npm包的关键词)
    • author - 包的作者。格式一般是name <你的邮箱>,一般来说,用vue/react脚手架创建项目的时候会默认这个格式写法, 当然也可以是一个github地址,也可以自定义,但就不那么标准了
    • license - 版权许可证,MIT还是ISC,或者其他都可以,只是我看很多包都是MIT
    • contributors - 包的其他贡献者(我没有写这个)
    • repository - 包代码的Repo信息,包括typeURLtype可以是gitsvnURL则是包的Repo地址
      • 我这里写的git仓库的地址,放着我的组件项目代码,这样在npm包页面就有会个github的入口
    • main - 该字段指定了程序的主入口文件(最好写dist文件夹内已经压缩后的文件)
      • main定义了包的入口文件,在NodeJs环境中,语句import [pkg] from '[package]'时,其实导入的就是main定义的文件
    • scripts - 指定了运行脚本命令的npm命令行缩写。如 npm startnpm run devnpm run build
    • dependencies / devDependencies - 生产/开发环境依赖包列表。它们将会被安装在 node_module 目录下
      • 对于一个开发者来说,dependenciesdevDependencies其实没太多区别,因为在执行npm install/yarn add时都会全部下载
      • dependencies 是运行你的包必须安装的依赖,即当用户npm install [package]或者yarn add [package]时,这些依赖也会下载
      • devDependencies 是开发你的包时需要安装的依赖,比如eslint, jest等开发工具,当用户npm install [package]yarn add [package] 时,这些依赖并不会下载!

我这里版本是1.0.1,是因为我自己后面迭代了一个版本,如果你是第一版,最好是1.0.0

  • (6)README.md

这是我的README.md写的东西,最终都会显示在npm的内容介绍里面(也就是上图),各位童鞋如果发包,也请写好README.md哈,毕竟是给人使用的嘛

## our-btn
>一个测试的npm包

# install

​```
npm install our-btn
​```
# use
​```
// main.js
import btn from 'our-btn'

Vue.use(btn)
​```
​```
// demo.js
<template>
  <div class="demo">
    <btn :text="msg"></btn>
  </div>
</template>

<script>
export default {
  name: 'demo',
  data () {
    return {
      msg: '成功'
    }
  }
}
</script>
​```
  • (7).npmignore

这个文件同.gitignore的功能一样,.gitignore是忽略自选的文件上传到github仓库中,而.npmignore则是忽略自选的文件上传到npm上。

但是呢,如果你的项目没有.npmignore文件,但有 .gitignore文件,则发布时会忽略 .gitignore中定义的文件;也就是说,.npmignore的优先级是高于.gitignore的。

.*
*.md
node_modules/
webpack.config.js
src/
  • (8)安装依赖

以上文件都配置好后就可以运行下面命令安装package.json的依赖了,然后就会在你们的目录中生成node_modules文件夹、package-lock.json文件

npm install
  • (9)打包我们的组件
npm run build
  • (10) 目录结构

下图就是我的目录结构,其中.gitattributes.gitignore文件你可以忽略,因为我把这个项目放在github仓库,所以才会有这两个文件的。

本地测试组件是否可以用

在项目中执行如下命令

npm run build // 打包
npm pack // 本地生成一个our-btn-1.0.1.tgz的包

你自己在你本地新建一个vue/react项目,因为我是vue组件,所以我就新建一个vue项目,取名为test-btn,我们把our-btn-1.0.1.tgz放进我们的本地项目test-btn

npm install our-btn-1.0.1.tgz
npm run dev // 启动vue项目

然后在项目的main.js引入我们的包

// src/main.js
// 注意:btn要保持和之前咱们封装组件的index.js一样
import btn from 'our-btn' // 引入包

Vue.use(btn)

在我们demo页面中引用这个组件

// demo.vue
<template>
  <div class="demo">
    <btn :text="msg"></btn> 
  </div>
</template>

<script>
export default {
  name: 'demo',
  data () {
    return {
      msg: '成功'
    }
  }
}
</script>

若测试可用,在测试项目test-btn中运行npm uninstall our-btn卸载插件,然后那么就可以发布到npm包上了

发布到npm

  • (1)注册npm账号

发布之前,你得注册一个npm账号吧,不然你怎么上传呢,建议上官网注册一个账号(顺便熟悉那个网站),注册完账号之后,我们就要在本地登录并发布我们的组件了

提示:因为有点童鞋会经常用cnpm源,所以呢,一定要切换到npm源上才可以,不然就会报出如下错误

error: no_perms Private mode enable, only admin can publish this module

因此你要切换到npm源,通过npm config set registry http://registry.npmjs.org命令

  • (2)开始发布

在发包之前,先去npm官网搜索一下有没有和你包名相同的,如果相同就改一个其他的名字吧

npm login // 登陆npm
// 然后输入你的账号、密码、邮箱
// 当你在控制台看到 Logged in as <Username> on https://registry.npmjs.org  说明登陆成功

// 如果你保证是最新版本且已经打包过,则跳过npm run build这一步
npm run build 
npm publish // 发布你的包(如果你出现👇,显示 +[package]@版本信息 ,那就发布成功了)

👇这是我的输入及其对应显示的东西

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉此时此刻你的包已经发布成功!!!🎉🎉🎉🎉🎉🎉🎉🎉🎉

⚠️拓展知识:一个版本只能发布一次,也就是你不能发了1.0.0,下次还继续1.0.0,理论上可以覆盖,但是npm不允许这样,因为你这样覆盖,就无法知道你的版本信息了啊,插件和项目都是迭代的,like git,是有版本信息的。

​ 方法一:每一次npm publish前,自行手动更改package.json的version

​ 方法二:通过以下命令来发布

假设初始版本为1.0.0
➜ npm version preminor (执行这个命令,就会价格当前包发布为版本v0.1.0-0)
v1.0.0-0
➜ npm version prepatch
v1.0.1-0
➜ npm version patch    (执行这个命令,就会价格当前包发布为版本v1.0.1)
v1.0.1

查看npm && 使用我们的包

​ 这个时候,你稍微等几分钟去npm官网搜索一下你的包名字,就可以找到啦。如果搜不到,那就再等等,或许更新慢,没有完全更新,但是你在自己npm账号可以查看到自己发布的包

​ 你自己在你本地新建一个vue/react项目,因为我是vue组件,所以我就新建一个vue项目,具体使用包的方法就是(以我的测试包为例)

npm install our-btn // 安装我们的包

然后在项目的main.js引入我们的包

// src/main.js
// 注意:btn要保持和之前咱们封装组件的index.js一样
import btn from 'our-btn' // 引入包

Vue.use(btn)

在我们demo页面中引用这个组件

// demo.vue
<template>
  <div class="demo">
    <btn :text="msg"></btn> 
  </div>
</template>

<script>
export default {
  name: 'demo',
  data () {
    return {
      msg: '成功'
    }
  }
}
</script>

如果错误请指正,我也好纠错更新

如有讲的不明白的,请留言,我看到会回复

over