Vue 规范
1. 项目结构
1.1 项目目录结构
★ [强制] 参照vue标准文件目录
示例:
node_nodules/
public/
src/
|- api ---------------------- 统一接口目录
|- assets ------------------- 资源目录
|- components --------------- 公共组件目录
|- directives --------------- 自定义指令目录
|- router ------------------- VueRouter目录
|- store -------------------- Vuex目录
|- styles ------------------- 样式文件目录
|- utils -------------------- 工具目录
|- views -------------------- 页面组件目录
|- App.vue
|- main.js
.browserslistrc
.env.development
.env.production
.eslintrc.js
.gitinore
babel.config.js
package.json
README.md
vue.config.js
1.2 拆分组件的原则
● [建议] 符合可复用、可组合、单一职责的原则。
● [建议] 相似功能合理拆分, 不在同一文件写过多冗余代码。
1.3 命名
★ [强制] 有意义的名词,简短、具有可读性。
★ [强制] 单文件组件的文件名采用大驼峰 (PascalCase)。index.vue为了简化引入可采用小写字母格式。
示例:
// good
components/
|- SearchButton.vue
// bad
components/
|- searchButton.vue
|- searchbutton.vue
● [建议] 与父组件紧密耦合的子组件应该以父组件名作为前缀命名。
示例:
// good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
// bad
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
● [建议] 组件名称应该以高阶的 (通常是一般化描述的) 单词开头,并以描述性的修饰词结尾。
::: details 详解
对于一个带搜索表单的应用来说,它可能包含这样的组件:
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
你可能已经注意到了,我们很难看出来哪些组件是针对搜索的。现在我们来根据规则给组件重新命名:
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
因为编辑器通常会按字母顺序组织文件,所以现在组件之间的重要关系一目了然。
你也许会想要换种方式解决这个问题,把所有的搜索组件放到“search”目录,把所有的设置组件放到“settings”目录。我们只推荐在非常大型的应用 (如有 100+ 个组件) 下才考虑这么做,因为:
- 在多级目录间找来找去,通常来说要比在单个 components 目录下滚动查找要花费更多的精力。
- 存在组件重名 (比如存在多个 ButtonDelete.vue 组件) 的时候在编辑器里更难快速定位。
- 重构将变得更困难,因为为一个移动了的组件更新相关引用时,查找/替换通常并不高效。
:::
// good
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
// bad
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
2. 组件结构
2.1 模板风格
● [建议] 使用Vuter风格,结构规范更利于寻找代码片段
示例:
<template></template>
<script></script>
<style></style>
2.2 组件名
● [建议] 给每个组件设置name属性
- 当使⽤keep-alive时,可以使⽤这个name进⾏过滤
- 递归组件(组件⾃⼰调⽤⾃⼰)中迭代时需要调⽤⾃⾝的name
- 指定 name 选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。
- 当在有 vue-devtools,未命名组件将显示成 <AnonymousComponent>,这很没有语义。通过提供 name 选项,可以获得更有语义信息的组件树。
3. tempalate
★ [强制] 为 v-for 设置关键值key,且尽量不使用 index
示例:
<!-- good -->
<ul>
<li
v-for="item in projectList"
:key="item.id"
>
{{item.name}}
</li>
</ul>
<!-- bad -->
<ul>
<li
v-for="item in projectList"
>
{{item.name}}
</li>
</ul>
★ [强制] 永远不要在一个元素上同时使用 v-if 和 v-for
-
vue2中v-for 比 v-if 具有更高的优先级,会导致哪怕我们只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。
-
vue3中v-if 比 v-for 具有更高的优先级,v-if先执行的时候v-for的迭代变量此时还不存在,会导致抛出错误。
示例:
<!-- good -->
<ul>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
</ul>
<!-- bad -->
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
★ [强制] 所有标签的属性值是有双引号而非单引号。
<!-- good -->
<div class="contanier"></div>
<!-- bad -->
<div class='contanier'></div>
★ [强制] 指令缩写,统一使用简写形式。
- 动态绑定属性 v-bind === :
- 动态绑定事件 v-on === @
- 动态绑定插槽 v-slot === #
<!-- 动态绑定属性 -->
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
<!-- 动态绑定事件 -->
<input
@input="onInput"
@focus="onFocus"
>
<!-- 动态绑定插槽 -->
<template #header>
<h1>Here might be a page title</h1>
</template>
★ [强制] 多个 attribute 的元素应该分多行撰写,每个 attribute 占一行。
<el-select
v-model="form.version"
clearable
placeholder="请选择版本"
style="width:200px"
>
<el-option
v-for="item in versionOptions"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
★ [强制] 对于自定义组件标签,必须写全开始标签和结束标签,方便有 slot 的情况
示例:
<!-- good -->
<MenuItem></MenuItem>
<!-- bad -->
<MenuItem/>
● [建议] 自定义组件标签使用大驼峰写法,便于组件引入查找
示例:
<!-- good -->
<MenuItem></MenuItem>
★ [强制] 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该显示什么,而非如何计算那个值。而且计算属性和方法使得代码可以复用。
<!-- good -->
<template>
{{ normalizedFullName }}
</template>
<script>
export default{
computed: {
normalizedFullName() {
return this.fullName.split(' ')
.map(word => word[0].toUpperCase() + word.slice(1))
.join(' ')
}
}
}
</script>
<!-- bad -->
<template>
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
</template>
4. script
● [建议] 属性、生命周期撰写顺序。
// good
export default {
name: 'App',
components: {},
props: {},
data() {},
computed: {},
created() {},
mounted() {},
methods: {}
watch: {},
};
★ [强制] 组件 props 定义详细。
// good
export default{
props: {
status: {
type: Boolean,
required: true
}
}
props: {
status: {
type: String,
default: ()=> 'synced'
}
}
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
}
// bad
props: ['status']
● [建议] 使用 ES6 风格编码源码。
5. style
★ [强制] 为组件样式设置作用域scoped
这条规则只适用于单文件组件。防止样式污染。
<!-- good -->
<style scoped>
.list-item{
}
</style>
<!-- bad -->
<style>
.list-item{
}
</style>
★ [强制] 一个项目只允许使用一种预处理语言。
同一个项目里面不能scss和less同时存在,微前端每个子应用只允许使用一种预处理语言。
● [建议] 元素选择器应该避免在 scoped 中出现。
在 scoped 样式中,类选择器要比元素选择器更好,因为大量地使用元素选择器是很慢的。
● [建议] 不推荐使用 id 选择器来定义样式。
6. Vuex
★ [强制] 组件内不能直接修改state,只能通过mutation修改。
● [建议] 仅在必要时使用vuex。
7. VueRouter
★ [强制] 使用路由懒加载。
// good
const UserDetails = () => import('./views/UserDetails')
// bad
import UserDetails from './views/UserDetails'