【Vue进阶】青铜选手,如何自研一套UI库?

前言

更新javascript

  1. 本地跑ange-ui项目的时候须要全局安装vuepress,目前vuepress有0.x和1.x版本(刚发布,查看1.x文档)系列,安装最新的vupress没法正常运行项目(感谢 @_呜啦啦啦火车笛 提出),本项目使用1.x vuepress会有如下问题:
  • 受权拒绝错误
  • 与最新版sas-loader(7.x)不兼容
  1. 使用0.x能够避免npm install vuepress@0.14.11 -g 几天前官方发布的vuepress 1.x版本:

谢谢你们支持!css

即使是一个青铜,也要用王者的心去编码!html

output

Github上关于Vue的UI库,大大小小不可胜数,即使是已经被推广使用的成熟库,也有不少。不少时候,咱们自研一套UI库,不是想要作得多牛逼,竞争过别人(事实咱也干不过人家,除非你不是一我的在战斗。毕竟这不只是个技术活,仍是个体力活),咱们仅仅是源自一个青铜对王者的仰望或者是为知足心里的需求。vue

这里跟你们讲一个一步一步自研UI库的故事。java

原文地址:github.com/qiud...node

项目地址:Ange UIwebpack

开发一套UI库,作成不难(这里指的是半半半成品,全品也好难。。。),作好很难。但不慌,咱有秘籍css3

  1. 必定的内功修为。所谓打铁还需自身硬,要作高复用组件的开发工做,对Vue.jsCSS3仍是有必定的技术要求。那我要多牛逼才能写好这个UI库啊?这取决于你这套UI库的实现高度。
  2. 一些招式套路。藏经阁(Github)上有不少关于Vue的UI库:六脉神剑、独孤九剑应有尽有。人家怎么写的,我们跟着来就行了。有人可能意见很大,那不是在模仿吗?这里要严正声明,咱们不是在模仿,咱们只是向标准靠拢。由于牛逼的就是标准的

UI库的必要架构

一套成熟的开源UI库通常都具有如下几个特色:git

  • 包含了组件源码
  • 完备的说明文档
  • 符合Eslint校验标准
  • 全面的单元测试
  • 完整的构建生态

所以,它们的目录架构也出现了主要的两种形式: github

image
&
image
有点大同小异,这里咱们按照第一种架构去开发。

  • build:存放构建配置文件
  • docs:官方说明文档
  • src:组件源码
  • test:单元测试用例(这里不做阐述)
  • eslintrc:基于eslint-plugin-vue的开发规范标准

搭建UI开发环境

观察社区几大UI库发现,它们都是基于webpack搭建了本身的构建配置,包括本地开发、生产环境构建、UI库的打包等,创建一套本身的构建生态。咱们就不一样了,业余一点(实际上是善于应用开源工具),咱们的docs文档是基于vuepress的,vuepress有本身一套的构建体系,因此咱们只须要针对UI组件源码写一份打包配置就行了。

下面开始搭建打包配置(其实就是好久之前咱们作的基于webpack的构建,如今cli用得多了,配置也不会写了,码耶): 首先在根目录创建configbuild文件夹,而后往两个目录分别新建文件,以下:

image

  • build-lib.js:node执行的脚本,读取并执行lib的配置进行构建;
  • webpack.base.conf.js:公共的构建配置;
  • webpack.lib.conf.js:针对UI组件打包的配置;
  • index.js:可变的配置信息;
  • prod.env.js:声明当前构建环境为生产环境的配置;

不是说只有一份打包的配置文件么?咋还多了那么多文件呢?是这样,虽然咱们是业余的,但咱也想作得专业一点对吧(有利于对配置进行扩展管理)!

咱们看 config/index.js有什么?

image
好像注释也很清楚?这些定义最终都被应用在 webpack.lib.conf.js中。

再看看build/webpack.base.conf.js里面的关键配置:

image
基础配置里面的 entryoutput会被 webpack.lib.conf.js覆写; rules定义一系列loader的转换规则,其中eslint的校验就是在这里定义了一个 eslint-loader

最后看一眼build/webpack.lib.conf.js配置:

image
lib里面重写 entry的规则是,根据传参值(components)分别走不一样的入口文件,一种只有一个 ./src/index.js,这个是UI对外注册的入口文件(这使得咱们能够引入整个UI);另外一种是各个UI组件的注册入口文件(这使得咱们能够按需引入组件);

先给你们贴图直观感觉下源码的目录结构:

image

那实际打包的时候走的是哪种呢?真相是都走!在build-lib.js中,执行了三次打包,打包输出效果以下:

image
第一次的打包的输出是通过压缩的(压缩css样式表文件,能够看到输出文件带了min后缀),第二次是未压缩的,最后一次是对各组件分别打包。(PS:我很想给你们用红圈圈在图上标记下,奈何设备不容许。。。数度哽咽......Ubuntu系统下你们有好用的截图标记工具能够推荐下吗?)

开发一个组件

前面扎好了马步,终于到修炼招式的阶段了! 咱们都知道,在应用某个插件的时候须要通过下面代码的调用:

import Ange from 'ange-ui'
Vue.use(Ange)
复制代码

