外观
优先级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>
使用详细的属性定义
在提交的代码中,属性定义应尽可能详细,至少指定类型。
错误
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
key
与v-for
在组件中总是必须的,以便维护子树内部组件状态。即使对于元素,维护可预测的行为也是好的实践,例如对象一致性在动画中。
详细解释
假设你有一个待办事项列表
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-if
和v-for>。
有两种常见情况可能会让人想这么做
在列表中过滤项目(例如,
v-for="user in users" v-if="user.isActive"
)。在这些情况下,用返回过滤列表的新计算属性(例如activeUsers
)替换users
。为了避免隐藏列表时渲染列表(例如,
v-for="user in users" v-if="shouldShowUsers"
)。在这些情况下,将v-if
移动到容器元素(例如ul
、ol
)。
详细解释
当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
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。例如,许多项目使用button
、btn
或icon
类名,因此即使不使用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>