组件化开发
前端组件化是现在前端框架中一个非常重要的思想元素,将页面内容进行拆分之后,可独立维护,可复用性大大提高。哪个地方出问题,直接去修改的对应的组件即可。团队人员配置多的时候,你写你的header,我写我的footer。相互不影响,不冲突。而组件的合理拆分,也就变成了衡量水平的一个维度之一。
组件其实就是页面组成的一部分,好比是电脑中的每一个元件(如硬盘、键盘、鼠标),它是一个具有独立的逻辑和功能或界面,同时又能根据规定的接口规则进行相互融化,变成一个完整的应用。
页面只不过是这样组件的容器,组件自由组合形成功能完整的界面,当不需要某个组件,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行。前端组件化的核心思想就是将一个巨大复杂的东西拆分成粒度合理的小东西。
组件化是长期开发过程中一个提炼精华的过程,目的主要是以下几点:
- 提高开发效率
- 方便重复使用
- 简化调试步骤
- 提升整个项目的可维护性
- 便于协同开发
- 使其高内聚,低耦合,达到分治与复用的目的。
Vue组件化开发
- 单文件系统,样式局部作用域
- 基本组成结构:<template/> <script/> <style scoped/>
- 组件注册方式:
1)公共组件全局注册:要注册一个全局组件,可以使用Vue.component(tagName,option)
2)其余组件局部注册:通过使用组件实例选项component
注册,可以使组件仅在另一个实例或者组件的作用域中可用
结构化规范
基于Vue-cli脚手架的结构基础划分:
-
├── index.html 入口页面
-
├── build 构建脚本目录
-
│ ├── build-server.js 运行本地构建服务器,可以访问构后的页面
-
│ ├── build.js 生产环境构建脚本
-
│ ├── dev-client.js 开发服务器热重载脚本,主要用来实现开发阶段的页面自动
-
│ ├── dev-server.js 运行本地开发服务器
-
│ ├── utils.js 构建相关工具方法
-
│ ├── webpack.base.conf.js wabpack基础配置
-
│ ├── webpack.dev.conf.js wabpack开发环境配置
-
│ └── webpack.prod.conf.js wabpack生产环境配置
-
├── config 项目配置
-
│ ├── dev.env.js 开发环境变量
-
│ ├── index.js 项目配置文件
-
│ ├── prod.env.js 生产环境变量
-
│ └── test.env.js 测试环境变量
-
├── mock mock数据目录
-
│ └── hello.js
-
├── package.json npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
-
├── src 项目源码目录
-
│ ├── main.js 入口js文件
-
│ ├── App.vue 根组件
-
│ ├── components 公共组件目录
-
│ │ └── Title.vue
-
│ ├── assets 资源目录,这里的资源会被wabpack构建
-
│ │ ├── css 公共样式文件目录
-
│ │ ├── js 公共js文件目录
-
│ │ └── img 图片存放目录
-
│ ├── routes 前端路由
-
│ │ └── index.js
-
│ ├── store 应用级数据(state)
-
│ │ └── index.js
-
│ └── views 页面目录
-
│ ├── Hello.vue
-
│ └── NotFound.vue
-
├── static 纯静态资源,不会被wabpack构建。
-
├── test
-
│ └── unit 单元测试
-
│ │ ├── index.js 入口脚本
-
│ │ ├── karma.conf.js karma配置文件
-
│ │ └── specs 单测case目录
-
│ │ │ └── HelloWorld.spec.js
-
│── .gitignore.js 提交代码时候需要配置的忽略提交的代码目录
-
│── README.md 展示项目基本用法功能的说明性文件
组件命名规范
Vue官方文档给予以下说明:
当注册组件 (或者 prop) 时,可以使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或 PascalCase (单词首字母大写命名)。
PascalCase 是最通用的声明约定,而 kebab-case 是最通用的使用约定。
命名可遵循以下规则:
- 有意义的名词、简短、具有可读性,如:SearchSidebar.vue
- 以大写开头,采用帕斯卡命名,如:TodoList.vue
- 公共组件命名以公司名称简拼为命名空间,如:app-xx.vue
- 文件夹命名主要以功能模块代表命名
同时还需要注意:必须符合自定义元素规范: 使用连字符分隔单词,切勿使用保留字。app- 前缀作为命名空间, 如果非常通用的话可使用一个单词来命名,这样可以方便于其它项目里复用。
强制要求
-
组件名为多个单词
组件名应该始终是多个单词的,根组件 App 除外。-
// 反例
-
export default {
-
name: 'Todo',
-
// ...
-
}
-
-
// 正例
-
export default {
-
name: 'TodoItem',
-
// ...
-
}
-
-
组件数据
组件的 data 必须是一个函数。
当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。-
// 反例
-
export default {
-
data: {
-
foo: 'bar'
-
}
-
}
-
-
// 正例
-
export default {
-
data () {
-
return {
-
foo: 'bar'
-
}
-
}
-
}
-
// 在一个 Vue 的根实例上直接使用对象是可以的,
-
// 因为只存在一个这样的实例
-
new Vue({
-
data: {
-
foo: 'bar'
-
}
-
})
-
-
Prop定义
Prop 定义应该尽量详细。
在你提交的代码中,prop 的定义应该尽量详细,至少需要指定其类型。-
细致的 prop 定义有两个好处:
-
1) 它们写明了组件的 API,所以很容易看懂组件的用法;
-
2) 在开发环境下,如果向一个组件提供格式不正确的 prop,Vue 将会告警,以帮助你捕获潜在的错误来源。
-
-
-
-
-
// 反例
-
props: ['status']
-
-
//正列
-
props: {
-
status: String
-
}
-
// 更好的做法!
-
props: {
-
status: {
-
type: String,
-
required: true,
-
validator: function (value) {
-
return [
-
'syncing',
-
'synced',
-
'version-conflict',
-
'error'
-
].indexOf(value) !== -1
-
}
-
}
-
}
-
-
为v-for设置键值
总是用 key 配合 v-for。
在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。甚至在元素上维护可预测的行为,比如动画中的对象固化 (object constancy),也是一种好的做法。-
// 反例
-
<ul>
-
<li v-for="todo in todos">
-
{{ todo.text }}
-
</li>
-
</ul>
-
// 正例
-
<ul>
-
<li v-for="todo in todos" :key="todo.id">
-
{{ todo.text }}
-
</li>
-
</ul>
-
-
避免 v-if 和 v-for 用在一起
永远不要把 v-if 和 v-for 同时用在同一个元素上。
一般我们在两种常见的情况下会倾向于这样做:- 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如activeUsers),让其返回过滤后的列表。
-
为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul, ol)。
-
// 反例
-
<ul>
-
<li v-for="user in users" v-if="user.isActive" :key="user.id" >
-
{{ user.name }}
-
</li>
-
</ul>
-
-
<ul>
-
<li v-for="user in users" v-if="shouldShowUsers" :key="user.id" >
-
{{ user.name }}
-
</li>
-
</ul>
-
-
// 正例
-
<ul>
-
<li v-for="user in activeUsers" :key="user.id">
-
{{ user.name }}
-
</li>
-
</ul>
-
-
<ul v-if="">
-
<li v-for="user in users" :key="user.id">
-
{{ user.name }}
-
</li>
-
</ul>
-
-
-
为组件样式设置作用域
对于应用来说,顶级 App 组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。这条规则只和单文件组件有关。你不一定要使用 scoped 特性。设置作用域也可以通过 CSS Modules,那是一个基于 class 的类似 BEM 的策略,当然你也可以使用其它的库或约定。
不管怎样,对于组件库,我们应该更倾向于选用基于 class 的策略而不是 scoped 特性。
这让覆写内部样式更容易:使用了常人可理解的 class 名称且没有太高的选择器优先级,而且不太会导致冲突。-
// 反例
-
<template>
-
<button class="btn btn-close">X</button>
-
</template>
-
<style>
-
.btn-close {
-
background-color: red;
-
}
-
</style>
-
-
// 正例
-
<template>
-
<button class="button button-close">X</button>
-
</template>
-
<!-- 使用 `scoped` 特性 -->
-
<style scoped>
-
.button {
-
border: none;
-
border-radius: 2px;
-
}
-
.button-close {
-
background-color: red;
-
}
-
</style>
-
强烈推荐 (增强可读性)
-
单文件组件文件的大小写
单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。-
// 反例
-
components/
-
|- myComponent.vue
-
|- mycomponent.vue
-
-
// 正例
-
components/
-
|- MyComponent.vue
-
-
-
基础组件名
应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。-
// 反例
-
components/
-
|- MyButton.vue
-
|- VueTable.vue
-
|- Icon.vue
-
-
// 正例
-
components/
-
|- BaseButton.vue
-
|- BaseTable.vue
-
|- BaseIcon.vue
-
-
单例组件名
只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。
这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,只是目前在每个页面里只使用一次。-
// 反例
-
components/
-
|- Heading.vue
-
|- MySidebar.vue
-
-
// 正例
-
components/
-
|- TheHeading.vue
-
|- TheSidebar.vue
-
-
紧密耦合的组件名
和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。-
// 反例
-
components/
-
|- SearchSidebar.vue
-
|- NavigationForSearchSidebar.vue
-
-
// 正例
-
components/
-
|- TodoList.vue
-
|- TodoListItem.vue
-
|- TodoListItemButton.vue
-
-
components/
-
|- SearchSidebar.vue
-
|- SearchSidebarNavigation.vue
-
-
-
模板中的组件名大小写
对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 命名.-
// 反例
-
<!-- 在单文件组件和字符串模板中 -->
-
<mycomponent/>
-
<!-- 在单文件组件和字符串模板中 -->
-
<myComponent/>
-
-
// 正例
-
<!-- 在单文件组件和字符串模板中 -->
-
<MyComponent/>
-
-
带引号的特性值
非空 HTML 特性值应该始终带引号。
在 HTML 中不带空格的特性值是可以没有引号的,但这鼓励了大家在特征值里不写空格,导致可读性变差。建议:html属性使用双引号" ",js中使用单引号' '
-
// 反例
-
<AppSidebar :style={sidebarWidth+'px'}>
-
let name = "张三",
-
address = '上海';
-
-
// 正例
-
<AppSidebar :style="{ sidebarWidth + 'px' }">
-
let name = '张三',
-
address = '上海';
-
-
-
-
推荐单文件组件元素的顺序
单文件组件应该总是让<template> 、<script> 和 <style> 标签的顺序保持一致。且 <style> 要放在最后,另外两个标签至少要有一个,并且所有单文件组件格式要统一。-
<template>...</template>
-
<script>/* ... */</script>
-
<style>/* ... */</style>
-
组件选项声明顺序
-
- name
-
- components
-
- directives
-
- filters
-
- mixins
-
- props
-
- data
-
- computed
-
- watch
-
- beforeCreate
-
- created
-
- beforeMount
-
- mounted
-
- beforeUpdate
-
- updated
-
- activated
-
- deactivated
-
- beforeDestroy
-
- destroyed
-
- methods
注释规范
代码注释在一个项目的后期维护中显的尤为重要,所以我们要为每一个被复用的组件编写组件使用说明,为组件中每一个方法编写方法说明。
以下情况,务必添加注释:
- 公共组件使用说明
- 各组件中重要函数或者类说明
- 复杂的业务逻辑处理说明
- 特殊情况的代码处理说明,对于代码中特殊用途的变量、存在临界值、函数中使用的hack、使用了某种算法或思路等需要进行注释描述
- 注释块必须以/**(至少两个星号)开头**/
- 单行注释使用// 后面空一格然后写注释
-
单行注释
普通方法一般使用单行注释// 来说明该方法主要作用
-
多行注释
组件使用说明,和调用说明-
<!--公用组件:Dialog弹出框
-
/**
-
* 组件名称
-
* @module 组件存放位置
-
* @desc 组件描述
-
* @author 组件作者
-
* @date 2018年12月20日17:22:43
-
* @param {Object} [title] - 参数说明
-
* @param {String} [show] - 参数说明
-
* @example 调用示例
-
* <dialog :title="title" :show="show"></dialog>
-
*/
-
-->
-
编码规范
优秀的项目源码,即使是多人开发,看代码也如出一人之手。统一的编码规范,可使代码更易于阅读,易于理解,易于维护。尽量按照ESLint格式要求编写代码:
-
使用
ES6
风格编码源码-
定义变量使用let ,定义常量使用const
-
使用export ,import 模块化
-
-
组件
props
原子化-
提供默认值
-
使用 type 属性校验类型
-
使用 props 之前先检查该 prop 是否存在
-
- 避免
this.$parent
- 谨慎使用
this.$refs
- 无需将
this
赋值给component
变量 - 调试信息
console.log(),debugger
使用完及时删除 - html属性值使用双引号,js中定义变量用单引号,如
let name = 'jack'
v-for
循环时一定要写:key
,并且key
使用不能使用循环的序号index
,要使用属性id
- 模板内的复杂逻辑,推荐使用计算属性
computed
watch
监听时,慎用deep:true
属性,深度监听会对性能产生影响
总结
这是我在项目开发中总结出来的一些vue开发规范,总结的还不是很完善,有问题的地方希望大家提出来,进一步完善vue开发规范,谢谢!
参考:vue风格指南
.