创建项目
- 创建仓库并上传到Github仓库中
git initgit commit -m 'init'
- 声明软件许可

Create new file => LICENSE => Choose a license template => MIT License => Review and submit => Commit new file => Create pull request => Merge pull request => Confirm merge => Delete branch
- 初始化项目
npm init
version: 0.0.1
description: 这是一个UI框架
keywords: vue,ui
author: Layouwen
license: MIT
- 安装Vue
npm i vue
- 设置unsigned
选中”Allow unsigned requests”
创建第一个全局组件
创建button.js
Vue.component('g-button', {template: `<button>hi</button>`})
在index.html中引用
<g-button><script src="./button.js"></script>
添加g-button的基本样式
.g-button{padding: 0 1em;height: var(--button-height);font-size: var(--font-size);border: 1px solid var(--border-color);border-radius: var(--border-radius);background: var(--button-bg);}.g-button:hover {border-color: var(--border-color-hover);}.g-button:active {background-color: var(--button-active-bg);}.g-button:focus {outline: none;}
将button改为单文件组件
<template><button class="g-button">按钮</button></template><script>export default {}</script><style lang="scss">.g-button {padding: 0 1em;height: var(--button-height);font-size: var(--font-size);border: 1px solid var(--border-color);border-radius: var(--border-radius);background: var(--button-bg);&:hover {border-color: var(--border-color-hover);}&:active {background-color: var(--button-active-bg);}&:focus {outline: none;}}</style>
去iconfont添加图标,并应于进去
使用slot插槽自定义按钮文字,使用props自定义icon名字、icon位置
button.js
<template><div><button class="g-button" :class="{[`icon-${iconPosition}`]: true}"><svg v-if="icon" class="icon"><use :xlink:href="`#icon-${icon}`"></use></svg><div class="content"><slot></slot></div></button></div></template><script>export default {props: ['icon', 'iconPosition']}</script>
index.html
<g-button>按钮</g-button><g-button icon="settings">按钮</g-button><g-button icon="settings" icon-position="right">按钮</g-button>
将Props改写为对象形式,更好控制其类型
props: {icon: {},iconPosition: {type: String,default: 'left'}}
将svg代码改为Icon组件
添加loading状态
添加button-group组件
<g-button-group><g-button icon="left">上一页</g-button><g-button>1</g-button><g-button>2</g-button><g-button>3</g-button><g-button icon="right" icon-position="right">下一页</g-button></g-button-group>
检测button-group中的元素是否合法
mounted() {for(let node of this.$el.children) {let name = node.nodeName.toLowerCase()if(name != 'button') {console.warn(`g-button-group 中应该只含有 button, 而你的是 ${name}`)}}}
使用chai作单元测试
npm i -D chainpm i -D chai-spies
添加自动测试
npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies
根目录创建karma.conf.js
module.exports = function (config) {config.set({// base path that will be used to resolve all patterns (eg. files, exclude)basePath: '',// frameworks to use// available frameworks: https://npmjs.org/browse/keyword/karma-adapterframeworks: ['mocha', 'sinon-chai'],client: {chai: {includeStack: true}},// list of files / patterns to load in the browserfiles: ['dist/**/*.test.js','dist/**/*.test.css'],// list of files / patterns to excludeexclude: [],// preprocess matching files before serving them to the browser// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessorpreprocessors: {},// test results reporter to use// possible values: 'dots', 'progress'// available reporters: https://npmjs.org/browse/keyword/karma-reporterreporters: ['progress'],// web server portport: 9876,// enable / disable colors in the output (reporters and logs)colors: true,// level of logging// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUGlogLevel: config.LOG_INFO,// enable / disable watching file and executing tests whenever any file changesautoWatch: true,// start these browsers// available browser launchers: https://npmjs.org/browse/keyword/karma-launcherbrowsers: ['Chrome'],// Continuous Integration mode// if true, Karma captures browsers, runs the tests and exitssingleRun: false,// Concurrency level// how many browser should be started simultaneousconcurrency: Infinity})}
创建 test目录 button.test.js 文件
const expect = chai.expect;import Vue from 'vue'import Button from '../src/button'Vue.config.productionTip = falseVue.config.devtools = falsedescribe('Button', () => {it('存在.', () => {expect(Button).to.be.ok})it('可以设置icon.', () => {const Constructor = Vue.extend(Button)const vm = new Constructor({propsData: {icon: 'settings'}}).$mount()const useElement = vm.$el.querySelector('use')expect(useElement.getAttribute('xlink:href')).to.equal('#icon-settings')vm.$destroy()})it('可以设置loading.', () => {const Constructor = Vue.extend(Button)const vm = new Constructor({propsData: {icon: 'settings',loading: true}}).$mount()const useElements = vm.$el.querySelectorAll('use')expect(useElements.length).to.equal(1)expect(useElements[0].getAttribute('xlink:href')).to.equal('#icon-loading')vm.$destroy()})it('icon 默认的 order 是 1', () => {const div = document.createElement('div')document.body.appendChild(div)const Constructor = Vue.extend(Button)const vm = new Constructor({propsData: {icon: 'settings',}}).$mount(div)const icon = vm.$el.querySelector('svg')expect(getComputedStyle(icon).order).to.eq('1')vm.$el.remove()vm.$destroy()})it('设置 iconPosition 可以改变 order', () => {const div = document.createElement('div')document.body.appendChild(div)const Constructor = Vue.extend(Button)const vm = new Constructor({propsData: {icon: 'settings',iconPosition: 'right'}}).$mount(div)const icon = vm.$el.querySelector('svg')expect(getComputedStyle(icon).order).to.eq('2')vm.$el.remove()vm.$destroy()})it('点击 button 触发 click 事件', () => {const Constructor = Vue.extend(Button)const vm = new Constructor({propsData: {icon: 'settings',}}).$mount()const callback = sinon.fake();vm.$on('click', callback)vm.$el.click()expect(callback).to.have.been.called})})
修改 package.json 的 scripts 参数
"scripts": {"dev-test": "parcel watch test/* --no-cache & karma start","test": "parcel build test/* --no-minify && karma start --single-run"},
开始测试
npm run testnpm run dev-test
