自定义Vue前端组件发包到npm

583 阅读3分钟

一、前言

最近在学习 element-ui,试着搭建自己的前端组件库(基于Vue),并发布到 npm。

二、搭建目录结构

  1. 创建目录 lushuixi-ui 并打开目录

  2. npm init ,生成一个package.json文件

  3. 搭建目录结构

  • packages 组件包目录
  • src 源码入口目录
  • scripts 脚本执行目录
  • README.md
  • package.json
  1. 在packages目录创建一个button组件
  • 创建button目录
  • 创建button/src/button.js
  • 创建button/index.js
// version 1.0.0  
// button/index.js
import Button from './src/button';

/* istanbul ignore next */
// 在Vue.use(Button) 时调用组件上的intall
Button.install = function (Vue) {
    // 全局注册组件
    // react与vue不同的是, vue组件必须注册(或全局注册,或局部注册)
    Vue.component(Button.name, Button);

};
export default Button;

注意:

  • 这里因为vue注册组件的时候,定义组件名有两种方式【kebab-case(短横线分隔命名) | PascalCase(首字母大写命名)】
  • 注册组件时,当使用 kebab-case 定义一个组件时,引用该组件时也必须使用 kebab-case
  • 注册组件时,当使用 PascalCase 定义一个组件时,引用该组件时即可使用 PascalCase 也可使用 kebab-case(尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的)
  • 在使用 element-ui 插件时,我们引用组件是通过 ... 形式,便是因为该组件在注册时,使用的是 ElButton 定义的
  • 所以,这里修改如下
// version 1.0.1 
// button/index.js
import LsxButton from './src/button';

/* istanbul ignore next */
// 在Vue.use(LsxButton) 时调用组件上的intall
LsxButton.install = function (Vue) {
    // 全局注册组件
    // react与vue不同的是, vue组件必须注册(或全局注册,或局部注册)
    // 这里LsxButton.name值是LsxButton
    // ./src/button.vue中的scripts标签中export default {name: LsxButton}
    Vue.component(LsxButton.name, Button);
};

export default LsxButton;
  1. 入口文件src/index.js
// version 1.0.0
import Button from '../packages/button';
const components = [
    Button,
]

// 全局注册组件
// 在Vue.use(插件)时调用 插件.install
const install = function (Vue, opts = {}) {
    components.forEach(component => {
        Vue.use(component.name, component);
    });
}

export default {
    install,
    Button,
}

这里需要注意一下

  • Vue组件使用之前必须导入且注册,在编写Vue插件时使用全局注册组件(Vue.use(component.name, component))
  • 该插件中的组件导入有两种方式,按需导入(Vue.use(Button),会调用Button.install方法以注册组件)和全局导入(Vue.use(插件名),会调用该插件的install以注册所有子组件)

纠正:

// version 1.0.2
import Button from '../packages/button';

const components = [
    Button,
]

// 全局注册组件
// 在Vue.use(插件)时调用 插件.install
const install = function (Vue, opts = {}) {
    components.forEach(component => {
        Vue.component(component.name, component);
    });
}

export default {
    install,
    Button,
}

三、发包到 npm

  1. 如果没有 npm 账号,先到 npm 上注册一个

  2. 如果是第一次发布,则 npm addUser输入用户名、密码和邮箱账号

  3. 如果非第一次发布,则 npm login

  4. 接下来,npm publish发包,如果package.json注明了"private": true,则不允许发到npm上的,需要将该字段去掉或者改为false

  5. 进入npm官网的packages可以查看发的包

注意:重复发包,如果版本号一致,则会报错的。所以,每次发包需更新版本号

四、使用包

  1. vue create hello-wrold, 搭建vue项目,选择vue版本为2.x,并安装依赖

  2. 使用 npm i 插件名,下载包

  3. src/main.js 导入插件

import Vue from 'vue'
import App from './App.vue'
import LushuixiUi from 'lushuixi-ui';

// 全局导入组件
Vue.use(WeizhiUi)
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

注意:两种组件导入方式

  • 全局导入
  • 按需导入
  1. app.vue 中全局导入插件
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    <lsx-button></lsx-button>
  </div>
</template>

注意:

  • 有如下报错,说是该组件没有被注册或者注册失败
  • 这里我使用的是全局导入插件中的所有组件,所以便去查看src/index.js中的install方法,发现组件注册那块的代码有误,应该使用的是Vue.component(组件名,组件本身)而不是Vue.use(组件名,组件本身),写错了,天呐!修改版本为1.0.2
  1. 这里使用的是全局导入插件中的全部,再使用按需导入插件中的组件
import Vue from 'vue'
import App from './App.vue'
import LushuixiUi from 'lushuixi-ui';

Vue.config.productionTip = false
// Vue.use(LushuixiUi)
Vue.use(LushuixiUi.Button)

new Vue({
  render: h => h(App),
}).$mount('#app')

这里有个问题,入口文件 src/index.js 中是通过 export default 暴漏的,暴漏出的只是一个对象,无法通过 import {} from '' 的方式导入,那么 element-ui 为什么可以通过这种方式按需导入(直接通过 import {Button} from 'lushuixi-ui'; Vue.use(Button)这种形式)?

百度了下,发现在使用 element-ui 插件时,实际是调用了 lib 目录下相应的 js 文件,而该目录按 babel-plugin-component 插件要求打包后存放最终代码的目录(lib中是最终打包的目标目录),将webpack配置成多入口,保证最终打包的目录结构符合 babel-plugin-component插件的要求,实现按需加载(该问题参考: segmentfault.com/a/119000001…

五、总结

(1)创建本地组件库; (2)发布到 npm; (3)在项目中引用该包。