在codepen上看到了一个好看的pen,于是把它重写成了一个vue的组件;
为了能在其他地方使用该组件,准备创建一个自己的vue组件库发布到npm上;
因为之前没有发布过,于是就先上网上搜索了一下教程
主要是参考了一下这篇教程还有iview和element这两个组件库的package.json、index.js、webpack文件。
只看创建流程可以直接看第三部分
一、npm publish
-
发布包到npm库的命令是
npm publish
-
npm publish
发布包,需要先配置webpack.json
文件,如果没有webpack.json
文件,可以通过npm init
命令初始化一个 -
package.json
的部分字段简介如下
name:发布的包名,默认是上级文件夹名。不得与现在npm中的包名重复。包名不能有大写字母/空格/下滑线!
version:你这个包的版本,默认是1.0.0。对于npm包的版本号有着一系列的规则,模块的版本号采用X.Y.Z的格式,具体体现为:
1、修复bug,小改动,增加z。
2、增加新特性,可向后兼容,增加y
3、有很大的改动,无法向下兼容,增加x
description:项目简介
mian:入口文件,默认是index.js
scripts:包含各种脚本执行命令
test:测试命令。
author:自己的账号名
license:开源文件协议
private:是否私有
-
如果要发布的话需要把private字段设为false
-
发布的包的资源可以通过
https://unpkg.com/$name@$version/
找到。- 比如我发布了一个包,name是zxz-ui,version是1.0.0,那么我就可以在 https://unpkg.com/zxz-ui@1.0.0/ 找到我发布的包里面的资源
-
我们可以通过创建文件
npmignore
或者pkg.files
来设置上传时过滤某些文件和文件夹,如果我们不设置的话,有些文件和文件夹也是会默认忽略上传的,比如node_modules文件夹、package-lock.json文件等
二、我们如何通过npm引用组件的
先看一下node.js中的模块调用的规则 https://nodejs.org/api/modules.html#modules_accessing_the_main_module
主要注意以下两部分
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with '/'
a. set Y to be the filesystem root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. If "main" is a falsy value, GOTO 2.
c. let M = X + (json main field)
d. LOAD_AS_FILE(M)
e. LOAD_INDEX(M)
f. LOAD_INDEX(X) DEPRECATED
g. THROW "not found"
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = [GLOBAL_FOLDERS]
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS
以iview为例,我们npm i iview
下载iview组件库后,通过import iView from 'iview'
引用它,其实就是通过import iView from './node_modules/iview'
引用它,然后./node_modules/iview
是一个文件夹,会按照LOAD_AS_DIRECTORY(X)
中的规则寻找,先是找到./node_modules/iview/package.json
这个文件,然后找到其中的main
字段,最后通过main
字段找到./node_modules/iview/dist/iview.js
这个文件。
iview.js
这个文件是通过webpack打包得到的,直接看代码基本上是看不懂的,我们看看它是怎么打包出来的。
先找到iview.js的webpack配置文件
https://github.com/iview/iview/blob/2.0/build/webpack.dist.dev.config.js
……
process.env.NODE_ENV = 'production';
module.exports = merge(webpackBaseConfig, {
devtool: 'source-map',
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/dist/',
filename: 'iview.js',
library: 'iview',
libraryTarget: 'umd',
umdNamedDefine: true
},
……
});
上面是在生产环节下打包iview.js的配置,我们找到它的入口文件
https://github.com/iview/iview/blob/2.0/src/index.js
import Affix from './components/affix';
……
const components = {
Affix,
……
};
const iview = {
...components,
iButton: Button,
……
};
const install = function(Vue, opts = {}) {
if (install.installed) return;
……
Object.keys(iview).forEach(key => {
Vue.component(key, iview[key]);
});
……
};
// auto install
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
const API = {
……
install,
……
};
……
module.exports.default = module.exports = API; // eslint-disable-line no-undef
从该文件可以看出来,它先是将components文件夹中的组件都通过import引入,然后定义了一个components对象来存放所有组件,然后又对部分组件增加了第二个key定义了iview对象(例如<Col><Col>
组件,也可以通过<i-col><i-col>
来使用),然后定义了一个install函数来遍历注册组件。最后将install函数作为输出对象的install方法。
然后这个输出对象其实就是上面import iView from 'iview'
得到的iView
对象,因此通过Vue.use(iView)
来调用iView.install
方法就能成功的把iView库的组件注册到我们的Vue对象上。
另外,如果是直接通过CDN引用js文件的话,会触发auto install注释下的内容,会将组件注册到当前window.Vue
上(需要先通过CDN引入vue.js)
三、流程
- 使用vue init webpack-simple xxx初始化项目
- npm install
- 在src目录下新建components文件夹,并将组件放入其中
- 在根目录下新建index.js
import XXX from './src/components/XXX'
import YYY from './src/components/YYY'
import ZZZ from './src/components/ZZZ'
……
const components = [
XXX,YYY,ZZZ,……
];
const install = function (Vue) {
components.forEach(component => {
Vue.component(component.name, component);
});
};
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export default {
install,
XXX,
YYY,
ZZZ,
……
};
这里说明一下,我是用组件内部的name属性做的组件标签名
5. 配置webpack.config.js
var path = require('path')
var webpack = require('webpack')
const NODE_ENV = process.env.NODE_ENV
module.exports = {
//入口这里一个是测试组件时npm run dev的入口文件,一个是npm run build的入口文件
entry: NODE_ENV === 'development' ? './src/main.js' : './index.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/', //输出的文件夹
filename: 'filename.js',//输出的文件名
library: 'objectname', //引用js文件时创建的全局变量名
libraryTarget: 'umd',
umdNamedDefine: true
},
……
}
……
- 配置
package.json
{
"name": "package-name",
"description": "描述",
"version": "1.0.0",
"author": "XXXXXXXXXXX",
"license": "MIT",
"private": false,
"main": "dist/filename.js",
……
}
- npm run build
- npm publish
- 使用
//npm导入
npm i package-name
import xxx from 'package-name'
Vue.use(xxx)
//cdn导入
<script src="//vuejs.org/js/vue.min.js"></script>
<script src="//https://unpkg.com/package-name@1.0.0/dist/filename.js"></script>
- 如果想把css单独抽离出来还需要更新webpack、webpack-cli,和安装optimize-css-assets-webpack-plugin、uglifyjs-webpack-plugin等包。
- webpack配置:https://github.com/pma934/zxz-ui/blob/master/webpack.config.js