Element组件源码研究-Layout,Link,Radio__Vue.js
发布于 4 年前 作者 banyungong 1495 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

本文的研究思路是通过阅读Element源码,然后自动动手一步一步编写组件,完善其对应功能。

Layout布局

研究过Input和Button之后,再来看看Element布局的实现。看看有什么值得我们学习的。

Element是通过24 分栏,迅速简便地创建布局。实现原理是利用百分比,将一行视为宽度100%,等分为24份。

  1. 如果你的列占:span=24。那么样式为.el-col-24 { width: 100%; }。
  2. 如果你的列占:span=12。那么样式为.el-col-12 { width: 50%; }。
  3. 如果你的列占:span=8。那么样式为.el-col-8 { width: 33.3333%; }。依次类推。

原理就是根据span属性控制col组件样式。

详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/layout/

Link 文字链接组件

相比于Button组件,Link组件的逻辑就简单的多了。区别于原生a标签,增加了type, disable属性,还支持下划线的控制,美化了样式。

这里可以稍微总结下Element封装组件的思路。

一是扩展了原生标签的功能。使一些常用的功能通过属性可以直接支持。比如Link的禁用。配置个disable属性就可以达到。若我们用原生标签a来写的话,需要自己写些逻辑。

二是提供了统一的视觉风格。使我们靠堆积组件就能有不错的视觉效果。如Button、Link都有固定几种type类型。展示主要、成功、信息、警告、危险等反馈给用户。

详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/link/

Radio 单选框

继续我们的研究之旅,看看Radio扩展原生了哪些功能。

原生radio需要有相同的name,才能达到单选功能。

<form>
你更喜欢哪种颜色?<br>
<input type="radio" name="colors" checked id="red">红色<br>
<input type="radio" name="colors" id="blue">蓝色<br>
<input type="radio" name="colors" id="green">绿色
</form>

原生radio通过checked属性来标识选没选中。Element的radio的方式完全不一样,让多个radio的v-model绑定同一个属性,当属性的值与label相同时,该项就选中了,其他项因为v-model的值跟lablel不同,就不被选中。

<el-radio v-model="radio" label="1">备选项</el-radio>
<el-radio v-model="radio" label="2">备选项</el-radio>

最基本的实现

实现最基本的功能,有新意的地方是computed的model属性,使用get/set来存取值,近而控制原生radio的checked选中不选中:

<template>
    <label class="el-radio"
        :class="[
            { 'is-checked': model === label }
        ]"
    >
        <span class="el-radio__input"
        :class="{
            'is-checked': model === label
        }"
        >
            <span class="el-radio__inner"></span>
            <input
                ref="radio"
                class="el-radio__original"
                :value="label"
                type="radio"
                v-model="model"
                @change="handleChange"
            >
        </span>
        <span class="el-radio__label" @keydown.stop>
          <slot></slot>
          <template v-if="!$slots.default">{{label}}</template>
        </span>
    </label>
</template>


<script>
export default {
    name: 'ElRadio',

    props: {
      value: {},
      label: {},
    },
    computed: {
        model: {
            get() {
                return this.value;
            },
            set(val) {
                this.$emit('input', val);
                this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
            }
        },
    },

    methods: {
      handleChange() {
        this.$nextTick(() => {
          this.$emit('change', this.model);
        });
      }
    }
}
</script>

效果如下:

禁用状态

为外层标签添加禁用样式{ ‘is-disabled’: disabled },为input[‘radio’]标签绑定:disabled="disabled"即可。

效果如下:

单选框组

为每个el-radio绑定v-model略显麻烦,所以增加了一个el-radio-group。将v-model绑在其上。这将会是个有意思的实现。

<template>
  <el-radio-group v-model="radio">
    <el-radio :label="3">备选项</el-radio>
    <el-radio :label="6">备选项</el-radio>
    <el-radio :label="9">备选项</el-radio>
  </el-radio-group>
</template>

<script>
  export default {
    data () {
      return {
        radio: 3
      };
    }
  }
</script>

先来实现一个基本的el-radio-group:

<template>
    <div    
        class="el-radio-group"
        role="radiogroup"
    >
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'ElRadioGroup',
    componentName: 'ElRadioGroup',

    props: {
      value: {},
    }
}
</script>

非常简单,用value来接收v-model的值。用componentName标识自己,方便el-radio寻找即可。重头戏还在el-radio的实现上。

首先实现一个广播器,就是可以让事件一层层的冒泡上去,具体代码在这里不贴,可以看我后续传到码云上的源码。然后我们用mixin引入它。

import Emitter from '../../mixins/emitter'
...
mixins: [Emitter],

更改model的get/set:

model: {
    get() {
        return this.isGroup() ? this._radioGroup.value : this.value;
    },
    set(val) {
        if (this.isGroup()) {
          this.dispatch('ElRadioGroup', 'input', [val]);
        } else {
          this.$emit('input', val);
        }
        this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
    }
},
...

isGroup() {
    let parent = this.$parent;
    while (parent) {
      if (parent.$options.componentName !== 'ElRadioGroup') {
        parent = parent.$parent;
      } else {
        this._radioGroup = parent;
        return true;
      }
    }
    return false;
},

通过isGroup判断当前el-radio是否在el-radio-group中。如果在,model的get就取el-radio-group的value,set就利用广播器的dispatch方法触发el-radio-group的input事件,达到改变用户在el-radio-group的v-model绑定属性的值的目的。

带有边框

这个就是样式的添加。根标签class添加{ ‘is-bordered’: border }即可。 效果如下:

按钮样式

按钮样式的radio,Element写了一个新组件radio-button。基本逻辑与radio组件类似。下面贴出代码:

<template>
    <label class="el-radio-button"
        :class="[
            { 'is-active': value === label },
            { 'is-disabled': disabled },
            { 'is-focus': focus }
        ]"
    >
        <input
            class="el-radio-button__orig-radio"
            :value="label"
            type="radio"
            v-model="value"
            @change="handleChange"
            :disabled="disabled"
            >
        <span
            class="el-radio-button__inner"
            @keydown.stop>
            <slot></slot>
            <template v-if="!$slots.default">{{label}}</template>
        </span>
    </label>
</template>


<script>
import Emitter from '../../mixins/emitter'
export default {
    name: 'ElRadioButton',
    mixins: [Emitter],
    props: {
      label: {},
      disabled: Boolean,
    },
    computed: {
        value: {
            get() {
                return this._radioGroup.value;
            },
            set(value) {
                this._radioGroup.$emit('input', value);
            }
        },
        _radioGroup() {
            let parent = this.$parent;
            while (parent) {
                if (parent.$options.componentName !== 'ElRadioGroup') {
                    parent = parent.$parent;
                } else {
                    return parent;
                }
            }
            return false;
        },
    },

    methods: {
      handleChange() {
        this.$nextTick(() => {
          this.$emit('change', this.model);
        });
      },
    }
}
</script>

效果如下:

详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/radio/

其他组件源码研究:

Element组件源码研究-Button

Element组件源码研究-Input输入框

Element组件源码研究-Layout,Link,Radio

Element组件源码研究-Checkbox多选框

Element组件源码研究-InputNumber 计数器

Element组件源码研究-Loading组件

Element组件源码研究-Message组件

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 猎户座小陈 原文链接:https://juejin.im/post/6854573219660496909

回到顶部