element-ui源码解读摘录

69 阅读2分钟

二次封装组件时,$attrs和$listeners很有用

//使用时,外部属性和方法可以和使用el-image时一致,内部没对应的props属性 都在$attrs里
//$listeners 包含了父作用域中的 (不含 `.native` 修饰器的) `v-on` 事件监听器
<template>
  <div id="CustomImage">
    <el-image v-bind="$attrs" v-on="$listeners">
      <div slot="error" class="image-slot">
        <img :src="require('image-f/icon-empty-img.png')" alt="图片加载失败.png"/>
      </div>
      <div slot="placeholder" class="placeholder-slot">加载中...</div>
    </el-image>
  </div>
</template>

vue2组件设计的一些技巧

  • 以下为研究elementui源码 摘出的

动态类名

//数组中 每一项都是js代码,产出字符串/vue规定的对象值为true 即赋予
:class="[      type ? 'el-button--' + type : '',      buttonSize ? 'el-button--' + buttonSize : '',      type ? `el-link--${type}` : '',      {        'is-disabled': buttonDisabled,        'is-loading': loading,        'is-plain': plain,        'is-round': round,        'is-circle': circle      }    ]"

插槽外层标签元素是否存在

    <span v-if="$slots.default">
      <slot></slot>
    </span>

检测是否传入某个props

Object.prototype.hasOwnProperty.call(this.$options.propsData,'disabled')

provide inject保持响应式(推荐传对象)

image.png 摘自:cdn链接

一行多个重复元素 中间需要间距

.el-button + .el-button {
  margin-left: 10px;
}

引入css

  • 官网建议 main.js 引入 import 'element-ui/lib/theme-chalk/index.css';
  • 实际上,该文件是把所有组件的css全堆到一个文件里(不是@includes引入 是css源码堆一块)13459行

line-height: 1;

  • 设置完font-size后 若想行高和font-size一致 如此便可,没必要写确切的px数值

不确定对象是否有某属性

  • (this.elFormItem || {}).elFormItemSize
  • this.elFormItem?.elFormItemSize (推荐) 和上面效果一样,若this.elFormItem不存在,返回的是undefined

事件发送与监听

两个基础概念

  1. 组件内$emit,使用组件时 @事件名="func"
  2. eventbus 借助同一个vue实例,一个在上面$emit 另一个在上面$on监听
  • 面对的情况
    • 单选是通过插槽的方式在单选组内的,把单选里触发的事件暴露为单选组的事件
//radio-button
 <input v-model="value"/>

 computed: {
    value: {
        get() {
          return this._radioGroup.value;
        },
        set(value) {
       //和 ElRadioGroup 组件内部this.$emit一样
          this._radioGroup.$emit('input', value);
        }
    },
  _radioGroup() {
      let parent = this.$parent
      while (parent) {
        // 往外层遍历,直到外层组件为ElRadioGroup 或者没有外层为止
        if (parent.$options.componentName !== 'ElRadioGroup') {
          parent = parent.$parent
        } else {
          return parent
        }
      }
      return false
    },
  },

实际上 有两个地方都能监听到

  1. 使用ElRadioGroup组件时,<el-radio-group @input="func">
  2. ElRadioGroup组件内部,mounted里用 this.$on('input',(val)=>{})

其实组件$emit时,组件本身也会成为eventbus的载体,插槽里的子组件向祖先传值 也可以考虑这种方法。element是直接把事件

element封装的跨组件传事件的方法

- src/mixins/emitter.js
// 向后代组件 传事件并携带参数
function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    var name = child.$options.componentName;

    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
    // 向上逐级找componentName匹配的,并用匹配到componentName的组件emit事件
    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));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

使用模式

  • broadcast 向后代发消息
祖先
import Emitter from '@/mixins/emitter.js'

export default {
   mixins: [Emitter],
   methods: {     //通用的往后代发事件的方法
        sendToSon() {
          this.broadcast('HzItem', 'ancestorSound', '俺是恁祖宗')
        },
-----------------------------------------------
后代
  mounted() {
    this.$on('ancestorSound', this.ancestorSound)
  },
  methods: {
      ancestorSound(val) {
          alert(val, '子组件收到的信息')
      },
  • dispatch向祖先发消息
祖先
//radio-group组件
//该组件使用时 <radio-group @handleChange="func">也能触发
    created() {
    //可以把祖先组件当成eventbus载体 $on监听该事件,也可以直接在外面监听该事件
      this.$on('handleChange', value => {
        this.$emit('change', value);
      });
    },
-------------------
后代
//radio-button组件
import Emitter from '@/mixins/emitter.js'
mixins: [Emitter],
    methods: {
      handleChange() {
        this.$nextTick(() => {
        //该方法是借助找到的 祖先vue实例.$emit('handleChange',this.value)
          this.dispatch('ElRadioGroup', 'handleChange', this.value);
        });
      }
    }

element设计该事件传值模式,其实是基于eventbus,但 实例.$emit 既可以是借助该实例搞eventbus,也可以是给该组件暴露一个事件。所以两种方式都能用

补充

  • call和apply主要差异。第二个参数开始是给函数传参,call为函数的每个参数,apply为参数组成的数组
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");
------------------------------------------------------------
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.apply(person1, ["Oslo", "Norway"]);