发布于 2026-01-06 6 阅读
0

Vue.js模板变量(一种方法)

Vue.js模板变量(一种方法)

问题

有时,我需要在 Vue 模板中临时存储方法调用的结果。这种情况在循环中尤为常见,因为循环中我们无法轻易使用计算属性。

我们基本上想要避免的是这种情况:

<!-- List.vue -->
<ul>
  <li v-for="id in users" :key="id">
    <img :src="getUserData(id).avatar"><br>
    🏷️ {{ getUserData(id).name }}<br>
    🔗 {{ getUserData(id).homepage }}
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

通用解决方案

我们可以将这个问题描述为“带参数的计算属性”,目前已经有一些成熟的解决方案:

外包组件

比较规范的做法是通过重构:我们可以将这些<li>项目外包到它们自己的<ListItem>组件中。

该组件会接收一个idprop,并将相应的元数据存储在一个计算属性中,然后 Vue 会缓存该属性,直到需要重新评估为止。

<!-- List.vue -->
<ul>
  <ListItem v-for="id in users" :key="id" :id="id" />
</ul>

<!-- ListItem.vue -->
<li>
  <img :src="metadata.avatar"><br>
  🏷️ {{ metadata.name }}<br>
  🔗 {{ metadata.homepage }}
</li>
Enter fullscreen mode Exit fullscreen mode

然而,这种方法编写和维护起来相当繁琐:我们需要在每个列表项中传递的所有数据都必须作为<ListItem>props 传递下去。

对于读者来说,这种代码也很难理解——尤其是在<ListItem>组件非常小的情况下。它可能只包含四行模板代码,后面紧跟着 25 行 props 定义样板代码。

Memoize 方法结果

我们还可以将结果记忆化getUserData()

然而,这种方法实现起来也很繁琐,通常只适用于可序列化的输入数据——而且在所有方法中,在 Vue 之上再添加一层记忆化似乎最不符合 Vue 的方式™。

我的方法

对于我的项目,我喜欢使用另一种(不太明显,而且据我所知也不太常见的)方法:我创建一个辅助组件,我称之为<Pass>

它真的很小:

const Pass = {
  render() {
    return this.$scopedSlots.default(this.$attrs)
  }
}
Enter fullscreen mode Exit fullscreen mode

基本上,这是一个占位符组件,它本身不渲染 DOM 元素,而是将接收到的所有 props 传递给它的子组件。

那么,让我们用辅助函数重写列表<Pass>

<!-- List.vue -->
<ul>
  <Pass v-for="id in users" :key="id" :metadata="getUserData(id)">
    <li slot-scope="{ metadata }">
      <img :src="metadata.avatar"><br>
      🏷️ {{ metadata.name }}<br>
      🔗 {{ metadata.homepage }}
    </li>
  </Pass>
</ul>
Enter fullscreen mode Exit fullscreen mode

这只会执行getUserData()一次:<Pass>在渲染时。简洁明了,不是吗?

另外,这里有一个 CodeSandbox,你可以在其中尝试我描述的示例:

注意事项

坦白地说,这种方法也存在一些缺点:

  • 辅助组件使用作用域槽来传递数据。这意味着它<Pass>只能有一个子组件。
  • 这种方法的另一个局限性在于,注入到插槽中的标记必须渲染一个真正的 DOM 节点。我们不能仅仅slot-scope在类似 `<div>` 这样的元素上设置 `<div> <template>`。

就这样。希望这能帮助你简化 Vue 模板!

文章来源:https://dev.to/loilo/an-approach-to-vuejs-template-variables-5aik