[Source] ElementUI On the Select Components

Code structure

Element simulated using div select, select component containing navigation-mixin.js, option-group.vue, option.vue, select-dropdown.vue, select.vueand other documents

Which select.vueis similar to the structure of the main HTML file and select analog mainstream idea, but a little more complex Element, select the following components HTML structure (structure for clarity, omitted part of the code):

<div class="el-select">
  <!-- 多选的情况 -->
  <div v-if="multiple" class="el-select__tags">
    <span>
      <!-- 放置多选时的选中的tag,以tag展现,或者合并成一段文字 -->
    </span>
    <!-- 搜索功能 -->
    <input v-model="query" v-if="filterable" />
  </div>
  <!-- 单选的时候选中的值回显 -->
  <el-input v-model="selectedLabel" :class="{ 'is-focus': visible }">
    <!-- xxx -->
  </el-input>

  <!--下拉框-->
  <el-select-menu>
    <el-scrollbar v-show="options.length > 0 && !loading">
      <!-- 选项内容 -->
      <el-option :value="query" created v-if="showNewOption"> </el-option>
      <slot></slot>
    </el-scrollbar>
    <!-- options为空显示的默认文字或者select处于loading的情况 -->
    <template
      v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0 ))"
    >
      <!-- xxx -->
    </template>
  </el-select-menu>
</div>
复制代码

How to organize parent component and subassembly

On a code structure analysis time, readers may feel very select assembly complex, which is how the parent-child communication processing component of it? Before I analyzed its organizational methods Table component, using a simple store mode , select the components using broadcast / dispatch and inject / provide. (PS: broadcast is ElementUI own writing, vue 2.0 has been removed $dispatch 和 $broadcast)

inject 和 provide

Vue2.2.0 new API, this option needs to be used together to allow an ancestor component inject a dependency to all future generations, regardless of how deep the component level, and in the upstream and downstream relations since the establishment of the time is always effective. A word which encapsulates: ancestral components provided by the variable provider, and then injected through the assembly offspring variables inject.

I will analyze inject and provide ElementUI how and why you can not use the back this.$parentand this.$childrencommunicate with each other

dispatch and broadcast

broadcast code in the 'element-ui / src / mixins / emitter', mixins by mixing, use eg: this.broadcast('ElOption', 'queryChange', '');andthis.dispatch("ElSelect", "setSelected");

As a multi-line only the front end of a year, I did not used the broadcast, specific detailed Vue $dispatch 和 $broadcastDetailed

Although broadcast has its drawbacks, such as event-based flow mode component tree is really hard to understand, it does not solve the communication problems between the brothers components. But father and son nesting component by $dispatch 和 $broadcastdirectional event to a parent or subassemblies remote calls, avoiding the transfer by operation or use props refs call the method of component instance, it is very simple.

ElSelect and ElOption

If I write this component may be so use <my-select v-model="input" options="options" />that option Select a component by component passing options. Perhaps the last function can be achieved, but this package feels a bit overdone. From the structure of this component is concerned, and native select like much. We often use Select this component is not very intuitive:

<el-select v-model="input" filterable clearable placeholder="请选择">
  <el-option label="foo" value="foo"></el-option>
  <el-option label="foo" value="foo"></el-option>
</el-select>
复制代码

From the code structure can see, there are a select assembly of default slots, displayed no problem, but taking into account the need to select components and option components communicate with each other, such as option components need to know if select components multiple choice, whether you can search and so on, select the components you need to know the number of components and so on option, how to do it? The answer is to inject and provide

select components directly in its own instance thisinjected into the option component, option component by this.selectdirectly modifying the parent component properties:

provide() {
  return {
    'select': this
  };
}
复制代码

How many options on how to create el-optioninstance, look at el-optionthe createdlife cycle:

created() {
  //把 el-option实例push进父组件select的options
  this.select.options.push(this);
  this.select.cachedOptions.push(this);
  this.select.optionsCount++;
  this.select.filteredOptionsCount++;
  // 监听自定义的事件
  this.$on('queryChange', this.queryChange);
  this.$on('handleGroupDisabled', this.handleGroupDisabled);
}

复制代码

Some students might ask, this.select.options.push(this);how has been the push operation, if my option is uncertain, select.optionswould not have a lot of value. Do not worry, look at el-optionthe beforeDestroylife cycle

