vue实现自己的ui组件并发布到npm

722 阅读4分钟

很多时候我们不需要自己造轮子,但是也可能有某些特殊情况,我们希望有自定义的ui组件。这里我用vue做一个基础的button组件并发布到npm,接着尝试使用这个ui组件。

需要前置

  • vue-cl3
  • npm账号密码
npm install -g @vue/cli
# OR
yarn global add @vue/cli

创建项目

执行vue curete bkyz-ui
手动选择功能模块Manually select features

 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 ( ) Router
 ( ) Vuex
 (*) CSS Pre-processors
 (*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing

按空格是选择,回车就进入下一步了。

剩下的选择项我的选择是:

代码风格--》 ESLint + Standard config
格式检测--》 Lint on Save
配置文件生成方式--》 In package.json
是否保存预配置--》否

配置看个人喜好,然后按回车,生成一个完整的项目。

在根目录中创建一个packages目录用来存放我们要开发的UI组件;在根目录创建一个local目录,用于测试引用我们自己开发的UI组件的效果。 由于我们更改了原项目的目录结构,使得系统本地运行以及打包找不到对应的目录,我们需要在项目的根目录中创建一个vue.config.js文件夹手动的去修改webpack配置,使得系统本地运行和打包正常。

// vue.config.js
const path = require('path');
module.exports = {
    pages: {
        index: {
            entry: 'local/main.js',
            template: 'public/index.html',
            filename: 'index.html'
        }
    },
    chainWebpack: config => {
        config.module
        .rule('js')
        .include.add(path.resolve(__dirname, 'packages')).end()
        .use('babel')
        .loader('babel-loader')
        .tap(options => {
            return options;
        })
    }
}

制作组件

在packages文件夹内新建两个文件夹button,fonts和一个index.js文件。 编辑packages/index.js

// packages/index.js
import Button from './button'

import './fonts/font.scss'
// 存储组件列表
const components = [
  Button
]

// 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册
const install = function (Vue) {
  // 遍历注册全局组件
  components.forEach(component => {
    Vue.component(component.name, component)
  })
}

// 判断是否是直接引入文件
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}
export default {
// 导出的对象必须具有 install,才能被 Vue.use() 方法安装
  install,
  Button
}

button文件夹内新建index.js用于导出组件

import Button from './src/button.vue'
// 为组件提供 install 安装方法,供按需引入
Button.install = function (Vue) {
  Vue.component(Button.name, Button)
}
export default Button

编辑packages/button/src/button.vue

<template>
  <button
    class="mc-button">
    <!-- 如果没有传入插槽的时候才显示 -->
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>
export default {
  name: 'McButton',
  props: {}
}

然后新建src文件夹,放button组件,这就是个普通的vue文件,后续会持续加入一些属性来完善这个组件。

button type

常见的类型有:primary / success / warning / danger / info / text

我们采用动态类型绑定的数组形式

<template>
<button 
    class="mc-button" 
    :class="[
      `mc-button--${type}`
    ]">
    ...
</button>
</template>
<script>
export default {
  ...
  props: {
    type: {
      type: String,
      default: 'default'
    }
  }
}
</script>
<style lang="scss'>
.mc-button {
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  background: #fff;
  border: 1px solid #dcdfe6;
  color: #606266;
  -webkit-appearance: none;
  text-align: center;
  box-sizing: border-box;
  outline: none;
  margin: 0;
  transition: 0.1s;
  font-weight: 500;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  padding: 12px 20px;
  font-size: 14px;
  border-radius: 4px;
  &:hover,
  &:focus {
    color: #409eff;
    border-color: #c6e2ff;
    background-color: #ecf5ff;
  }
  &--primary {}
  &--success {}
  &--warning {}
  &--info {}
  &--danger {}
}
</style>

这就实现了不同type的button样式

添加圆角

<button 
    class="mc-button" 
    :class="[
      `mc-button--${type}`,
      {
            ...
            'is-round': round
       }
    ]">
    ...
</button>

再增加一个round入参,此处略过,再增加圆角的样式

&.is-round {
    border-radius: 20px;
    padding: 12px 23px;
 }

类似的可以实现圆形按钮,是否禁用,添加图标,这里就不细写。

<template>
  <button
    class="mc-button"
    :disabled="disabled"
    @click="handleClick"
    :class="[
      `mc-button--${type}`,
      {
        'is-plain': plain,
        'is-round': round,
        'is-circle': circle,
        'is-disabled': disabled
      }
    ]">
    <i :class="icon" v-if="icon"></i>
    <!-- 如果没有传入插槽的时候才显示 -->
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>

