小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
一直想实现一个自己的组件库,趁着前段时间抽空自己做了一个。构建之前觉得此事并不难,当真正动手去实现的时候,会出现各种问题,还好都一步步的解决了。
中文文档
技术准备:Vue2.x、VueTestUtils(测试)、Vuepress(文档)
其他准备:npm账号、阿里云服务器、域名等
构建过程
初始化
借助vue-cli初始化项目,我的选择有sass、jest(测试)等;
借助脚手架初始化的项目@vue/test-utils会自动安装官方文档;
使用npm install -D vuepress命令安装安装Vuepress官方文档;
在package.json的scripts中添加如下命令
"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发布
- package.json中的
'private':false非私有,'version': '0.1.0'保证版本不同 - 确保npm中没有对应命名的私包,如果已经有的话,改一下package.json中的
name - 运行
npm run lib命令,打包成能发布到npm的包 - 注册npm账号,在该项目下的cmd中键入
npm login后,输入自己的账号、密码、邮箱进行登录。(此时要保证npm的config地址必须为npm的源地址) - 使用
npm publish命令发布自己的npm包,后续发布时注意修改package.json中的version版本
总结
在从0构建整个组件的过程,一切看似很简单,但是有些东西真正去操作的时候,会出现各种各样的问题,有些东西是自己以前从未上手使用的,比如Vuepress、npm发布等,这些都或多或少的能从网上找到一些参考,但是和自己项目本身上所出现的情况又会有所不同,只要大胆的去尝试,总会解决的。
我在尝试这个组件库开发的时间段内,参考了多个组价库和文档去解决我所碰到的问题,希望看到这里的你,对你也有所帮助!