Vue UI库造轮子之路

Vue UI库造轮子之路

. 约 8 分钟读完
IDE 配置(Webstorm)
  • Shift + Shift 快速搜索
  • Ctrl + V 打开VCS快捷选择器
  • 关闭 Safe Write 功能 Appearance... → Use "safe write"...
  • 勾选允许未授权的HTTP请求 Build... → Debugger → Allow unsigned requests
  • 设置CSS颜色背景
  • emmet快捷模糊搜索
项目搭建
  1. Parcel打包
  2. 全局或项目安装parcel:npm install -g parcel-bundler
  3. Webpack打包

html引用

<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
  <title>Cyberpunk-ui base vue.js</title>
</head>
<body>
  <div id="app">
    <c-button>测试</c-button>
  </div>
  <script src="./src/app.js"></script>
</body>
</html>

button/index.js 注册Button组件

import CButton from './button';

CButton.install = function(Vue) {
  Vue.component(CButton.name, CButton);
};

export default CButton;

app.js 直接使用注册的组件

import Vue from 'vue';
import Button from './components/button'

Vue.use(Button);

new Vue({
  el: '#app',
});

初始化目录

.
├── app.js
└── components
    └── button
        ├── button-group.vue
        ├── button.vue
        └── index.js
对比其它UI库

Ant Design Vue 实现

  • 按照类React风格实现
  • 类型支持原生JS引用和TypeScript使用
  • HTML模板使用Vue的JSX实现
  • CSS样式使用Less编译,且放在组件目录下,便于管理
  • 整体风格偏向React & JSX
  • 测试用例写在组件目录__test__

Element UI 实现

  • 使用vue-loader关注点分离原则实现
  • 类型系统采用Vue自带的props配置
  • HTML模板使用Vue 原生的template编译引擎
  • CSS样式使用Scss编译
    • /packages/theme-chalk/目录下单独作为一个主题项目存在
    • 使用gulp处理scss和font,使其自动补全浏览器前缀,压缩代码,并放入lib文件夹
  • 整体风格比较纯正的Vue
  • 测试用例放在/test/unit/specs/
代码测试
  • BDD 行为驱动开发:是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。行为驱动开发的根基是一种“通用语言”,由于客户和开发者使用同一种“语言”来描述同一个系统,可以最大程度避免表达不一致带来的问题。
  • TDD 测试驱动开发:倡导先写测试程序,然后编码实现其功能,测试驱动开发的目的是取得快速反馈,并使用“illustrate the main line”(说明主线)方法来构建程序。开发过程:首先,驱动代码的设计和功能的实现;其后,驱动代码的再设计和重构。
  • Assert 断言:目的是为了标示与验证程式开发者预期的结果
  • 前端测试常用工具:
    1. Karma([ˈkɑrmə] 卡玛)是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例
    2. Mocha([ˈmoʊkə] 摩卡)是一个单元测试框架/库,它可以用来写测试用例
    3. Sinon(西农)是一个 spy / stub / mock 库,用以辅助测试(使用后才能理解)
    4. Chai.js 断言库
持续集成

.travis.yml 配置文件模板:

language: node_js
node_js:
	- "12"
addons:
  chrome: stable
sudo: required
before_script:
  - "sudo chown root /opt/google/chrome/chrome-sandbox"
  - "sudo chmod 4755 /opt/google/chrome/chrome-sandbox"
发布NPM包
  1. 更新 package.json
    • 在 package.json 里将版本号改为 0.0.1,并随代码库更新版本
    • 创建 index.js,在 index.js 里将你想要导出的内容全部导出
  2. https://www.npmjs.com/ 注册一个账户
  3. 在项目目录下运行 npm adduser
  4. 运行 npm publish
造轮子总结
http://static.ylzon.com/blog/Jbrz9d.jpg

Button 按钮组件


任务清单

第一期API:

  • [x] Button 基本样式
  • [x] Button icon属性
  • [x] Button iconPosition属性
  • [x] Button loading属性
  • [x] Button onClick回调
  • [x] ButtonGroup 组件

第二期API:

  • [ ] Button disable属性
  • [ ] Button type属性
  • [ ] Button size属性
  • [ ] Button shap属性
  • [ ] Button ghost属性
  • [ ] Button htmlType属性

第三期API:

  • [ ] Button Link类型
  • [ ] Button href属性
  • [ ] Button target属性
实现要点
  • ButtonGroup间距合并需要margin-left + :not(:first-child)实现
  • Button的icon顺序可以利用flex + order实现,也可以用float实现,不过以防万一还是要清除浮动

Button的loading动画,按实际开发应该交给Icon组件,不要在button中实现,实现方法:

.c-button-loading-icon {
  animation: spin 1.2s infinite linear;
}

