个人 Element-ui 源码学习 ---

做者:Horace
介绍:个人 Element-ui 源码学习,持续不定时更新~
ps:新人小白一枚,若有错误,欢迎指出~css

笔者如今是一个大三快要实习的学生,最近在学习 Vue 的时候感受本身学了不少的东西可是没有什么实质性的进步,感受达到了一个瓶颈,但自认为尚未达到能够深挖 Vue 源码的功力,因此打算开始剖析 Element-ui 的源码,但愿能够借此深刻 Vue,后面我也会开始 Vue 的源码学习。vue

由于笔者刚开始 Element-ui 的源码学习不是好久,本文会从 <el-alert> 这个组件开始,而且会以一个小白的角度尽量地把每个细节都抠出来,因此在大佬看来可能会以为很简单,文章可能不适合大佬食用~git

写在前面

笔者的 Element-ui 源码的解析不会涉及它的 css 样式,这里我在项目中下载了 Element-ui,只引入了它的样式,其他的部分由本身来写,而后将组件引入到本身的页面中。程序员

我会更加的专一于其中应用到的 Vue 语法、代码风格以及其中的小技巧,以达到更好的增强本身的 Vue 和 JS 功底,为未来解构 Vue 的源码作一个缓冲~github

<el-alert>

在开始以前看看效果web

效果图
<el-alert>效果图

组件设计的精髓中复用性是不可不提的,Element-ui 的 <el-alert>组件能够说是复用性能很好,可以适用于大部分的消息提示场景。接下来咱们就来解构它的源码,经过源码来增强对 Vue 的一些学习。element-ui

结构设计

组件都是有一个通用的结构,经过 Vue 的动态绑定和 slot 等等语法达到千人千面,实现组件复用的效果,咱们先来解析它的模板结构数组

<template>
  <div>
    <!-- ElAlert -->
    <transition name="el-alert-fade">
      <div
        class="el-alert"
        :class="[ typeClass, center ? 'is-center' : '', 'is-' + effect ]"
        v-show="visible"
        >
        <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">
            <!-- {{title}} -->
            <!-- 属性和具名插槽两种方式显示 -->
            <slot name="title">{{title}}</slot>
          </span>
          <!-- 经过 slot:default 显示 description -->
          <p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
          <!-- 经过属性显示 description -->
          <p class="el-alert__description" v-if="!$slots.default && description">{{ description }}</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>
  </div>
</template>
复制代码

在代码之中的一些可能会有点复杂的地方我也写上了详细的注释,下面咱们一块儿来看看可以从这里能学到些什么东西。app

代码风格

首先第一个,抛开全部的语法,咱们能够学到的就是一个代码的书写习惯和风格,要适当的用空格和回车,可使代码更加的清晰,对本身和别人都更友好。编辑器

其次就是类名的命名规范,尽量的清晰明了,Element-ui 的源码中 css 方面使用了 BEM 的命名规范---Block(块)、Element(元素)、Modifier(修饰符)这样有助于咱们更好的去理解每一个类名的做用。

动态类名

这里用到了比较多的动态类名,平时咱们可能写动态类名更多的是直接使用:class=""这样的一个形式,可是这里有几个地方可能对于新手来讲有点疑惑的地方,好比:

:class="[ typeClass, center ? 'is-center' : '', 'is-' + effect ]"
:class="[ isBoldTitle ]" v-if="title || $slots.title"

这两个地方的动态类名使用了 [] 和 {},缘由是由于要绑定多个动态类名,而且可能要对类名进行必定的修改和根据命名规范格式化,使用 [] 和 {} 能够一次性的作完这些事情,更加的方便。这在咱们平时写 demo 之类的情境下可能用的会稍微少一点,因此我也拎出来说一下。

在 Element-ui 中动态类名还有一种巧妙的用法,后面我会讲到。

提供多种选择

Element-ui 之因此这么受欢迎,在看完源码以后我以为除了它很好用以外,还有一点就是它对于用户比较友好,给用户提供了多种选择。咱们从源码这两个地方来看看:

<span class="el-alert__title" :class="[ isBoldTitle ]" v-if="title || $slots.title">
  <!-- 属性和具名插槽两种方式显示 -->
  <slot name="title">{{title}}</slot>
</span>
复制代码
<!-- 经过 slot:default 显示 description -->
<p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
<!-- 经过属性显示 description -->
<p class="el-alert__description" v-if="!$slots.default && description">{{ description }}</p>
复制代码

这里我在代码中也给出了详细注释,title 和 description 经过 v-if 的判断,能够实现属性传值和插槽填充两种方式显示到页面上,这对于使用它的人来讲比较便利。

可能有人看到了我在 desctiption 的显示后面有一段注释的代码:

<!-- <p class="el-alert__description" v-if="description || $slots.default">{{ description }}</p> -->
复制代码

