从0搭建自己的vue前端组件库(发布、部署)

4,556 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

一直想实现一个自己的组件库,趁着前段时间抽空自己做了一个。构建之前觉得此事并不难,当真正动手去实现的时候,会出现各种问题,还好都一步步的解决了。 中文文档 1635257833(1).jpg 技术准备:Vue2.x、VueTestUtils(测试)、Vuepress(文档)
其他准备:npm账号、阿里云服务器、域名等

构建过程

初始化

借助vue-cli初始化项目,我的选择有sass、jest(测试)等;
借助脚手架初始化的项目@vue/test-utils会自动安装官方文档
使用npm install -D vuepress命令安装安装Vuepress官方文档

package.jsonscripts中添加如下命令

"scripts": {
    "serve": "vue-cli-service serve", // 项目运行
    "build": "vue-cli-service build", // 项目打包
    "test:unit": "vue-cli-service test:unit", // 测试
    "docs:build": "vuepress build docs", // 对Vuepress的文档打包后放到服务器
    "docs:dev": "vuepress dev docs", // 本地运行查看Vuepress的文档
    "lib": "vue-cli-service build --target lib packages/index.js" // 打包用来npm发布,后面路径换成自己的路径
  },
目录结构
|-- xixixi-ui
    |-- .npmignore // 对npm发布相关配置
    |-- babel.config.js
    |-- jest.config.js // 测试相关配置
    |-- README.md
    |-- vue.config.js 
    |-- docs // Vuepress文档的内容,文件夹命名及文件位置要遵循官方配置
    |   |-- README.md
    |   |-- .vuepress
    |   |   |-- config.js
    |   |   |-- components // 此文件夹下的vue代码可以直接在md中使用,无需再次引入
    |   |   |   |-- xxx-button.vue
    |   |   |   |-- xxx-checkbox.vue
    |   |   |-- public // 静态资源(静态资源一定要放到此处)
    |   |       |-- favicon.ico
    |   |       |-- fonts // 阿里巴巴矢量图标
    |   |-- views // 单个的md文件页面
    |       |-- components
    |       |   |-- basic
    |       |   |   |-- Button.md
    |       |   |   |-- Icon.md
    |       |-- guide
    |           |-- install.md
    |           |-- start.md
    |-- packages // 组件库的每个组件
    |   |-- index.js // 导出每个组件,供vue.use()
    |   |-- button
    |   |   |-- button.vue
    |   |   |-- index.js
    |-- src 
    |   |-- App.vue
    |   |-- main.js
    |   |-- views
    |       |-- Home.vue
    |-- tests // 测试代码
        |-- button.spec.js
        |-- unit
            |-- example.spec.js
编写组件

此处以Button代码为例展示,css省略

// button.vue
<template>
  <button class="xxx-button" :class="[`xxx-button--${type}`,`xxx-button--${size}`, {
    'is-plain': plain,
    'is-round': round,
    'is-circle': circle,
    'is-disabled': disabled
    }]"
    :disabled="disabled"
    @click="handleClick"
  >
    <i v-if="icon" :class="icon"></i>
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>

<script>
export default {
  name: 'XxxButton',
  props: {
    type: {
      type: String,
      default: 'default'
    },
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    icon: {
      type: String,
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: 'default'
    },
  },
  methods: {
    handleClick (e) {
      this.$emit('click', e)
    }
  }
}
</script>

导出对应的button

// button/index.js
import Button from './button.vue'
export default Button
把组件全局和局部暴露出去
// package/index.js
import Button from './button'
import Dialog from './dialog'
...
import '../src/assets/fonts/iconfont.css'

// 存储组件列表
const components = [
  Button,
  Dialog,
  // ...还有多个此处略过
]
const install = function (Vue, options = {}) {
  // 全局注册所有的组件
  components.forEach((item) => {
    Vue.component(item.name, item)
  })
}

Vue.prototype.$message = Message;
Vue.prototype.$notice = Notice;

 
// 判断是否是直接引入文件,如果是,就不用调用 Vue.use()
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default {
  install,
  Button,
  Dialog,
  // ...还有多个此处略过
}
对组件进行单元测试。更多详细使用请参考VueTestUtils官方文档

配置jest.config.js文件

module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
  transform: {
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
  },
  // 自定义路径简称,类始于webpack中的alias
  moduleNameMapper: {
    "^@pack(.*)$": "<rootDir>/packages$1"
  },
  // 匹配tests目录下的spec.js和test.js文件进行测试,默认值为tests/unit文件下的文件测试
  "testMatch": [
    "<rootDir>/tests/**/*.spec.js",
    "<rootDir>/tests/**/*.test.js",
  ]
}

对Button组件进行单元测试

button.spec.js
import { mount } from '@vue/test-utils'
import Button from '@pack/button/button.vue' // @pack路径在jest.config.js中配置

describe('button.vue',()=>{
    it('button class', async ()=>{
        const wrapper  = mount(Button)
        await wrapper.setProps({ type: 'warning' })
        expect(wrapper.classes()).toContain('xxx-button--warning')
    }),
    it('点击 button 触发 click 事件', async () => {
        const wrapper  = mount(Button,{
            slots:{
                default:"按钮"
            }
        })
        wrapper.vm.$emit('handleClick', 123)
        await wrapper.vm.$nextTick() // 等待事件处理完成
        expect(wrapper.emitted().handleClick).toBeTruthy()
    })
})
借助Vuepress撰写组件文档(也可选择StroyBook)此处可参考我的另一篇文档传送门

此处默认你已有服务器,并对nginx有一定的了解。使用npm run docs:build命令对文档进行打包,打包后的文件会在docs/.vuepress/dist文件夹下,把打包后的文件放到自己云服务器对应的目录下,配置对应的nginx(不同的人不同的配置,此处不再展开)。

npm发布
  1. package.json中的'private':false非私有,'version': '0.1.0'保证版本不同
  2. 确保npm中没有对应命名的私包,如果已经有的话,改一下package.json中的name
  3. 运行npm run lib命令,打包成能发布到npm的包
  4. 注册npm账号,在该项目下的cmd中键入npm login后,输入自己的账号、密码、邮箱进行登录。(此时要保证npm的config地址必须为npm的源地址
  5. 使用npm publish命令发布自己的npm包,后续发布时注意修改package.json中的version版本
总结

在从0构建整个组件的过程,一切看似很简单,但是有些东西真正去操作的时候,会出现各种各样的问题,有些东西是自己以前从未上手使用的,比如Vuepress、npm发布等,这些都或多或少的能从网上找到一些参考,但是和自己项目本身上所出现的情况又会有所不同,只要大胆的去尝试,总会解决的。
我在尝试这个组件库开发的时间段内,参考了多个组价库和文档去解决我所碰到的问题,希望看到这里的你,对你也有所帮助!