一步一步教你如何发布一个vue组件到NPM

Vue.js已经火了好久了,组件化代码的思想已经深入人心。自己写了一个自我感觉超牛逼的组件,好想分享给别人,这时候怎么做呢?这篇文章就教你如何一步步把你的组件发到npm上。

下面我将通过一个例子来演示整个流程:

  • 确认包里没有包含其他依赖
  • 通过webpack分别构建出browser和node场景的版本
  • package.json配置
  • 发布到NPM

Example: Vue Countup 数字滚动

这是一个数字滚动的组件,我们将通过它来演示整个过程。


vue-countup-v2.gif

下面是数字滚动组件的代码
Countup.vue

<template>
  <span :id="targetId"></span>
</template>

<script>
import CountUp from 'countup.js'

export default {
  data () {
    return {
      targetId: '',
      numAim: null
    }
  },

  props: {
    endVal: Number,
    decimals: {
      type: Number,
      default: 0
    },
    duration: {
      type: Number,
      default: 4
    }
  },

  watch: {
    endVal: function (val) {
      this.numAim.update(val)
    }
  },

  created () {
    this.targetId = 'countup_' + Math.random().toString(36).substr(2, 9)
  },

  mounted () {
    this.numAim = new CountUp(this.targetId, 0, this.endVal, this.decimals, this.duration / 2)
    this.numAim.start()
  }

}
</script>

构建工具:Webpack

Webpack神器我就不介绍了,相信你们有用Vue的应该都知道。我们的大部分工作都是通过配置webpack.config.js来完成的。我这里用的是webpack4,关于webpack4的改动,可以翻看我上一篇文章《Webpack 4.0发布了!!》

webpack.config.js

const webpack = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');
const path = require('path');

module.exports = {
  entry: path.resolve(__dirname + '/src/CountUp.vue'),
  output: {
    path: path.resolve(__dirname + '/dist/'),
    filename: 'vue-countup.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: __dirname,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader'
        }
      },
      {
        test: /\.css$/,
        use: {
          loader: 'css-loader'
        }
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

Externals

组件中我们引入了countup.js,但我不希望我的组件包含这个依赖,因为这会导致我这个组件体积变大,而且可能会跟用户的环境造成版本冲突。这时候externals就派上用场了,官网文档解释的很清楚,就是webpack可以不处理应用的某些依赖库,使用externals配置后,依旧可以在代码中通过CMD、AMD或者window/global全局的方式访问。
webpack.config.js

module.exports = {
  ...
  externals: {
    countup: 'countup'
  },
  ...
}

构建环境

在Vue.js中,有两种引入组件的方式,一种是浏览器引入:

<script type="text/javascript" src="vue-countup.js"></script>

另一种是通过Node模块引入:

import VueCountup from 'vue-countup';

这两种引入方式意味着我们要构建出两份代码,我们只好通过两套配置来实现。使用两个conf文件不好,我们可以通过webpack-merge来解决!
webpack.config.js

const webpack = require('webpack');
const merge = require('webpack-merge');
const path = require('path');

var commonConfig = {
  output: {
    path: path.resolve(__dirname + '/dist/'),
  },
  module: {
    rules: [ ... ]
  },
  plugins: [ ... ]
};

module.exports = [
  // For browser env
  merge(commonConfig, {
  
  }),
  // For Node-based development env
  merge(commonConfig, {
  
  }),
]

注意我在commonConfig里把entryoutput.filename移除了。

浏览器环境

大部分浏览器暂时还不能跟Node一样引入模块,它们可以通过AMD等装载器来引入,不过为了最简单化,我将我的组件作为全局变量添加到window里。

Vue插件的使用方式主要是通过Vue.use(),我也希望我们的组件以这种方式来调用:

// 实际上就是调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

那么最终我们的调用方式就是这样:

<body>
  <div id="app">
    <vue-clock></vue-clock>
  </div>
  <script type="text/javascript" src="vue-countup.js"></script>
  <script type="text/javascript">
    Vue.use(VueCountup);
    new Vue({
      el: '#app',
    })
  </script>
</body>

插件化

plugin.js

import VueCountup from './VueCountup.vue';

module.exports = {
  install: function (Vue) {
    Vue.component('vue-countup', VueCountup);
  }
};

Webpack 配置

webpack支持多种导出方式,可通过output.libraryTarget来设置,默认是var,会将值作为变量声明导出,这里我们用window,你也可以使用umd这种通用性高的,不过鉴于我们已经分开成两份配置了,这里只需要考虑浏览器端的。

output.library的值取决于libraryTarget的配置,这里会将我们的模块赋值到window.VueCountup

module.exports = [
  merge(commonConfig, {
    entry: path.resolve(__dirname + '/src/plugin.js'),
    output: {
      filename: 'vue-countup.min.js',
      libraryTarget: 'window',
      library: 'VueCountup',
    }
  });
]

Node-based 开发环境

使用UMD的方式导出,可让我们的组件支持以多种方式加载。

module.exports = [
  // For Node-based development environments
  merge(commonConfig, {
    entry: path.resolve(__dirname + '/src/VueCountup.vue'),
    output: {
      filename: 'vue-countup.js',
      libraryTarget: 'umd',
      // if the user wants to load the module with AMD
      library: 'vue-countup',
      umdNamedDefine: true
    },
  })
]

注意我们entry是直接引入组件的,不是plugin.js了,这样我们可以这样引用组件:

import VueCountup from 'vue-countup';

new Vue({
  components: {
    VueCountup
  }
})

package.json

至此我们大部分工作已经完成,但要发布到NPM上还需要修改一下 package.json
package.json

{
  "name": "vue-countup",
  "version": "1.0.0",
  "description": "A Vue.js component that countup number with animation",
  "main": "dist/vue-countup.js",
  "scripts": {
    "build": "rimraf ./dist && webpack --mode production"
  },
  "author": "Tofuxb",
  "license": "ISC",
  "dependencies": {
    "countup.js": "^1.9.3"
  },
  "devDependencies": { ... }
}

注意:

  1. "main": "dist/vue-countup.js" 可告知模块加载时所映射的文件。
  2. Dependencies. 我们的组件里没有把countup.js构建进来,但是要写到这里。

最后一步:发布到NPM

具体我不在这里演示了,参考官方教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容