@keyframes spin {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

Inpu 按钮组件


任务清单

第一期API:

  • [x] Input 基本样式
  • [x] Input placeholder参数
  • [x] Input size参数
  • [x] Input disabled 参数
  • [x] Input readonly 参数
  • [x] Input change 事件监听
  • [x] Input input 事件监听
  • [x] Input focus 事件监听
  • [x] Input blur 事件监听

第二期API:

  • [ ] Input type 参数
  • [ ] Input + Form 表单验证
实现要点
  • 测试change/input/focus/blur事件可以使用循环的方式测试,利用spy函数测试是否被触发
  • 测试触发事件可以用new Event('input'), 测试传参可以用Object.defineProperty添加
['change', 'input', 'focus', 'blur'].forEach((eventName) => {
  vm = new Constructor({}).$mount()
  const callback = sinon.fake();
  vm.$on(eventName, callback)
  //触发input的change 事件
  let event = new Event(eventName);
  Object.defineProperty(
    event, 'target', {
      value: {value: 'hi'}, enumerable: true
    }
  )
  let inputElement = vm.$el.querySelector('input')
  inputElement.dispatchEvent(event)
  expect(callback).to.have.been.calledWith('hi')
})

Grid 网格组件


任务清单

第一期API:

  • [x] Row 基本样式
  • [x] Row gutter 属性
  • [x] Row align 属性
  • [x] Col 基本样式
  • [x] Col span 属性
  • [x] Col offset 属性
  • [x] Col 响应式属性xs, sm, md, lg, xl, xxl

第二期API:

  • [ ] Row gutter 响应式属性xs, sm, md, lg, xl, xxl
  • [ ] Row gutter 数组传参,设置上下边距
  • [ ] Col order 属性
实现要点
  • 测试Row的样式必须要创建一个dom元素并挂载在document上,才能测到实际元素的计算样式,可以用getComputedStyleAPI获取

测试Row和Col的gutter属性,需要先创建一个dom,并用innerHTML的方式写入row和col标签结构,然后通过vue创建vm实例,然后进行用异步测试dom中的计算样式

Vue.component('c-row', Row)
Vue.component('c-col', Col)
const div = document.createElement('div');
document.body.append(div);
div.innerHTML = `
  <c-row gutter="20">
    <c-col span="12"></c-col>
    <c-col span="12"></c-col>
  </c-row>
`;
const vm = new Vue({
  el: div
});
setTimeout(() => {
  const row = vm.$el.querySelector('.c-row');
  expect(getComputedStyle(row).marginLeft).to.eq('-10px');
  expect(getComputedStyle(row).marginRight).to.eq('-10px');
  const col = vm.$el.querySelectorAll('.c-col');
  expect(getComputedStyle(col[0]).paddingLeft).to.eq('10px');
  expect(getComputedStyle(col[0]).paddingRight).to.eq('10px');
})

Layout 布局组件


任务清单

第一期API:

  • [x] Layout / Header / Footer / Main / Aside 组件基本样式
  • [x] Header、Footer height参数
  • [x] Aside width参数

第二期API:

  • [ ] Layout direction参数,接受horizontal / vertical
  • [ ] 添加相关测试用例
实现要点
  • 该组件主要利用flex实现,利用Layout组件的flex属性给包裹的子组件设置水平或者垂直排列
  • 参考Element UI Container

Tabs 标签页组件


任务清单

第一期API:

  • [x] Tabs / TabsItem / TabsHead / TabsBody / TabsPane 组件基本样式
  • [x] TabsItem、TabsBody name参数
  • [x] Tabs selected参数

第二期API:

  • [ ] 测试用例待完善
  • [ ] 三层组件优化为两层组件
  • [ ] 添加direction属性
  • [ ] 添加多个标签时左右滑动的功能
  • [ ] 重构样式
实现要点
  • Tabs组件核心主要用到了Vue的依赖注入和发布订阅模式实现,根节点作为派发节点
  • 可以在组件上绑定data-xxx用于测试

Popover 弹出框组件


任务清单

第一期API:

  • [ ] position:上下左右弹出功能
  • [ ] trigger:事件触发方式 click / hover

第二期API:

  • [ ] 完善左上右上左下右下八个位置
实现要点
  • 利用slot插槽绝对定位弹窗的位置,同时用getBoundingClientRectAPI获取详细的位置
  • 点击的时候在文档末尾插入弹窗的dom节点
  • 同时在document上绑定关闭事件,点关闭的时候清除自身绑定

Collapse 折叠面板组件


任务清单

第一期API:

  • [x] collapse selected属性
  • [x] collapse-itemname属性
  • [x] collapse-itemtitle属性

第二期API:

  • [ ] 完善测试用例
实现要点
  • 可以取父元素最后一个元素中的title去除最后一个元素的border
  • 实现手风琴模式可以用eventBus和props/事件回调两种方法实现
Vue
本篇已被阅读