运行项目

现在本地跑起来,看看效果。完善之前的local文件夹内容,新增App.vue,man.js,TestButton.vue三个文件。
App.vue从testButton中引入了McButton组件并展示

// local/App.vue
<template>
  <div id="app">
    <test-buttons></test-buttons>
  </div>
</template>

<script>
import TestButtons from './TestButton'
export default {
  name: 'app',
  components: {
    TestButtons
  }
}
</script>

<style lang="scss">
</style>

main.js是入口文件

// local/main.js
import Vue from 'vue'
import App from './App.vue'
// 导入组件库
import BkyzUI from '../packages'

Vue.config.productionTip = false

Vue.use(BkyzUI)

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

在引入了packages的组件库后,TestButton.vue可以直接使用mc-button

<template>
  <div class="app">
    <div class="row">
      <mc-button>按钮</mc-button>
      <mc-button type="primary">按钮</mc-button>
      <mc-button type="success">按钮</mc-button>
      <mc-button type="info">按钮</mc-button>
      <mc-button type="warning">按钮</mc-button>
//      ...
    </div>
  </div>
</template>

<script>
export default {
  name: 'TestButtons'
  methods: {
  }
}
</script>

<style lang="scss" scoped>
.app {
    width: 600px;
    height: 200px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin-top: 100px;
}
.row {
  padding: 10px 0;
}
.mc-button {
  margin-left: 10px;
}
</style>

在执行npm run serve之后打开页面可以看到外面自定义的组件在项目内是可以正常使用了。

Screen Shot 2021-08-12 at 2.28.44 PM.png

打包

在Vue-cli3的 官方文档 中有个构建目标有明确的说明怎么打包成一个应用或者一个库!此时,我们需要在package.json中添加一条打包命令

vue-cli-service build --target lib 指定打包的文件

然后控制台执行yarn lib 即可将外面的组件库包括字体图标一起打包生成一个dist文件夹。

由于我们开发的组件库是给别人用的,我们没有必要把所有的代码都发布到npm上。所以我们需要在项目的根目录创建一个.npmignore的文件,忽略那些文件上传

// .npmignore
# 忽略目录
local/
packages/
public/
 
# 忽略指定文件
vue.config.js
babel.config.js
*.map
.editorconfig.js

编辑package.json,添加main方便其他人下载时找到对应打包的文件

{
 "main": "dist/bkyz-ui.umd.min.js",
 ...
}

我的git仓库 点击 注意点:

  • 上传到npm上时,要将package.json中的private属性修改为false
  • 包迭代时,要修改package.json里的版本号,再执行发布命令

发布到npm

注意:由于我们是要上传的npm的,所以本地npm的源要使用原本的源,不能使用淘宝源或其他

npm config get registry // 查看npm当前镜像源
npm config set registry https://registry.npmjs.org/ //设置为npm源

在控制台执行npm login,登陆后执行npm publish .进行发布。

使用我们的自定义组件库

发布完成后就可以拿来尝试使用了。这里仍然是用vue的web项目来尝试。新建一个vue101项目,过程略过,然后在main.js中引用它

import Vue from 'vue'
import App from './App.vue'
import BkyzUI from 'bkyz-ui'
Vue.config.productionTip = false
Vue.use(BkyzUI)
new Vue({
  render: h => h(App),
}).$mount('#app')

修改helloworld.vue文件

<template>
  <div class="hello">
    <mc-button plain @click="handleClick">字体</mc-button>
    <mc-button type="primary">按钮</mc-button>
    <mc-button icon="mc-icon-check" circle plain type="primary"></mc-button>
  </div>
</template>

<script>
import 'bkyz-ui/dist/bkyz-ui.css'
import Bkyz from 'bkyz-ui'

export default {
  name: 'HelloWorld',
  methods: {
    handleClick() {
      console.log('click');
    }
  },
  props: {
    msg: String
  }
}
</script>

如果是按需引用:

<template>
  <div class="hello">
    <mc-button plain @click="handleClick">字体</mc-button>
    <mc-button type="primary">按钮</mc-button>
    <mc-button icon="mc-icon-check" circle plain type="primary"></mc-button>
  </div>
</template>

<script>
import 'bkyz-ui/dist/bkyz-ui.css'
import Bkyz from 'bkyz-ui'

export default {
  name: 'HelloWorld',
  components: {
    McButton: Bkyz.Button
  },

  methods: {
    handleClick() {
      console.log('click');
    }
  },
  props: {
    msg: String
  }
}
</script>

查看下页面

Screen Shot 2021-08-12 at 2.53.22 PM.png

参考