ElementUI 源码分析2 - 组件篇

2020-03-04  本文已影响0人  风之化身呀

ElementUI 是一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。

0、前言

老规矩,带着问题看源码:

1、组件分析

3.1、基本结构

组件的共性分析:
1、结构都是类似的,以Alert为例:

import Alert from './src/main';  // 单文件组件
/* istanbul ignore next */
Alert.install = function(Vue) {
  Vue.component(Alert.name, Alert);
};
export default Alert;

这样写的目的在于提供两种方式使用 Alert:

import {Alert} from 'element-ui'
// 方式1:直接使用(每次使用的时候都需要import)
<alert>
// 方式2:注册成全局组件(每次使用的时候不需要import)
Vue.use(Alert)

2、单文件组件的结构
i、只写template和script部分,style部分单独在 theme-chalk 下,在使用按需加载组件时,会配合 babel-plugin-component 插件将style部分引入

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

ii、template 部分大都以 transition 组件包裹,其name属性指定过渡动画,过渡动画的css代码单独抽出在 theme-chalk/src/common/transition.scss,联合 icon.scss 组成两个所有组件都依赖的公共css文件。template中固定类名写在 class 上,动态类名写在 :class 上,且类名都以 computed 方式通过JS代码提供
iii、template 中较多地方使用 slot 实现内容的动态化
iiii、通过 this.$emit() 的方式向外部触发事件

3.2 组件实现分析

组件数量较多,这里仅做简要分析,详细分析请看 Element非官方分析

v-bind="[$props,$attrs]"  // 这种写法可以将父组件的所有props和attrs绑定到子组件

指令v-clickoutside 的实现原理:将使用了该指令的组件收集到一个数组nodeList ,然后监听document 的 mouseup 事件,遍历nodeList,通过el与event.target来判断是否在外部点击,从而执行对应的处理函数

  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = MessageBox.confirm;
  Vue.prototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = Notification;
  Vue.prototype.$message = Message;
// 全局
this.$message({type:'success',message: '恭喜你,这是一条成功消息'})
// 局部
Message.success('恭喜你,这是一条成功消息')

当多次调用时,仍会创建多个实例

2.4、样式分析

ElamentUI 以 BEM 的方式组织样式,以 Alert.vue 分析:

<template>
  <transition name="el-alert-fade">
    <div
      class="el-alert"
      :class="[typeClass, center ? 'is-center' : '', 'is-' + effect]"
      v-show="visible"
      role="alert"
    >
      <i class="el-alert__icon" :class="[ iconClass, isBigIcon ]" v-if="showIcon"></i>
      <div class="el-alert__content">
        <span class="el-alert__title" :class="[ isBoldTitle ]" v-if="title || $slots.title">
          <slot name="title">{{ title }}</slot>
        </span>
        <p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
        <p class="el-alert__description" v-if="description && !$slots.default">{{ description }}</p>
        <i class="el-alert__closebtn" :class="{ 'is-customed': closeText !== '', 'el-icon-close': closeText === '' }" v-show="closable" @click="close()">{{closeText}}</i>
      </div>
    </div>
  </transition>
</template>

再看对应的 alert.scss:

// 省略了部分
@import "mixins/mixins";
@import "common/var";

@include b(alert) {
  width: 100%;
  padding: $--alert-padding;
  margin: 0;
  box-sizing: border-box;
  border-radius: $--alert-border-radius;
  position: relative;
  background-color: $--color-white;
  overflow: hidden;
  opacity: 1;
  display: flex;
  align-items: center;
  transition: opacity .2s;

  @include when(center) {
    justify-content: center;
  }

  @include m(success) {
    &.is-light {
      background-color: $--alert-success-color;
      color: $--color-success;

      .el-alert__description {
        color: $--color-success;
      }
    }

    &.is-dark {
      background-color: $--color-success;
      color: $--color-white;
    }
  }

  @include e(closebtn) {
    font-size: $--alert-close-font-size;
    opacity: 1;
    position: absolute;
    top: 12px;
    right: 15px;
    cursor: pointer;

    @include when(customed) {
      font-style: normal;
      font-size: $--alert-close-customed-font-size;
      top: 9px;
    }
  }
}

其中的 b,e,m,when都是定义好的 mixin;这种实现让代码显得异常整洁。且大部分组件都是这种模式,维护起来也是非常方便,值得借鉴!

3、小结 & 收获

小结

回答开始的问题

收获

上一篇下一篇

猜你喜欢

热点阅读