分支规则
主分支: master
开发分支: develop
功能开发以 `feature/功能名` 命名
组件开发以 `components/组件名` 命名
修复 bug 以 `bugfix/bug(bugId或简短名)` 命名
紧急修复以 `hotfix/bug` 命名
文档开发以 `docs/功能名` 命名
命名规则
统一命名规则
动态组件 统一使用 `tag` 属性来指定生成的标签类型
状态对应属性应该为直接的状态名
如: `loading` 状态对应属性名应该为 `loading`、`disabled` 状态对应属性名应该为 `disabled`
分子以上组件,内部出现多个组件有相同状态则以 [组件名][状态名] 小驼峰形式对外暴露
内部组件对外暴露 `class` 属性以 `[组件名][Class]` 小驼峰形式命名
`script`、`js` 中驼峰命名,文档、文件夹、模板中以 - 连接
数组类型以复数形式命名
例如: `options` 对应为 `array` 类型, `option` 对应为 `object` 类型
内部组件命名也应该符合命名规范
内部组件属性透传命名建议以 `[组件名]Props` 名称命名
例如: 导航组件使用 `logoProps` 属性对 `logo` 标签进行属性传递
内部响应事件函数应该以 `handle[EventName]` 命名, `update` 事件以 `update[PropName]` 命名,如果内部有多个相同的事件名,则以 `handle[Element/ComponentName][EventnName]` 命名
子组件有更改属性需求时,对外发送 `update:PropName` 事件,并且将新值当做第一个参数
代码块Vue.js Component<template>
<div></div>
</template>
<script>
export default {
props: {
visible: Boolean,
},
methods: {
toggle () {
this.$emit('update:visible', !this.visible);
}
}
}
</script>
slot 命名应该明确表明该 slot 对应元素的意义
例如: <slot name='loading' /> 表明此插槽用来显示自定义的 loading 元素
// only an demo
<script>
export default {
name: 'MtdButton',
props: {
// logo 地址
logo: String,
// [组件/模块名]Props
logoProps: Object,
// [组件/模块名]Class
logoClass: String,
// 数组类型命名复数
options: Array,
option: Object,
},
methods: {
// handle[ComponentName][EventName]
handleInputClick () {
}
}
}
</script>
Props
样式相关枚举类型属性不做强校验、功能相关强校验
用户可能自己定义了其他样式并将改属性传入,所以对样式方面的枚举类型不做强校验,但是功能方面需要内部的支持,传入一个不支持的对于用户来说没有任何意义。
代码块Vue.js Component// tooltip component
<script>
export default {
name: 'MtdTooltip',
props: {
// 此属性用于生成对应的 class,内部样式只支持 dark、 light,但是不能排除用户提供了其他样式的可能,所以此处不校验
theme: String,
// 此属性用于 tooltip 的显示触发方式,由于内部只实现了 hover,click 所以对于用户来说传递一个其他方式并没有意义
trigger: {
type: String,
validator: function (v) {
return ['hover', 'click'].includes(v);
}
}
// ...other
}
// ...
}
</script>
表单需要支持 `v-model`, 使用 .sync 修饰符表达更新 props 意图
代码块Vue.js Component// switch component
<script>
export default {
name: 'MtdSwitch',
model: {
prop: 'actived',
},
props: {
actived: Boolean,
},
methods: {
handleClick () {
this.$emit('input', this.actived);
}
}
}
</script>
上层组件需要支持内部组件属性透传,特别是要提供内部组件 `class` 传递
有些时候需要对内部进行样式的覆盖,如果不提供 `class` 支持,外部只能通过选择器的优先级完成.
代码块Vue.js Component// 分页组件分为 Pagination, Pager
// Pagination component
<template>
<ss-pager v-bind="$attrs" :class="pagerClass"></ss-pager>
// or
<ss-pager v-bind="pagerProps"></ss-pager>
// ...
</template>
<script>
export default {
name: 'MtdPagination',
props: {
pagerClass: string,
}
}
</script>
组件作用对应某一个原生标签时,需要使用 `v-bind="$attrs"` ,对应的也可以使用 `v-on="$lisenters"`
这样做可以不用显示的定义全原生标签属性,而且当原生属性扩展时不需要更变
代码块Vue.js Component// input component
<template>
// ...
<input v-bind="$attrs" />
</template>
组件避免出现不同属性控制相同功能、样式
代码块Vue.js Component// pager component
// 不推荐
<script>
export default {
props: {
total: Number,
pageCount: Number,
pageSize: Number,
}
}
</script>
// 推荐
<script>
export default {
props: {
total: Number,
pageSize: Number,
}
computed: {
pageCount () {
return Math.ceil(total/pageSize) || 0;
}
}
}
</script>
Data / State
组件无状态,控制权交与使用者
代码块Vue.js Component// switch component
<script>
export default {
props: {
actived: Boolean,
},
methods: {
handleClick () {
this.$emit('input', !this.actived); // 此时如果外部没有改变 actived 属性的值,显示依然会是原来的状态
}
}
}
</script>
优先使用计算方式得出当前所需内部属性 (vue 中的 computed)
例如: tabs 组件中,tab 组件会有 active 的状态,active 的状态应该由计算属性得出来,而不是通过 watch 来改变内部变量
代码块JavaScript// better
get active () {
return tabsValue === this.value
}
// not
watch {
tabsValue (n) {
this.active = n === this.value
}
}
避免出现内部属性的使用 (vue 的 data 函数, react 的 state)
Event
在父子组件通信、属性方法定义时,当不需要方法的返回值时,原则上都应该使用事件的方式 ( Vue )
代码块JavaScript// 不推荐
export default {
name: 'MtdInput',
props: {
onChange: Function,
},
methods: {
handleInput (v) {
this.onChange(v);
}
}
}
// 推荐
export default {
name: 'MtdInput',
methods: {
handleInput (v) {
this.$emit('input', v);
}
}
}
组件应该支持常用的原生事件,原生事件第一个参数应该是 event 对象
代码块Vue.js Component<template>
<div>
<input v-bind="$attrs" v-on="$lisenters" :value="value" @input="handleInput" />
</div>
</template>
<script>
export default {
name: 'MtdInput',
props: {
value: [String, Number]
},
// ...other
}
</script>
原生类型事件,其行为应该同原生事件 例如: compositionstart、compositionend 事件,应该表现同原生,不应该对外发送 change 类型事件
change、update 类型事件第一个参数是 新值,第二个参数是 旧值
代码块Vue.js Component<template>
<div></div>
</template>
<script>
export default {
props: {
visible: Boolean,
},
methods: {
handleClose () {
this.$emit('update:visible', false, this.visible);
}
}
}
</script>
事件、方法参数应该避免超过3个,且越常用的参数应该越靠前
方法中最后一个参数不应该是 bool 类型,应该将所有 bool 类型参数改为 object
代码块JavaScript// 不推荐:
function doSomthing (param, replace /* bool */)
// 推荐:
function doSomthing (param, { replace /* bool */ })
组件通信
统一使用 事件 方式向父级通信,父级通过更改 prop 做出回应
避免使用 refs
避免使用 $parent
Vue.js 支持组件嵌套,并且子组件可访问父组件的上下文。访问组件之外的上下文违反了基于模块开发的第一原则。因此你应该尽量避免使用 this.$parent
其他
组件内部不要出现魔数,如果确实有需求需要使用,必须添加注释,描述清楚数值的作用及来源,如果可能更改(该值可能出现自定义的需求),则将其作为属性,默认值为当前值
样式相关需求优先考虑 `css` 或 `scss` 变量方式,如果 `css`、`scss` 实现不了,则需要在 `js` 实现时添加注释说明原因
将样式与 `js` 分离,方便用户做样式覆盖,如果写在了 `js` 中则很大概率会使用 `style` 方式来生成样式,对于用于自定义样式来说非常困难,而且,各个页面、组件之间样式很可能有一定的关联性,一部分在 `css` 中,一部分在 `js` 中不利于管理