好奇下Vue.use在作什么处理呢?它其实就是注册/安装这个插件,根据use内部的定义,它经过调用install方法去注册插件,那么,Ange就必须是一个Function(会被use当作是install方法调用)或者是一个包含了install方法的Object

道理我都懂,但是install方法里面到底写些什么?

试想一下,咱们但愿在项目的任意位置都能引用这个插件,那咱们的每个组件是否是要在全局注册?好比经过下面这种方式全局注册组件:

Vue.component('pagination', pagination)
复制代码

没错,install方法内部就是批量地全局注册组件。

搭好目录架构

首先咱们按照下图的方式新建目录和文件:

image
src目录的index.js文件中定义 install方法:

import './scss/ange.scss'  // 引入组件样式表,也可让用户在使用的时候自行引入
import components from './components'

function install(Vue, opts = {}) {
    Object.values(components).forEach((each) => {
        Vue.component(each.name, each)
    })
}

if (typeof window !== 'undefined' && window.Vue) {
    install(window.Vue)
}
export default {
    version: '1.0.0',
    install,
    ...components
}
复制代码

核心逻辑install就是对全部的组件循环注册在全局。components目录的 index.js 则是逐个对外暴露组件对象,其次每个组件也有一个 index.js ,它的做用是为当前组件注入install方法。理所固然地,install里面是将该组件注册在全局,因而咱们能够按需引用组件。

开发Button组件

组件开发的通用模板

<template>
    <component :is="'button'"></component>
</template>

<script>
export default {
    name: 'ag-button',
    props: {}
}
</script>
复制代码

component是vue的内置组件,is参数设置成button,代表最终渲染的html是button标签,咱们也能够直接使用button标签,但咱们的按钮组件不必定是button,还多是a标签,为了更好的拓展,这里使用component。

声明组件参数

export default {
    name: 'ag-button',
    props: {
        // 按钮类别
        primary: Boolean,
        secondary: Boolean,
        dashed: Boolean,
        link: Boolean,
        // 按钮状态
        color: {
            type: String,
            validator (val) {
                return new Set(['success', 'warn', 'danger']).has(val)
            }
        },
        // 按钮尺寸
        size: {
            type: String,
            validator (val) {
                return new Set(['large', 'normal', 'small']).has(val)
            }
        },
        // 图标按钮
        icon: String,
        // 圆形按钮(通常结合图标按钮使用)
        circle: Boolean,
        // 外链按钮
        external: Boolean,
        // 异步按钮
        loading: Boolean
    }
}
复制代码

完善组件模板

<template>
    <component :is="tag" class="ange-btn" :class="[ btnSize, color, { 'default': isDefault, 'primary': primary, 'secondary': secondary, 'dashed': dashed, 'link': link, 'icon': icon, 'circle': circle }]" @click="$emit('click', $event)" :disabled="loading">
        <span class="ange-btn-content">
            <!-- 图标按钮依赖 ag-icon 组件 -->
            <ag-icon v-if="icon" :icon="icon" />
            <slot />  <!-- 插槽接收按钮文本 -->
        </span>
    </component>
</template>

<script> export default { // ... computed: { tag () { return this.external ? 'a' : 'button' }, isDefault() { const type = [this.primary, this.secondary, this.dashed, this.link] return type.every((each) => !each) }, btnSize() { return this.size || 'normal' } } } </script>

复制代码

剩下的工做就是写好样式表了,你能够选择直接写在vue文件里面,也能够新建_scss/scss_样式表。

组件应用及效果在线查看

image

以上,便开发好一个button组件了,执行一下node build-lib.js或者npm run build:lib(如今package.json声明script)就能够打包这个UI框架,而后再将其发布到npm平台(若是你想...)

写好一份文档

在线查看 Ange UI Docs 写好文档是一个库不可或缺的部分,写的过程经过实际应用各组件,还能够对其进行测试校验。前面说到,咱们的文档要基于vuepress开发,简洁的Markdown写法,非常方便。这里一篇 指南 能够很好地帮助你,它告诉了你如何搭建架构,写好配置以及部署上线,或者参考我这个 仓库 的配置。

假设docs/views/button.md是你的button组件的文档页面,要如何引用你的组件?

image
Ange UI内置引入了样式表,这里能够不用再引入,也能够按需只引入 button组件:

import '@scss/ange.scss'
import Button from '@component/button'
Vue.use(Button)
复制代码

至此,文档也就写好了!

最后的最后,按照 vuepress doc 部署到你的github仓库上就能够了!

本文经过button组件从0到1的开发,深刻浅出阐述了Vue UI框架的开发流程,你对Vue.js的理解越深,组件的功能越复杂,你就会用到更多的高级用法。经过自研UI框架,咱们也有很大的收获:

  • 从新温习webpack配置
  • 深刻理解Vue内部机制,掌握更多的vue高级用法
  • 夯实js和css3基础
  • 掌握编程范式和设计模式

PS:CSS实际上是UI开发中占比很重的部分,你们按照本身的风格组件化开发就好。给你们推荐几个很棒的配色网站

最后,但愿你们也能多多去尝试,这个青铜自研UI库的故事到这里就结束了。

The end.

相关文章
相关标签/搜索