Vue2.0中嵌套v-for结构的第二个key为什么总是报错?

西木
  • 8

使用uni-app写小程序,总体上是以下结构,vscode总是报 绑定的key期望是v-for中定义的变量
但这个index就是从v-for中遍历出来的,改为item也不行。
注:我知道可以去掉错误提示,但是我希望弄明白这是为什么出现这样的问题,该如何改。

<template v-for="(propItem, propIndex) in ['str1','str2','str3']">
  <view :key="propIndex"></view>

  <template v-for="(item, index) in [0,1,2,3,4]">
    <view
      :key="index"
      v-if="item===4"
    ></view>
  </template>
</template>

image.png

image.png

回复
阅读 667
3 个回答

也许思否上活跃的用户太少了,也许也和提问时间太短有关系,我并没有在思否上找到我想要的答案。不过我在Stack Overflow找到了和我几乎完全一致的问题。

首先我想声明,埋怨并修改eslint配置问题不是我所期望的解决办法,既然eslint报错了,那么就意味着代码的写法或者风格不符合规范,而我所探寻的就是怎么写才是符合规范的。

可以看到下图报错的是eslint-plugin-vue这个插件,在vscode中可以找到相关的配置。
image.png
image.png
如果你只是追求关闭烦人的错误提示,那么关掉Vetur的这个设置即可。但正如我所说,关掉它不是我所期待的解决方法。

那么来看正确的解决办法吧。
它是我准备在Stack Overflow上提问题时发现的已存在问题Expected v-bind directive on complex <template> / v-for

可以看到该问题中,提问者的代码结构与我完全一致

<template v-for="(scorecard, scorecardIndex) in scorecards">
    <template v-for="(property, propertyIndex) in properties">
        <tr v-if="scorecardIndex === 0"
            v-bind:key="propertyIndex">
        </tr>
    </template>

    <tr v-if="scorecardIndex > 0"
        v-bind:key="scorecardIndex">    
    </tr>
</template>

我删掉了一些不相干的内容,留下的核心的结构。我们都是两层v-for,并且两层v-for都在<template>标签上。

首先key是不能在<template>标签上绑定的,因为它不是实际被渲染到dom上的内容,只接受控制属性。所以key的绑定规范是绑定到<template>标签下一级的dom结构上,如果<template>标签下有多个同级结点,那么每一个同级结点都要绑定该<template>标签v-for中定义的变量的key。

注:使用index如上述与另一个回答者讨论的一样,如果是简单的不会变更的列表渲染可以使用index或者直接使用遍历出来的内容(例如我代码中的是字符串数组遍历出来的就是字符串)。

key的绑定规范如上所述,所以第二个<template>标签下的内容绑定key也需如此。以我的代码为例,注意看注释

<template v-for="(propItem, propIndex) in ['str1','str2','str3']">
  <view :key="propIndex"></view>

  <template v-for="(item, index) in [0,1,2,3,4]">
    <!-- 下面这个view标签(也就是div标签)不仅属于第一个<template>标签的下一级标签 -->
    <!-- 同时也属于第二个<template>标签的下一级标签,所以它需要同时绑定两个v-for中定义的变量 -->
    <view
      :key="index"
      v-if="item===4"
    ></view>
  </template>
</template>

按照回答者的原话

You need to make sure that the key of the first <tr> uses variables defined by both v-fors.

这个<tr>是Stack Overflow上提问者的第二个<template>标签下的内容,也是我上述代码中注释下的那个<view>标签(<div>标签)

所以这个问题正确解决办法是,同时绑定两个v-for中定义的变量,如下所示(以我的代码为例)

<view :key="propIndex+index"></view>

其实怎样绑定也行,只要这个key值,与两个v-for中的变量有关即可。

回顾这个问题,其实它并不只发生在两个v-for在两个<template>标签上。
如下代码

<template v-for="item1 in ['str1','str2','str3']">
  <view :key="item1"></view>
  <view v-for="item2 in [1,2,3]" :key="item2"></view>
</template>

image.png
可以看到它仍然报了这个问题的错误,正确应该是

<template v-for="item1 in ['str1','str2','str3']">
  <view :key="item1"></view>
  <view v-for="item2 in [1,2,3]" :key="item1+item2"></view>
</template>

至此,问题解决完毕


更新
因为我是使用uni-app开发时发现的这个问题,以上的方法,可以解决h5端,也就是正常web页面可以正常使用。但是使用uni-app编译到非H5端时,uni-app会报key不能绑定复杂表达式的问题导致小程序上会出现warning,非常难受。
image.png
image.png
可以通过定义一个方法返回字段值的方法解决
image.png
image.png

vue建议不要用index作为key吧,相同key的项会复用,容易造成性能问题,最好使用id等唯一标识

先给题主的求知精神点赞。

但这个问题其实跟 template 关系不大,template 只是会干扰你观察实际的层级结构而已。跟 v-for 嵌套其实也没什么关系。

真正报错的原因其实是兄弟节点的 key 应该是不同的

比如你要是这么写:

<div v-for="i in [1, 2, 3]" :key="i">
    {{ i }}
</div>
<div v-for="j in [1, 2, 3]" :key="j">
    {{ j }}
</div>

这没嵌套对吧?虽然 ESLint 识别不出来(这其实是个 Bug,有人提过了),但在运行时,你会在控制台得到一个警告:

image.png

所以说,压根就不应该用 index 当 key,这比没有 key 更差。

你知道吗?

宣传栏