依赖注入

核心思想:让后代组件能够访问到祖先组件的内容。

  1. <el-select v-model="value" placeholder="请选择">
  2. <el-option
  3. v-for="item in options"
  4. :key="item.value"
  5. :label="item.label"
  6. :value="item.value">
  7. </el-option>
  8. </el-select>

el-select 中通过 provider 来提供变量,在子孙组件 el-option 中通过 inject 来注入变量

  1. provide() {
  2. return {
  3. 'select': this
  4. };
  5. }

select 组件直接将自己的实例 this 注入 option 组件,这样 option 组件则可以通过 this.select 来修改父组件属性

  1. inject: ['select']
  1. created() {
  2. this.select.options.push(this);
  3. }

option 组件的 created 生命周期中,将自己的实例 this 注入到 select 组件的 options 数组中

  1. selectOptionClick() {
  2. if (this.disabled !== true && this.groupDisabled !== true) {
  3. // emitter.js 文件通过 mixins 传进来方法 dispatch
  4. this.dispatch('ElSelect', 'handleOptionClick', [this, true]);
  5. }
  6. }

option 组件中通过 dispatch 把点击事件暴露给 select 组件

  1. handleOptionSelect(option, byClick) {
  2. if (this.multiple) {
  3. const value = (this.value || []).slice();
  4. const optionIndex = this.getValueIndex(value, option.value);
  5. if (optionIndex > -1) {
  6. value.splice(optionIndex, 1);
  7. } else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) {
  8. value.push(option.value);
  9. }
  10. this.$emit('input', value);
  11. this.emitChange(value);
  12. // ...
  13. } else {
  14. this.$emit('input', option.value);
  15. this.emitChange(option.value);
  16. this.visible = false;
  17. }
  18. // ...
  19. this.$nextTick(() => {
  20. this.scrollToOption(option);
  21. });
  22. }

在多选还是单选下进行不同的操作。this.$emit('input', value); 中传递改变的值就是 select 中绑定的 v-model 中的值 <el-select v-model="value">

下拉框的定位

引入了第三方库 popper.js,同时写了个 vue-popper.vue 来控制

参考资料

  1. ElementUI Select 源码微分析与功能增强 #4
  2. Element源码分析系列7-Select(下拉选择框)
  3. 【源码浅析】 ElementUI 的 Select 组件