Element 2 组件源码剖析之Tag标签

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战css

0x00 简介

组件Tag 多用于标记和分类。 本文将深刻分析组件源码,剖析其实现原理,耐心读完,相信会对您有所帮助。packages/tag/src/tag.vue 文件是组件源码实现。 🔗 组件文档 Tag 🔗 github源码 tag.vuevue

更多组件剖析详见 👉 📚 Element 2 源码剖析组件总览git

0x01 组件源码

使用 渲染函数 建立组件,代码结构以下 👇。github

<script>
  export default {
    name: 'ElTag',
    // 组件 prop
    props: {
      // ...
    },
    methods: {
      // 关闭 Tag 时触发的事件
      handleClose(event) {
        // ...
      },
      // 点击 Tag 时触发的事件
      handleClick(event) {
        // ...
      }
    },
    // 计算属性
    computed: {
      // 标签尺寸 
      tagSize() {
        // ...
      }
    },
    // 渲染 标签 虚拟DOM
    render(h) {
      // ...
    }
  };
</script> 
复制代码

attributes 属性

组件定义了8个 propweb

props: {
  text: String,  // 代码中没有被使用
  closable: Boolean, // 是否可关闭
  type: String, // 类型
  hit: Boolean, // 是否有边框描边
  disableTransitions: Boolean, // 是否禁用渐变更画
  color: String, // 背景色
  size: String, // 尺寸
  // 主题
  effect: {
    type: String,
    default: 'light',
    validator(val) {
      return ['dark', 'light', 'plain'].indexOf(val) !== -1;
    }
  }
},
复制代码

prop详细描述以下(只有7个):element-ui

参数 说明 类型 可选值 默认值
type 类型 string success/info/warning/danger
closable 是否可关闭 boolean false
disable-transitions 是否禁用渐变更画 boolean false
hit 是否有边框描边 boolean false
color 背景色 string
size 尺寸 string medium / small / mini
effect 主题 string dark / light / plain light

proptext 虽然定义了,可是在代码中没有被使用。 typesizeeffect 的值若没有匹配指定的字符串,根据值生成无效的class(样式规则未定义)。gulp

计算属性

tagSize 根据size$ELEMENT动态计算标签的尺寸。浏览器

组件定义了prop size , 则tagSize 值为 size 定义值。若 size 值为 undefined'', tagSize 值为由全局配置 $ELEMENT决定。sass

$ELEMENT 对象不为空且包含 size 属性, tagSize 值为对象的属性值 $ELEMENT.size;不然 tagSize 值为 undefined,markdown

// 标签尺寸
tagSize() {
    return this.size || (this.$ELEMENT || {}).size;
}
复制代码

前文可知,组件入口文件中 install 方法中定义了对象 $ELEMENT 全局配置。其中组件的默认尺寸 size值为''

// src/index.js 组件入口文件
const install = function(Vue, opts = {}) {
  // ... 
  // 全局配置 组件的默认尺寸size 弹框的初始z-index
  Vue.prototype.$ELEMENT = {
    size: opts.size || '',
    zIndex: opts.zIndex || 2000
  };
  // ...
};
复制代码

完整引入组件时,使用 vue.use()注册组件,会执行 install 方法,声明了全局属性 $ELEMENT

import Element from 'element-ui'; 
Vue.use(Element);

// 手动设定 组件默认size
Vue.use(Element, { 
  size: "small", 
});
复制代码

按需引入时, 须要手动配置 Vue.prototype.$ELEMENT = { size: 'small', zIndex: 3000 }; ,若不配置 $ELEMENT 未定义值为 undefined,若此调用 $ELEMENT.size 则会抛出异常Uncaught ReferenceError: $ELEMENT is not defined

import { Tag } from 'element-ui' 
 
// 全局配置
Vue.prototype.$ELEMENT = { size: 'small', zIndex: 3000 }; 

// 引入组件 
Vue.use(Tag);
 