这段代码是我在看到 title 的显示方式控制只用一个 v-if 判断就作到了,因此想本身尝试也只用一个 v-if 来实现效果,惋惜的是这条语句并不能达到想要的效果,缘由是 description 的插槽是默认插槽,经过 $slots.default 并不能判断到它,可是若是你换成下面这样的格式就能够实现了:

<p class="el-alert__description" v-if="description || $slots.description"><slot name="description">{{ description }}</slot></p>
复制代码

这样虽然能够实现效果,可是你增长了一个具名插槽,Element-ui 官方不这么作确定是有本身相应的考虑吧。

数据设计

看完告终构,下面来看数据和方法部分的设计。老规矩,先上代码看看:

<script>
// 常量 
const TYPE_CLASS_MAP = {
  'success': 'el-icon-success',
  'warning': 'el-icon-warning',
  'error': 'el-icon-error'
}
export default {
  name: 'ElAlert',
  props: {
    type: { // 类型
      type: String,
      default: 'info' // 默认值
    },
    title: { // 标题
      type: String,
      default: ''
    },
    description: {
      type: String,
      default: ''
    },
    center: Boolean,
    effect: { // 提供的主题
      type: String,
      default: 'light',
      validator: function(value) { // 验证 effect 的值必须二选一
        // 其中之一
        return ['light', 'dark'].indexOf(value) !== -1;
      }
    },
    showIcon: Boolean,
    closeText: {
      type: String,
      default: ''
    },
    closable: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      visible: true
    }
  },
  computed: {
    // 计算属性
    typeClass() {
      return `el-alert--${ this.type }`
    },
    iconClass() {
      return TYPE_CLASS_MAP[this.type] || 'el-icon-info'
    },
    isBigIcon() {
      return this.desciption || this.$slots.default ? 'is-big' : ''
    },
    isBoldTitle() {
      return this.description || this.$slots.default ? 'is-bold' : ''
    }
  },
  methods: {
    close() {
      this.visible = false;
      // 给父组件传递一个 close 方法,便于在用户关闭提示的时候进行一些操做
      this.$emit('close')
    }
  }
}
</script> 复制代码

computed 计算类名

先来补上以前留下的动态类名的坑,在这里有四个类名是经过 computed 属性来动态肯定的:
typeClassiconClassisBigIconisBoldTitle
computed 属性的使用场景大体是下面这样:

  1. 某个数据受多个数据影响
  2. 须要对其余数据进行 JS 处理再显示

使用 computed 来计算类名的好处是能够不用在 data 中添加相应的数据,而且能够根据实际状况来改变类名的形式,进行动态改变或是根据规范格式化。固然这里不能直接放在 :class 中,由于判断比较复杂,并且 :class 也没法彻底知足这里的需求。

数据的巧妙设计

在这里的数据有一个比较巧妙地设计,那就是常量。

// 常量 
const TYPE_CLASS_MAP = {
  'success': 'el-icon-success',
  'warning': 'el-icon-warning',
  'error': 'el-icon-error'
}
复制代码

由于这里 icon 地类名是固定不变的,它是一个可遍历的同一类型的数据,而且要在多个地方使用,在使用过程当中也不会去改变它,因此能够考虑做为常量来使用。这里和大文件上传过程当中将文件上传状态做为常量对象来使用是一样的道理,能够增强代码的可维护性,作到一改全改。

props 接收验证

咱们在业务要求不是很高的状况下父子组件用 props 通讯传值,通常是写成数组的格式,而这里使用了对象的形式,在这里使用对象形式能够实现这些方面的功能:

  1. 声明类型进行检验
  2. 声明 validate 方法进行检验

使用数组的那种方式只能是知足一半的业务场景接收数据,比较的粗线条,并不能对数据进行太多的校验。

方法设计

methods: {
  close() {
    this.visible = false;
    // 给父组件传递一个 close 方法,便于在用户关闭提示的时候进行一些操做
    this.$emit('close')
  }
}
复制代码

在这里的数据源中还设置了一个 visible 属性,方便用户能够关闭这个提示框组件。这里方法的设计考虑的也是很全面,在点击关闭的时候使用 $emit 给父组件提交一个 close 事件,父组件就能够经过 @close 进行绑定一个事件,实现用户在关闭提示的时候进行一些业务操做。

注意的点

在本身写 Element-ui 组件的时候若是是和我同样在 App.vue 中使用必定要注意把 #app 的样式去掉,否则会有样式冲突致使样式显示不正确。

写在最后

正所谓不看源码的程序员不是好程序员,若是感受本身达到了一个瓶颈阶段,看源码是一个很好的选择。本文可能对于不少人来讲可能比较的基础,技术含量也不是很高,可是我会不断的深刻 Element-ui 的源码学习,把其中更多的知识点挖掘出来,以便也能使本身有所提高。这个系列我也会持续的更新下去,若是你以为对你有帮助的话,欢迎点赞和在评论区和我交流~

我把个人学习记录都记录在了个人 github 而且会持续的更新下去,有兴趣的小伙伴能够看看~
github

相关文章
相关标签/搜索