Vue.js已经火了好久了,组件化代码的思想已经深入人心。自己写了一个自我感觉超牛逼的组件,好想分享给别人,这时候怎么做呢?这篇文章就教你如何一步步把你的组件发到npm上。
下面我将通过一个例子来演示整个流程:
- 确认包里没有包含其他依赖
- 通过webpack分别构建出browser和node场景的版本
- package.json配置
- 发布到NPM
Example: Vue Countup 数字滚动
这是一个数字滚动的组件,我们将通过它来演示整个过程。
下面是数字滚动组件的代码
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里把entry
和output.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": { ... }
}
注意:
-
"main": "dist/vue-countup.js"
可告知模块加载时所映射的文件。 - Dependencies. 我们的组件里没有把
countup.js
构建进来,但是要写到这里。
最后一步:发布到NPM
具体我不在这里演示了,参考官方教程