菜鸟读element源码6.5 el-radio

395 阅读3分钟

接前文

本来在 系列6尾部有说把 emitter.js中的混入函数拿出来讲一下,突然发现其实在 radio组件里面涉及不多,因此就单独根据 radio使用到的函数讲一下。 我模仿的代码都会放在该地址下,有兴趣看的同学可以点链接

正文

首先还是说一下 混入 的概念吧。 混入从名字不难理解说是 将什么东西混入到目标当中,(其实它就是这么个意思)。 比如说我们有多个页面需要用到一个函数,通常情况下我们会把这个函数单独抽离出来,在需要的页面import即可,其实混入也是差不多的。 那么为什么要有 混入这个东西存在呢? 比如说你需要这个公有的函数,并且在这个函数中需要操作当前模块的数据怎么办? 举个场景吧。 在工作中有这么一个场景,我需要把 excel文件导入进页面,并且显示出来(前端引入并处理,不经过后端)。这时候引入文件需要动态生成表头,数据。如果使用 import我们需要在这个 import引入的函数里处理好数据然后将数据return回去,然后模块内置其他函数接收处理的结果并操作。 但是使用混入的话,我们只需要

data () {
    return {
      head: [],
      value: []
    }
  },
  methods: {
      dealMethods () {...}
  }

我们可以直接将需要的数据和方法通过混入的方式导入页面即可。 并且 混入的数据,方法和本页面数据方法产生冲突的时候,会以当前页面的数据为主,混入的冲突数据和方法会被覆盖掉,这就减少了冲突的风险。 我们看一下在 radio中混入的方法

dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root
      var name = parent.$options.componentName
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent
        if (parent) {
          name = parent.$options.componentName
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
    },

radio不涉及到 form组件的情况下,触发的混入方法就是 dispatch 触发代码

model: {
      get() {
        // 如果是以el-radio-group包裹 则取group的value值
        return this.isGroup ? this._radioGroup.value : this.value
      },

      set(val) {
        console.warn(val)
        if (this.isGroup) {
          this.dispatch('ElTestRadioGroup', 'input', [val])
        } else {
          this.$emit('input', val)
        }
        this.$refs.radio && (this.$refs.radio.checked = this.model === this.label)
      }
    },

当我们点击某个 radio按钮的时候,如果radio位于radio-group中时,此时 this.isGrouptrue,则触发 dispatch函数。 dispatch会获取当前组件的父组件,并一直向上查询,直到没有父组件或者 父组件为ElTestRadioGroup(我的测试代码均在el后加test,官方的父组件为 ElRadioGroup), 如果找到,就触发 input事件,并通过 apply将参数带过去。

为什么使用apply

因为这样参数是以数组形式传递过去, 使用call返回的是参数列表, 而 bind则绑定指定对象,并返回该函数, applycall则会立即执行该函数。

radio-group源码

<template>
  <div
    class="el-radio-group"
    role="radioGroup"
    @keydown="handleKeyDown"
  >
    <slot></slot>
  </div>
</template>
<script>
import Emitter from "@/plugins/mixins/emitter.js";
// 上下左右键码
const keyCode = Object.freeze({
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40
});
export default {
  name: "ElTestRadioGroup",

  componentName: "ElTestRadioGroup",

  // 集成自el-form的属性
  inject: {
    elFormItem: {
      default: ''
    }
  },

  mixins: [Emitter],

  props: {
    value: {},
    size: String,
    fill: String,
    textColor: String,
    disabled: Boolean
  },

  computed: {
    _elFormItemSize() {
      return (this.elFormItem || {}).elFormItemsSize;
    },

    radioGroupSize() {
      return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
    }
  },

  created() {
    // 定义一个监听函数
    this.$on("handleChange", value => {
      this.$emit("change", value);
    });
  },

  mounted() {
    // 当radioGroup没有默认选项时,第一个可以选中Tab导航
    const radios = this.$el.querySelectorAll("[type=radio]");
    const firstLabel = this.$el.querySelectorAll("[role=radio]")[0];
    if (![].some.call(radios, radio => radio.checked) && firstLabel) {
      firstLabel.tabIndex = 0;
      console.warn(1)
    }
  },

  methods: {
    // 上下左右按键 触发radio
    handleKeyDown (e) {
      // 获取触发该事件的节点
      const  target = e.target;
      // 判断是否是原生input节点
      const className = target.nodeName === 'INPUT' ? '[type=radio]' : '[role=radio]'
      const radios = this.$el.querySelectorAll(className);
      const length = radios.length;
      const index = [].indexOf.call(radios, target)
      const roleRadios = this.$el.querySelectorAll('[role=radio]')
      switch (e.keyCode) {
        case keyCode.LEFT:
        case keyCode.UP:
        // (删除这两个事件好像没什么变化)
          // 阻止事件的传播 阻止该事件配分派到其他节点上 
          e.stopPropagation();
          //如果此事件没有被显式处理,那么它默认的动作也不要做(因为默认是要做的)。此事件还是继续传播,除非碰到事件侦听器调用stopPropagation() 或stopImmediatePropagation(),才停止传播
          e.preventDefault();
          if (index === 0) {
            roleRadios[length - 1].click();
            roleRadios[length - 1].focus();
          } else {
            roleRadios[index - 1].click();
            roleRadios[index - 1].focus();
          }
          break;
        case keyCode.RIGHT:
        case keyCode.DOWN:
          if (index === (length - 1)) {
            e.stopPropagation();
            e.preventDefault();
            roleRadios[0].click();
            roleRadios[0].focus();
          } else {
            roleRadios[index + 1].click();
            roleRadios[index + 1].focus();
          }
          break;
        default:
          break;
      }
    }
  },

  watch: {
    value () {
      this.dispatch('ElformItem', 'el.form.change', [this.value])
    }
  }
};
</script>

关于radio-group组件没有什么其他可以讲解的,在这里简单放出来,大家可以看一看。

总结

感觉这块没什么值得太大注意的 ,混入还是值得一学的。