beforeDestroy() {
  this.select.onOptionDestroy(this.select.options.indexOf(this));
}
复制代码

Parent component select the onOptionDestroymethod, el-optiondestroyed, options parent component of which will be removed:

onOptionDestroy(index) {
  if (index > -1) {
      this.optionsCount--;
      this.filteredOptionsCount--;
      // cachedOptions 没有去除
      this.options.splice(index, 1);
  }
}
复制代码

Features

Alternatives group show

option-group.vueIn its HTML structure is very simple:

<ul class="el-select-group__wrap" v-show="visible">
  <li class="el-select-group__title">{{ label }}</li>
  <li>
    <ul class="el-select-group">
      <slot></slot>
    </ul>
  </li>
</ul>
复制代码

Grouping function is equivalent to a layer of el-option packet, then the time el-optioncomponent is equivalent to el-selectthe Sun component assembly, then el-option and el-select component can not pass through this.$parentthe communication of, for compatibility el-optionof components or sub-components of the Sun in both cases, injectand provideit is appropriate.

Local Search

In fact, local screening function, and therefore enable the local search capabilities need to pass filterablefield instead of searchablescreening function is really wonderful, have said before how many options on how to create el-optioninstances, so when we have to enable the screening time, and just need regular el-option does not match the examples hide it. Code is as follows (is not simple than expected):

queryChange(query) {
   // 因为select还可以手动创建条目,所以手动创建的条目一定显示
  this.visible =
    new RegExp(escapeRegexpString(query), 'i').test(this.currentLabel) ||
    this.created;
  if (!this.visible) {
    // 筛选的选项数目减一
    this.select.filteredOptionsCount--;
  }
}
复制代码

Remote search

Code section is so simple, because we know the remote search options are returned from the server, re-create a new el-optioncomponent can be.

if (this.remote && typeof this.remoteMethod === 'function') {
  this.hoverIndex = -1;
        this.remoteMethod(val);
  }
}
复制代码

select remote search component Echo

element-ui when your options are a fixed time, it will based on your selected value, echo corresponding label, but due to the remote search component optionsis not fixed, echo is a problem.

The solution is passed in the value of the selected optionsincoming, for example, I have a component ArticleSelect, I checked the id value of [1,2], if not treated, then this component will not echo. 1 and 2 show only two dry tag. But I can put the selected value options(such as the value [{value:1,label:'第一篇'},{value:2,label:'第二篇'}]passed in this assembly) to achieve echo display the title.

However, some people may ask, select components with the remote search keyword search options will not dynamically change it, why can? We look ElementUI select component settings selected value code:

setSelected() {
    // 省略不是多选的情况的代码
    // 多选
    let result = [];
    if (Array.isArray(this.value)) {
      this.value.forEach(value => {
        // 注意到这里是push操作,且getOption是从cachedOptions里面取的,(cachedOptions是被缓存的,不会因为el-option销毁而销毁)
        result.push(this.getOption(value));
      });
    }
    this.selected = result;
    // 设置完成之后重新计算选项框的高度
    this.$nextTick(() => {
      this.resetInputHeight();
    });
  }
复制代码

From the Code, Element set the selected value is a push operation, so the change will not affect the follow-up options I selected value, the perfect solution for my needs

Creating entries

Here is even more subtle, and I drop-down box and then show you the code structure:

<el-scrollbar v-show="options.length > 0 && !loading">
  <!-- 选项内容 -->
  <!-- 看到这个受 showNewOption 控制的option了嘛,他就是用来创建option -->
  <el-option :value="query" created v-if="showNewOption"> </el-option>
  <slot></slot>
</el-scrollbar>
复制代码

Computed attribute showNewOptioncode:

showNewOption() {
  let hasExistingOption = this.options.filter(option => !option.created)
    .some(option => option.currentLabel === this.query);
    // this.query为空的时候,因为v-if是真正的条件渲染,所以这个el-option组件又被销毁了,(没有手动去销毁哦,是不是很巧妙)
  return this.filterable && this.allowCreate && this.query !== '' && !hasExistingOption;
}
复制代码

to sum up

Element of select components a bit complicated, but the function implementation, especially the idea of ​​the structure of the code is really very delicate. As part of the CSS, not my strengths, not analyzed.

Reference article

Reproduced in: https: //juejin.im/post/5d01fce16fb9a07eb3097548

Guess you like

Origin blog.csdn.net/weixin_33806300/article/details/93164075