第二集: 从零开始实现 (icon 组件)
本集定位: 这套 ui 组件我本来是先从 button 做的, 但是我发现每个组件都要用到 icon 这个组件, 如果没有他, 很多组件没法扩展, 而且 icon 本身不依赖其他组件, 所以还是先把它作为本篇文章的重点吧.icon 组件 读过 element-ui 源码的同学都知道, 他们选择的是字体图标的方式来做 icon 组件的, 而我的这套 ui 在写法与用法上参考了他们的做法, 但组件本身是靠 svg 来书写的, 其中的区别还是简单阐述一下把.icon font 与 svg
1.icon font 做为字体无法支持多色图形,这就很尴尬了.
2.icon font 主要在页面用 Unicode 符号调用对应的图标,这种方式不管是浏览器,搜索引擎和对无障碍方面的能力都没有 SVG 好.
3.icon font 采用的是字体渲染,icon font 在一倍屏幕下渲染效果并不好,在细节部分锯齿还是很明显的,SVG 上面我说过它是图形所以在浏览器中使用的是图形渲染,所以 SVG 却没有这种问题.
4. 兼容性较差,支持 ie9+, 及现代浏览器.
5. 浏览器渲染 svg 的性能一般,还不如 png。
行动起来 上一集基本环境已经搭建好了, 这里我们采用 ’bem’ 的思想, 来构建组件的样式, 所有的样式抽离在一个文件夹里面, 做到组件本身没有样式代码, 我们来先书写组件的代码. 结构如下:
index.js 文件里面是导出这个组件:
import Icon from ‘./main/icon.vue’
// 明白 vue.use 方法原理的同学都能明白这段代码的意义
// 当被 use 的时候, 进行 icon 组件的渲染
Icon.install = function(Vue) {
Vue.component(Icon.name, Icon);
};
export default Icon
这样单抽出来做一个文件的好处是, 更好的工程化, 保证职能的单一性.
main 文件夹为什么 main 文件夹里面只有一个文件还要单独抽成一个文件夹??, 原因是有的组件可能要配合自己独有的容器组件一起使用, 比如一个 button 的包裹容器, 他可以让内部的每个 button 有相同的上下左右距离, 相同的圆角等等 …
icon.vue 文件
第一步: 使用 svg, 当然要去下载 svg 图片了, 本篇推荐使用大家都在用的阿里巴巴矢量图标库, 选择自己喜欢的图标放入购物车选项.
第二步: 放入工程, 点击添加入项目, 如果没有项目要点击新建项目来完成此操作.
第三步: 复制链接到你的 script 标签里面引入, 在 index.html 里面就可以, 下面会讲遇到的问题与优化.
// 使用方法如下:
<svg class=”icon”>
// 把名字写在下面的 ’xxx’ 处, 就可以正常显示图标了;
<use xlink:href=”#icon-xxx”></use>
</svg>
第四步: 开始正式书写组件.
1: 先定义一个最简单的组件模板, 他仅仅支持颜色的调整, 与 icon 的调整
2:svg 的颜色控制, 需要通过 fill 属性, 我经常面试遇到说自己用过 svg 图片, 但是没听说过 fill 属性的尴尬场面????
3: 不传颜色的时候,svg 默认是原色
<template>
<div
class=’cc-icon’
}”>
<svg :style=”{fill:color}”>
<use :xlink:href=’`#icon-${name}`’></use>
</svg>
</div>
</template>
<script>
export default {
name: “ccIcon”,
props: {
color: String,
name: {
type: String,
required: true
}
}
};
</script>
第五步: 基本的结构已经有了, 现在要考虑的就是我们的组件还需要什么功能? .
1: 控制图标的大小, 这个还是需要的
props: {// 接受一个 size 属性
size: {// 因为用户可能传带单位的与不带单位的
type: [Number, String],
default: “1em”
}
},
computed: {// 计算属性里面对这个值进行操作, 类型如果是数字, 就我们来给他加上单位吧, 也就是默认单位是 ’px’
iconSize() {
if (typeof this.size === “number”) return this.size + “px”;
else return this.size;
}
}
<template>
<div
class=’cc-icon’
height: iconSize, // 在此使用
width: iconSize, // 在此使用
}”>
<svg :style=”{fill:color}”>
<use :xlink:href=’`#icon-${name}`’></use>
</svg>
</div>
</template>
2: 如果是加载的 icon, 我们需要让他旋转一下, 这里我是这么做的, 所有的加载图标, 我的命名都是 load 加数字, 所以检测字符串里面是否有这个关键词就好了
<svg :style=”{fill:color}”
:class=”[
‘cc-icon-‘+name,
{
//icon-loading 这个 class 名, 只有在 icon 的 name 属性里面有 load 字段的时候加上, 这个属性名里面的属性就是旋转.’~’ 位运算符是很高效的书写方式, 也就是对位求反,1 变 0,0 变 1,也就是求二进制的反码, 这里不懂的话, 先看犀牛书, 简单的说就是把 - 1 转成 0 了, 变成了布尔中的 false;
‘icon-loading’:~name.indexOf(‘load’)
}
]”>
<use :xlink:href=’`#icon-${name}`’></use>
</svg>
3: 实际使用的时候我发现, 不给 div 加上 display: ‘inline-flex’, 属性, icon 有时候对不整齐, 所以还是加上为妙, 这个地方以后应该还会弄一弄, 感觉还会有一段故事.
<div
class=’cc-icon’
:style=”{
display: ‘inline-flex’,
height: iconSize,
width: iconSize,
}”>
4: 接下来就是为了配合其他组件的 disabled 状态 把图标本身也置为灰色, 这样写很方便
<svg :style=”{fill:color}”
:class=”[
‘cc-icon-‘+name,
{
‘icon-loading’:~name.indexOf(‘load’),
‘is-disabled’:disabled // 在这里进行了定义
}
]”>
<use :xlink:href=’`#icon-${name}`’></use>
</svg>
props: {
disabled: Boolean // 当然是布尔类型
},
小知识点: props 里面定义了默认类型是布尔, 则用户可以直接在标签上写属性名, 不给属性值也是可以代表 true 的, 但是默认不是布尔, 不给属性值的话, 组件里面得到的就是 undefined;
第六步: 书写样式吧
icon.scss 文件
1:disabled 状态时禁止点击
2:icon-loading 时旋转
@import ‘./common/mixin.scss’;
@import “./common/animation.scss”;
@include b(icon) {
.#{$state-prefix}disabled {
cursor: not-allowed;
fill: $–color-disabled;
}
.icon-loading {
animation: rotating 1s infinite linear;
}
}
mixin.scss 文件里面的 b 方法
抽象出 $namespace 方便管理
@mixin b($block) {
$B: $namespace+’-‘+$block !global;
.#{$B} {
@content;
}
}
animation.scss 文件
@keyframes rotating {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
var.scss 文件
// lulu 的美工不好, 只是想做出点不同样子的东西玩玩
// 基本色
$–color-black:#000000 !default;
$–color-white:#FFFFFF !default;
// 基本色鲜艳
$–color-nomal:#409EFF !default;
$–color-success:#7CCD7C !default;
$–color-warning:#FFB90F !default;
$–color-danger: #FF0000 !default;
$–color-disabled: #bbbbbb !default;
$–color-difference: #F5F7FA !default;
// 字体
$–size-big: 16px;
$–size-nomal: 15px;
$–size-small: 14px;
// 输入框
$–color-input-placeholder:#aaa
第六步: 上面说的 svg 文件引用的改良, 可以把 svg 文件下载到本地, 在别人因为这个 ui 组件的时候不会引用 index.html 文件, 而且大部分组件都引用了 icon, 我们可以去阿里巴巴矢量图标库进行操作
点击下载到本地取出 js 文件, 让在本地
用 index.js 文件引用这个 js 文件就完成了!!
结束 icon 组件是最小的组件, 其他组件的代码量与难道都大的多, 详细 code 与用法可以访问下面的地址, npm 同步更新! 下一期是关于 button 的, 我们一起交流吧
github: 项目地址个人博客: 个人博客