跳转到内容

优先级A规则:不可或缺的

这些规则有助于防止错误,所以无论如何都要学习和遵守它们。例外情况可能存在,但应该非常罕见,并且只有那些对JavaScript和Vue都有专业知识的人才能做出。

使用多词组件名称

用户组件名称应始终为多词,除非是根App组件。这防止与现有和未来的HTML元素冲突,因为所有HTML元素都是单词。

错误

template
<!-- in pre-compiled templates -->
<Item />

<!-- in in-DOM templates -->
<item></item>

正确

template
<!-- in pre-compiled templates -->
<TodoItem />

<!-- in in-DOM templates -->
<todo-item></todo-item>

使用详细的属性定义

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

详细解释

详细的属性定义有两个优点

  • 它们记录了组件的API,因此可以很容易地看到组件应该如何使用。
  • 在开发过程中,Vue会在组件提供格式不正确的属性时警告你,帮助你捕获潜在的错误来源。

错误

js
// This is only OK when prototyping
props: ['status']

正确

js
props: {
  status: String
}
js
// Even better!
props: {
  status: {
    type: String,
    required: true,

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

错误

js
// This is only OK when prototyping
const props = defineProps(['status'])

正确

js
const props = defineProps({
  status: String
})
js
// Even better!

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

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

使用带键的v-for

keyv-for在组件中总是必须的,以便维护子树内部组件状态。即使对于元素,维护可预测的行为也是好的实践,例如对象一致性在动画中。

详细解释

假设你有一个待办事项列表

js
data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Learn to use v-for'
      },
      {
        id: 2,
        text: 'Learn to use key'
      }
    ]
  }
}
js
const todos = ref([
  {
    id: 1,
    text: 'Learn to use v-for'
  },
  {
    id: 2,
    text: 'Learn to use key'
  }
])

然后你按字母顺序排序。当更新DOM时,Vue将优化渲染以执行最便宜的DOM突变。这可能意味着删除第一个待办事项元素,然后将其添加到列表的末尾。

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

根据我们的经验,最好始终添加唯一的键,这样你和你的团队就永远不会担心这些边缘情况。然后在极少数性能关键的场景中,当对象一致性不是必需的时,你可以有意识地例外。

错误

template
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

正确

template
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

避免在v-for中使用v-if

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

有两种常见情况可能会让人想这么做

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

  • 为了避免隐藏列表时渲染列表(例如,v-for="user in users" v-if="shouldShowUsers")。在这些情况下,将v-if移动到容器元素(例如ulol)。

详细解释

当Vue处理指令时,v-if的优先级高于v-for,所以这个模板

template
<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)
})
template
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

或者,我们可以使用带有v-for<template>标签来包装<li>元素

template
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

错误

template
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

正确

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

使用组件作用域样式

对于应用来说,顶级App组件和布局组件中的样式可以是全局的,但所有其他组件都应该始终具有作用域。

此内容仅适用于单文件组件。它不要求使用scoped属性。作用域可以通过CSS模块、基于类的策略,如BEM,或其他库/约定来实现。

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

这使得覆盖内部样式更加容易,使用人类可读的类名,这些类名不具有过高的特定性,但仍非常不可能产生冲突。

详细解释

如果你正在开发一个大型项目,与其他开发者合作,或者有时包含第三方HTML/CSS(例如来自Auth0),一致的作用域将确保你的样式仅应用于它们应该应用的组件。

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

错误

template
<template>
  <button class="btn btn-close">×</button>
</template>

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

正确

template
<template>
  <button class="button button-close">×</button>
</template>

<!-- Using the `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

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

<!-- Using CSS modules -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
template
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- Using the BEM convention -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
优先级A规则:基本已加载