el-select使用index做key遇到的问题

1,064 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

前言

最近在写一个表单页面,需要使用到el-select,然后使用了index做key,遇到了问题,通过这篇文章记录下。

问题描述

正常我们使用el-select,是使用index做key是没有问题的,比如:

  <el-select v-model="value" placeholder="请选择">
    <el-option
      v-for="(item,i) in options"
      :key="i"
      :label="item.label"
      :value="item.value">
    </el-option>
  </el-select>
  
  data () {
   return {
      options: [{
        value: '选项1',
        label: '黄金糕'
      }, {
        value: '选项2',
        label: '双皮奶'
      }],
      value: ''
   }
  }

这样使用是没有问题的,因为你并没有对options做增加或者删除的操作。就不会影响到options重新渲染的问题。

但是我们这边有个需求,需要支持搜索,而且需要支持可以搜label,也可以搜value。

我们都知道,el-select有个filterable属性,但是只支持搜索label。所以我们得另辟蹊径。

继续看文档,我们看到有个filter-method方法

image.png

它配合filterable一起使用,当你搜索的时候,会触发filter-method方法,然后把搜索的内容当作参数传入。

所以,我们可以通过自定义搜索方法来实现。

我们需要先定义个源数据sourceOptions和展示的options,然后在filter-method方法对源数据进行过滤,赋值给options

代码如下:

<template>
  <el-select 
  v-model="value" 
  filterable 
  placeholder="请选择" 
  :filter-method="filterMethod">
    <el-option 
      v-for="(item,i) in options" 
      :key="i" 
      :label="item.label" 
      :value="item.value"></el-option>
  </el-select>
</template>
<script>
export default {
  data () {
    return {
      value: '',
      options: [],
      sourceOptions: [{
        value: '选项1',
        label: '黄金糕'
      }, {
        value: '选项2',
        label: '双皮奶'
      }, {
        value: '选项3',
        label: '蚵仔煎'
      }],
    }
  },
  methods: {
    filterMethod (query) {
      this.options = this.sourceOptions.filter(item =>
        item.label.includes(query) || item.value.includes(query)
      )
    }
  },
  mounted () {
    this.options = this.sourceOptions
  }
}
</script>

没花多少时间就写好了,但是一测试,就发现问题了。

输入“双”的时候,option是正常的,只过滤出双皮奶这个选项。但是,输入框的value被清空了。

image.png

我一开始以为filter-method的方法有问题,但是官方例子却没有问题。

那就奇了怪了。

思索无果,对比了二者代码,才发现,原来我是用了index做key,而官方文档使用了value做key。

顿时恍然大悟。

因为vue是使用key来决定是否渲染元素的,如果key是一样的,就会复用,不一样就会重新渲染。

因为我这里使用了index做key。导致了输入“双”的时候,双皮奶排第一了,index为0。vue认为和没输入时的index=0(黄金糕)是一样的,组件会复用。

但是因为option的value不一样,导致触发了value的watch。

源码的option.vue, 触发了select组件的setSelected的方法。

image.png

此时this.value是空字符串,导致this.selectedLabel也是空字符串。导致输入框的value被清空了。

image.png

image.png

最终我换成value做key就正常了。组件会复用,而且option的value是一样的,所以它不会触发了value的watch。

代码如下:

<el-select 
  v-model="value" 
  filterable 
  placeholder="请选择" 
  :filter-method="filterMethod">
  <el-option 
   v-for="item in options" 
   :key="item.value" 
   :label="item.label" 
   :value="item.value">
   </el-option>
</el-select>

切记:如果你的options可能会改变的时候,不要用index做key。