复制代码

表达式 (this.$ELEMENT || {}) 防止$ELEMENT 未定义赋予空对象防止异常调用。

events 事件

点击 Tag 时触发 click 事件。

// 点击 Tag 时触发的事件
handleClick(event) {
  // 触发当前实例上的事件
  this.$emit('click', event);
}
复制代码

关闭 Tag 时触发 close 事件。

// 关闭 Tag 时触发的事件
handleClose(event) {
  // 阻止捕获和冒泡阶段中当前事件的进一步传播。
  event.stopPropagation();
  // 触发当前实例上的事件
  this.$emit('close', event);
},
复制代码

render() 渲染函数

组件将建立一个 <span> 元素VNode,动态添加 class,使用内联样式设置背景色,定义了组件点击事件。 <span> 元素包含2个子节点

  1. 默认插槽;
  2. 名为el-icon-closeIcon图标,定义了关闭 Tag 时 click 事件。当 closable 值为true时才会渲染。

disableTransitions值为true时,使用内置组件 transition 包裹 <span> 元素实现缩放zoom-in-center效果。

// 渲染 标签 虚拟DOM
render(h) {
  const { type, tagSize, hit, effect } = this;
  // 动态添加class
  const classes = [
    'el-tag',
    type ? `el-tag--${type}` : '',
    tagSize ? `el-tag--${tagSize}` : '',
    effect ? `el-tag--${effect}` : '',
    hit && 'is-hit'
  ];
  // tag 元素
  const tagEl = (
    <span class={ classes } style={{ backgroundColor: this.color }} on-click={ this.handleClick }> { this.$slots.default } { this.closable && <i class="el-tag__close el-icon-close" on-click={ this.handleClose }></i> } </span>
  );
  // 组件VNode
  return this.disableTransitions ? tagEl : <transition name="el-zoom-in-center">{ tagEl }</transition>;
}
复制代码

根据组件prop 动态添加 class

  • 'el-tag' 组件默认样式。
  • type ? 'el-tag--${type}' : '' 设置组件不一样类型颜色。若 type 值不是如下success/info/warning/danger其中一个,设置无效(生成无效的class)。
  • tagSize ? 'el-tag--${tagSize}' : '' 设置组件不一样尺寸。若 tagSize 值不是如下medium/small/mini其中一个,设置无效(生成无效的class)。
  • effect ? 'el-tag--${effect}' : '' 设置组件不一样主题。若 effect 值不是如下dark/light/plain其中一个,设置无效(生成无效的class)。 effect 默认值为 light,对应的 el-tag--light是无效的样式,未定义。
  • hit && 'is-hit' 边框描边效果 。

color 属性定义组件内联样式 backgroundColor,权重较高会覆盖其余样式的颜色。

0x02 组件样式

src/tag.scss

组件样式源码 packages\theme-chalk\src\tag.scss 使用混合指令 bmgenTheme 嵌套生成组件样式。

genTheme() 组件主题

混合指令 genTheme 用于生成组件的主题样式。

@mixin genTheme($backgroundColorWeight, $borderColorWeight, $fontColorWeight, $hoverColorWeight) {
  background-color: mix($--tag-primary-color, $--color-white, $backgroundColorWeight);
  // ...
  
  // 生成 &.is-hit
  @include when(hit) {
    // ...
  }

  .el-tag__close {
    // ...
    &:hover {
      // ...
    }
  }
  // info / success / warning / danger 结构相同
  &.el-tag--info {
    // ...
    
    // 生成 &.is-hit
    @include when(hit) {
      // ...
    }

    .el-tag__close {
      // ...
      &:hover {
        // ...
      }
    }
  }
  // success
  // warning
  // danger
  
}
复制代码

Mix 函数是将两种颜色根据必定的比例混合在一块儿,生成另外一种颜色。 前两个参数是想混合的颜色(能够使用颜色变量、十六进制、RGBA、RGB、HSL 或者 HSLA 颜色值),第三个参数是第一种颜色的比例值。

