Vue 2.x 规范
组件 data 必须是一个函数
当在组件中使用 data 的时候 (除了 new Vue 外的任何地方),它的值必须是一个返回对象的函数,这样做可以规避一些组件复用时的 bug
不推荐
export default {
data: {
foo: 'bar'
}
}
复制代码
推荐
export default {
data () {
return {
foo: 'bar'
}
}
}
复制代码
Prop 定义尽量详细
Prop 的定义应该尽量详细,至少需要指定其类型,这样可以避免一些潜在的类型错误
不推荐
// 来源:src\components\chart\embedexcel\dataFilterModal.vue
props: ["position", "spread"]
复制代码
推荐
props: {
strProp: {
type: String,
default: ''
},
arrProp: {
type: Array,
default: () => []
},
objProp: {
type: Object,
default: () => ({})
}
}
复制代码
v-for 设置唯一键值
使用 v-for 时必须设置唯一的 key,以便 Vue 在渲染时把可能的 DOM 变更降到最低
不推荐
<!-- 来源:src\components\easy-more\easyShowMoreToolTip.vue -->
<p v-for="(item,index) in arr">
{{item[keyVal]}}
</p>
复制代码
推荐
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
复制代码
避免 v-for 和 v-if 同时使用(一)
当过滤一个列表中的项目时,永远不要把 v-for 和 v-if 同时用在同一个元素上,取而代之的是使用计算属性
不推荐
<div
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</div>
复制代码
推荐
<div
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</div>
复制代码
computed: {
activeUsers: function () {
return this.users.filter(() => user.isActive)
}
}
复制代码
避免 v-for 和 v-if 同时使用(二)
当需要隐藏一个本应该被隐藏的列表时,永远不要把 v-for 和 v-if 同时用在同一个元素上,取而代之的是添加一个父元素并在父元素上使用 v-if
不推荐
<!-- 来源:src\components\toolBarComponent\liquidfill\liquidfillNumericalSet.vue -->
<div v-if="!numericalSet.configs.length" class="noInfo">
</div>
<div v-for="(field,indexs) in numericalSet.configs" :key="field.columnId" v-else>
</div>
复制代码
推荐
<template v-if="shouldShowUsers">
<div
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</div>
</template>
复制代码
Mixin 中使用私有属性名
为避免混入时属性相冲突,建议对插件、混入等不考虑作为公共 API 的自定义私有属性使用 $_ 前缀
不推荐
// 来源:src\views\Pages\Dashboard\mixins\embedexcelFuncs.js
export const embedexcelFuncs = {
data () {
return {
styleTemp: {}
}
},
methods: {
handleSpaceList() {}
}
}
复制代码
推荐
export const myGreatMixin = {
data () {
return {
$_myGreatMixin_title: ''
}
},
methods: {
$_myGreatMixin_update: function () {}
}
}
复制代码
组件命名应由多单词组成
组件名应该始终是多个单词的(根组件 App 以及 Vue 内置组件除外)且命名风格为大驼峰,这样做可以避免跟现有或未来的 HTML 元素相冲突
不推荐
Vue.component('todo', {
// ...
})
// 来源:src\components\modal\confirm.vue
export default {
name: 'confirm',
// ...
}
复制代码
推荐
Vue.component('TodoItem', {
// ...
})
export default {
name: 'BaseConfirm',
// ...
}
复制代码
为组件的样式设置作用域
除特殊情况(如 element 部分组件的 dom 会被 transfer 到 body 下)外,单文件组件应该对样式设置作用域,以免当前组件的样式影响到其他组件的样式
不推荐
<!-- 来源:src\components\chart\slicertabs\relevanceCharts.vue -->
<template>
<div class="common_bottom_20"></div>
</template>
<style>
.common_bottom_20 {
margin-bottom: 20px;
}
</style>
复制代码
推荐
<template>
<button class="button">X</button>
</template>
<style scoped>
.button {
border: none;
border-radius: 2px;
}
</style>
复制代码
单文件组件的文件命名
单文件组件的文件命名风格应保持一致,统一为大驼峰
不推荐
components/
|- table-page.vue
|- dragRightIcon.vue
|- UserInfoModal.vue
复制代码
推荐
components/
|- TablePage.vue
|- DragRightIcon.vue
|- UserInfoModal.vue
复制代码
模板中的组件名大小写
在 Vue 模板中,统一使用烤肉串命名法(不强制)
不推荐
<mycomponent/>
<myComponent/>
<MyComponent></MyComponent>
复制代码
推荐
<my-component></my-component>
复制代码
自闭合组件
在 Vue 模板中,组件统一自闭合
不推荐
<my-component></my-component>
复制代码
推荐
<my-component/>
复制代码
完整单词的组件名
组件名应该倾向于完整单词而不是缩写
不推荐
components/
|- SdSettings.vue
|- UProfOpts.vue
复制代码
推荐
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
复制代码
Prop 名大小写
在声明 prop 的时候,其命名应该始终使用小驼峰命名,而在 Vue 模板中应该始终使用烤肉串命名
不推荐
props: {
'greeting-text': String
}
复制代码
<!-- 来源:src\views\Pages\Workbench\selfSupportTemplete\header.vue -->
<easy-select searchWidth="278px"></easy-select>
复制代码
推荐
props: {
greetingText: {
type: String,
default: '',
}
}
复制代码
<welcome-message greeting-text="hi"/>
复制代码
多个 attribute 的元素
多个 attribute 的元素应该分多行撰写,每个 attribute 一行,因为这样更易读
不推荐
<Icon type="ios-search" @click="changeSearchStatus" v-if='!info.isSearch'></Icon>
复制代码
推荐
<my-component
foo="a"
bar="b"
baz="c"
></my-component>
复制代码
模板中简单的表达式
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法,因为计算属性和方法直观且可复用
不推荐
<!-- 来源:src\views\Pages\Authority\basicSet\ocAutoConfig.vue -->
{{item.split('-status:')[0].split('/')[0]}}
复制代码
推荐
{{ normalizedFullName }}
复制代码
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
复制代码
计算属性 SRP
应该把复杂计算属性分割为尽可能多的更简单的 property,一个属性只做一件事(即单一职责原则),这样易于测试、阅读以及更好的拥抱变化
不推荐
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
复制代码
推荐
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
复制代码
带引号的 attribute 值
非空 attribute 的值应该始终带引号(推荐双引号),增加可读性
不推荐
<!-- 来源:src\components\chart\slicertabs\slicertabs.vue -->
<el-date-picker
:type=timeFilterType[item.columnFilterType]
:format=timeFormat[item.columnFilterType]
>
</el-date-picker>
复制代码
推荐
<input type="text">
<app-side-bar :style="{ width: sidebarWidth + 'px' }"></app-side-bar>
复制代码
指令缩写统一
指令缩写(用 : 表示 v-bind:,用 @ 表示 v-on:,和用 # 表示 v-slot:)应该要么都用要么都不用,推荐缩写
推荐
<template #footer>
<div @click="divClick">
<div :someProp="propData"></div>
</div>
</template>
复制代码
组件/实例的选项顺序统一
组件/实例的选项应该有统一的顺序
- 副作用 (触发组件外的影响)
- el
- 全局感知 (要求组件以外的知识)
- name
- parent
- 组件类型 (更改组件的类型)
- functional
- 模板修改器 (改变模板的编译方式)
- delimiters
- comments
- 模板依赖 (模板内使用的资源)
- components
- directives
- filters
- 组合 (向选项里合并 property)
- extends
- mixins
- 接口 (组件的接口)
- inheritAttrs
- model
- props/propsData
- 本地状态 (本地的响应式 property)
- data
- computed
- 事件 (通过响应式事件触发的回调)
- watch
- 生命周期钩子 (按照它们被调用的顺序)
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- 非响应式的 property (不依赖响应系统的实例 property)
- methods
- 渲染 (组件输出的声明式描述)
- template/render
- renderError
常用组件选项示例
new Vue({
el: '',
name: '',
components: {},
directives: {},
filters: {},
mixins: [],
props: {},
data () {},
computed: {},
watch: {},
beforeCreate () {},
created () {},
beforeMount () {},
mounted () {},
beforeDestroy () {},
destroyed () {},
methods: {},
template: ``,
render () {}
})
复制代码
元素 attribute 的顺序统一
元素 (包括组件) 的 attribute 应该有统一的顺序(不强制)
- 定义 (提供组件的选项)
- is
- 列表渲染 (创建多个变化的相同元素)
- v-for
- 条件渲染 (元素是否渲染/显示)
- v-if
- v-else-if
- v-else
- v-show
- v-cloak
- 渲染方式 (改变元素的渲染方式)
- v-pre
- v-once
- 全局感知 (需要超越组件的知识)
- id
- 唯一的 attribute (需要唯一值的 attribute)
- ref
- key
- 双向绑定 (把绑定和事件结合起来)
- v-model
- 其它 attribute (所有普通的绑定或未绑定的 attribute)
- 事件 (组件事件监听器)
- v-on
- 内容 (覆写元素的内容)
- v-html
- v-text
常用 attribute 示例
<input
v-for=""
v-once
id=""
ref=""
:key=""
v-model=""
class=""
style=""
v-on=""
v-html=""
/>
复制代码
单文件组件的顶级元素顺序
单文件组件应该总是让 <template>
、<script>
和 <style>
标签的顺序保持一致,且 <style>
要放在最后,因为另外两个标签至少要有一个
不推荐
<style>/* ... */</style>
<template>...</template>
<script>/* ... */</script>
复制代码
推荐
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
复制代码
scoped 中慎用元素选择器
元素选择器应该避免在 scoped 中出现,因为在 scoped 样式中,类选择器比元素选择器更快
不推荐
<style scoped>
/* 来源:src\layouts\SiderMenu.vue */
button span {
color: #447ee7;
}
</style>
复制代码
推荐
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
复制代码
避免父子组件隐式通信
应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或变更 prop
不推荐
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: '<input v-model="todo.text">'
})
复制代码
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo () {
var vm = this
vm.$parent.todos = vm.$parent.todos.filter(function (todo) {
return todo.id !== vm.todo.id
})
}
},
template: `
<span>
{{ todo.text }}
<button @click="removeTodo">
X
</button>
</span>
`
})
复制代码
推荐
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `
<input
:value="todo.text"
@input="$emit('input', $event.target.value)"
>
`
})
复制代码
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `
<span>
{{ todo.text }}
<button @click="$emit('delete')">
X
</button>
</span>
`
})
复制代码
通过 commit 修改状态
应当通过 commit 显式地修改状态而不是直接给状态赋值,这样可以更明确地追踪到状态的变化,也能让一些调试工具记录每次状态的改变(不强制)
不推荐
// 来源:src\views\Pages\Workbench\selfSupportTemplete\setMenu\crossTable.vue
this.$store.state.selfOrderLimit = item.nodeData.selfOrderLimit
复制代码
推荐
this.store.commit('changeSelfOrderLimit', item.nodeData.selfOrderLimit)
复制代码
善用修饰符
大多数事件中,Vue 都提供了修饰符,使用可提高开发效率
不推荐
<!-- 来源:src\views\Pages\Workbench\electronicForm\tableData.vue -->
<div @click="stopPropagation($event)">
复制代码
stopPropagation($event) {
$event.stopPropagation();
}
复制代码
推荐
<div @click.stop>
复制代码
请求写在 created 中
除特殊情况(比如动态组件使用了 keep-alive)外,在 created 中处理请求是最佳实践
不推荐
mounted () {
api.getAuthTreeAjax()
.then(() => {})
.catch(() => {})
}
复制代码
推荐
created () {
api.getAuthTreeAjax()
.then(() => {})
.catch(() => {})
}
复制代码
公共组件应附上 README
公共组件的编写应随之附上 README,README 中应详细描述 props、插槽、事件和对外暴露的方法的使用说明,做到“箱说齐全”
公共组件 README 模板
### 组件描述
这里是组件描述
### props
| 属性 | 类型 | 默认值 | 描述 |
| -- | -- | -- | -- |
| disabled | Boolean | false | 是否禁用组件 |
### 插槽
| 插槽名 | slotProps | 描述 |
| -- | -- | -- |
| default | `{ userName: 'John', userAge: 24 }` | 默认插槽 |
### 事件
| 事件名 | 返回值 | 返回值类型 | 描述 |
| -- | -- | -- | -- |
| on-select | 当前选中项 | String | 当选中选项时触发该事件 |
### 暴露的方法
| 方法名 | 参数 | 参数类型 | 描述 |
| -- | -- | -- | -- |
| resetForm | - | - | 重置表单 |
复制代码