知海

必要规则

Vue风格指南

必要规则 {#priority-a-rules-essential}

这些规则有助于防止错误,因此请始终学习并遵守它们。可能存在例外情况,但应当极为罕见,且必须由精通 JavaScript 和 Vue 的专家做出判断。

使用多词组件名称 {#use-multi-word-component-names}

用户组件名称应始终为多词,根 App 组件除外。这样可以避免与现有及未来的 HTML 元素发生冲突,因为所有 HTML 元素都是单词。

不推荐

vue-html 复制代码
<!-- 在预编译模板中 -->
<Item />

<!-- 在 DOM 内模板中 -->
<item></item>

推荐

vue-html 复制代码
<!-- 在预编译模板中 -->
<TodoItem />

<!-- 在 DOM 内模板中 -->
<todo-item></todo-item>

使用详细的 prop 定义 {#use-detailed-prop-definitions}

在提交的代码中,prop 定义应始终尽可能详细,至少指定类型。

::: details 详细说明
详细的 prop 定义 有两个优点:

  • 它们记录了组件的 API,因此很容易看出组件的预期使用方式。
  • 在开发环境中,如果向组件传递了格式不正确的 prop,Vue 会发出警告,帮助你捕获潜在的错误来源。

不推荐

js 复制代码
// 仅在原型设计时可以这样做
props: ['status']

推荐

js 复制代码
props: {
  status: String
}
js 复制代码
// 更好!
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

不推荐

js 复制代码
// 仅在原型设计时可以这样做
const props = defineProps(['status'])

推荐

js 复制代码
const props = defineProps({
  status: String
})
js 复制代码
// 更好!

const props = defineProps({
  status: {
    type: String,
    required: true,

    validator: (value) => {
      return ['syncing', 'synced', 'version-conflict', 'error'].includes(
        value
      )
    }
  }
})

v-for 设置 key {#use-keyed-v-for}

在组件上使用 v-for 时,key必须 的,以维护子树内部的组件状态。即使对于普通元素,这也是一种良好的实践,可以保持可预测的行为,例如动画中的对象恒定性。 details 详细说明
假设你有一个待办事项列表:

js 复制代码
data() {
  return {
    todos: [
      {
        id: 1,
        text: '学习使用 v-for'
      },
      {
        id: 2,
        text: '学习使用 key'
      }
    ]
  }
}
js 复制代码
const todos = ref([
  {
    id: 1,
    text: '学习使用 v-for'
  },
  {
    id: 2,
    text: '学习使用 key'
  }
])

然后你按字母顺序排序。在更新 DOM 时,Vue 会优化渲染以执行成本最低的 DOM 变更。这可能意味着删除第一个待办事项元素,然后在列表末尾重新添加它。

问题是,在某些情况下,不删除将保留在 DOM 中的元素非常重要。例如,你可能希望使用 <transition-group> 为列表排序添加动画,或者在渲染的元素是 <input> 时保持焦点。在这些情况下,为每个项目添加唯一的 key(例如 :key="todo.id")将告诉 Vue 如何更可预测地运行。

根据我们的经验,最好 始终 添加唯一的 key,以使你和你的团队无需担心这些边缘情况。然后在极少数对性能要求很高且不需要对象恒定性的场景中,可以有意识地做出例外。

不推荐

vue-html 复制代码
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

推荐

vue-html 复制代码
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

避免 v-ifv-for 同时使用 {#avoid-v-if-with-v-for}

永远不要在同一元素上同时使用 v-ifv-for

在以下两种常见情况下,可能会让人忍不住这样做:

  • 为了过滤列表中的项目(例如 v-for="user in users" v-if="user.isActive")。在这种情况下,请将 users 替换为一个新的计算属性,返回过滤后的列表(例如 activeUsers)。

  • 为了避免在列表应隐藏时渲染(例如 v-for="user in users" v-if="shouldShowUsers")。在这种情况下,请将 v-if 移动到容器元素上(例如 ulol)。 details 详细说明
    当 Vue 处理指令时,v-if 的优先级高于 v-for,因此这个模板:

vue-html 复制代码
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

会抛出错误,因为 v-if 指令会首先被求值,而此时迭代变量 user 尚不存在。

可以通过改为遍历计算属性来解决,像这样:

js 复制代码
computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
js 复制代码
const activeUsers = computed(() => {
  return users.filter((user) => user.isActive)
})
vue-html 复制代码
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

另外,我们可以使用带有 v-for<template> 标签来包裹 <li> 元素:

vue-html 复制代码
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

不推荐

vue-html 复制代码
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

推荐

vue-html 复制代码
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
vue-html 复制代码
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

使用组件作用域样式 {#use-component-scoped-styling}

对于应用,顶层 App 组件和布局组件中的样式可以是全局的,但所有其他组件应始终使用作用域样式。

这仅适用于单文件组件。它并_不_要求必须使用scoped 属性。作用域样式可以通过 CSS 模块、基于类的策略(如 BEM)或其他库/约定来实现。

然而,组件库应优先使用基于类的策略,而不是使用 scoped 属性。

这样可以更轻松地覆盖内部样式,使用具有可读性的类名,既不会有过高的特异性,又不太可能导致冲突。 details 详细说明
如果你正在开发一个大型项目、与其他开发人员合作,或者有时包含第三方 HTML/CSS(例如来自 Auth0),使用一致的样式作用域可以确保你的样式只应用于它们所针对的组件。

除了 scoped 属性之外,使用唯一的类名有助于确保第三方 CSS 不会应用于你自己的 HTML。例如,许多项目使用 buttonbtnicon 这样的类名,因此即使不使用类似 BEM 的策略,添加特定于应用和/或特定于组件的前缀(例如 ButtonClose-icon)也可以提供一些保护。
:::

不推荐

vue-html 复制代码
<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>

推荐

vue-html 复制代码
<template>
  <button class="button button-close">×</button>
</template>

<!-- 使用 `scoped` 属性 -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
vue-html 复制代码
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- 使用 CSS 模块 -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
vue-html 复制代码
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- 使用 BEM 约定 -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。