mix($color1, $color2, $weight: 50%) //=> color 
复制代码

组件主题默认样式直接在 el-tag 下生成 。

// 生成 .el-tag
@include b(tag) {
  // 主题规则 默认light
  @include genTheme(10%, 20%, 100%, 100%);
  // ...
 
  // 生成 .el-tag--dark
  @include m(dark) {
    // 主题规则
    @include genTheme(100%, 100%, 0, 80%);
  }
  
  // 生成 .el-tag--plain
  @include m(plain) {
    // 主题规则
    @include genTheme(0, 40%, 100%, 100%);
  }
}
复制代码

结合上述规则生成的主题样式以下

// el-tag 能够替换为 el-tag--dark el-tag--plain
.el-tag {
  // ...
}
.el-tag.is-hit {
  // ...
}
.el-tag .el-tag__close {
  // ...
}
.el-tag .el-tag__close:hover {
  // ...
}
// info / success / warning / danger 结构相同
.el-tag.el-tag--info {
  // ...
}
.el-tag.el-tag--info.is-hit {
  // ...
}
.el-tag.el-tag--info .el-tag__close {
  // ...
}
.el-tag.el-tag--info .el-tag__close:hover {
  // ...
}
// success ...
// warning ...
// danger ...

复制代码

组件样式

组件样式逻辑以下。

// 生成 .el-tag
@include b(tag) {
  // 默认light样式 
  // ...
  
  // 生成 .el-tag .el-icon-close
  .el-icon-close {
    // ...
    // 生成 .el-tag .el-icon-close::before
    &::before {
      // ...
    }
  }

  // dark样式 ... 
  // plaink样式 ...
  
  // 生成 .el-tag--medium
  @include m(medium) {
    // ...
    // 生成 .el-tag--medium .el-icon-close
    .el-icon-close {
      // ...
    }
  }
  
  // 生成 .el-tag--small
  @include m(small) {
    // ...
    // 生成 .el-tag--small .el-icon-close
    .el-icon-close {
      // ...
    }
  }
  // 生成 .el-tag--mini
  @include m(mini) {
    // ...
    // 生成 .el-tag--mini .el-icon-close
    .el-icon-close {
      // ...
    }
  }
}

复制代码

lib/tag.scss

前文可知使用 gulpfile.js编译 scss 文件转换为CSS,通过浏览器兼容、格式压缩,最后生成 packages\theme-chalk\lib\tag.scss,内容格式以下。

.el-tag {
  // ...
}
.el-tag .el-tag__close {
  // ...
}
.el-tag .el-tag__close:hover {
  // ...
}

 
/* theme start -- el-tag / el-tag--dark / el-tag--plain -------------------------- */
.el-tag.is-hit {
  // ...
} 
.el-tag .el-icon-close {
  // ...
}
.el-tag .el-icon-close::before {
  // ...
}  
/* type -- info / success / warning / danger -------------------------- */
.el-tag.el-tag--info {
  // ...
}
.el-tag.el-tag--info.is-hit {
  // ...
}
.el-tag.el-tag--info .el-tag__close {
  // ...
}
.el-tag.el-tag--info .el-tag__close:hover {
  // ...
} 
// success ...
// warning ...
// danger ...


/* el-tag--dark -------------------------- */
// ... 

/* el-tag--plain -------------------------- */
// ... 


/* theme end -- el-tag / el-tag--dark / el-tag--plain -------------------------- */

 

/* size -- medium / small / mini -------------------------- */ 
.el-tag--medium {
  // ...
}
.el-tag--medium .el-icon-close {
 // ...
} 

复制代码

0x03 📚参考

“stopPropagation”,MDN
“mix funciton color”,sass

0x04 关注专栏

此文章已收录到专栏中 👇,能够直接关注。

相关文章
相关标签/搜索