前端代码开发规范

1,125 阅读14分钟

命名规范

项目命名

全部采用小写方式,以中划线分隔

// good
mall-management-system

// bad
mall_management-system / mallManagementSystem

目录命名

全部采用小写方式,以中划线分隔,有复数结构时,要采用复数命名法,缩写不用复数


// good
scripts / styles / components / images / utils / layouts / demo-styles / demo-scripts / img / doc

// bad
script / style / demo_scripts / demoStyles / imgs / docs

JS、CSS、HTML、PNG 文件命名

全部采用小写方式,以中划线分隔


// good
render-dom.js / signup.css / index.html / company-logo.png

// bad
renderDom.js / UserManagement.html

命名严谨性

1、代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用


// good
henan / luoyang / rmb 等国际通用的名称,可视同英文。

// bad
DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3

2、杜绝完全不规范的缩写,避免望文不知义。


// bad
AbstractClass“缩写”命名成 AbsClass;
condition“缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。

HTML规范

HTML标签

  • 标签必须合法且闭合、嵌套正确,标签名需小写;
  • 标签语法无错误,需要符合语义化;
  • 标签的自定义属性以 data-开头,如:<a href="#" data-num="18"></a>
  • 除非有特定的功能、组件要求等,禁止随意使用 id 来定义元素样式。

链接

  • <a> 标签加上 title 属性;
  • 非本专题的页面间跳转,采用打开新窗口模式:target="_blank"

flash

页面禁止使用 flash,动画使用 video、CSS3、canvas 等方式实现,低版本浏览器使用背景图片降级。

HTML类型

推荐使用 HTML5 的文档类型申明:(建议使用 text/html 格式的 HTML。避免使用 XHTML。XHTML 以及它的属性,比如 application/xhtml+xml 在浏览器中的应用支持与优化空间都十分有限)。


规定字符编码
IE 兼容模式
规定字符编码
doctype 大写

// good
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta charset="UTF-8" />
    <title>Page title</title>
  </head>
  <body>
    <img src="images/company-logo.png" alt="Company" />
  </body>
</html>

缩进

  • 缩进使用 2 个空格(一个Tab);
  • 嵌套的节点应该缩进。

分块注释

在每一个块状元素,列表元素和表格元素前,加上一对 HTML 注释。注释格式。

语义化标签

HTML5 中新增很多语义化标签,所以优先使用语义化标签,避免一个页面都是 div 或者 p 标签


// good
<header></header>
<footer></footer>

引号

属性值使用双引号(" ") 而不是单引号('') 。


//good
<div :data="data" data-src="some"></div>

标签中的属性顺序

class(class是最高的复用设计,应该放在最前)

id name(id 应尽量少用)

data-自定义属性(属性名称全小写)

src(资源文件)

placeholder title alt(提示)

required readonly disable(辅助)

CSS规范

命名


类名使用小写字母,以中划线分隔
id 采用驼峰式命名
scss、less 中的变量、函数、混合、placeholder 采用驼峰式命名

选择器

css 选择器中避免使用标签名,从结构、表现、行为分离的原则来看,应该尽量避免 css 中出现 HTML 标签,并且在 css 选择器中出现标签名会存在潜在的问题。

很多前端开发人员写选择器链的时候不使用 直接子选择器(注:直接子选择器和后代选择器的区别)。有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能。然而,在任何情况下,这是一个非常不好的做法。如果你不写很通用的,需要匹配到 DOM 末端的选择器, 你应该总是考虑直接子选择器。


// good
.content > .title {
  font-size: 2rem;
}

// bad 
.content .title {
  font-size: 2rem;
}

尽量使用缩写属性


// good
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;

// bad 
border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;

每个选择器及属性独占一行


// good
button {
  width:100px;
  height:50px;
  color:#fff;
  background:#00a0e9;
}

// bad 
button {
  width:100px;height:50px;color:#fff;background:#00a0e9;
}

省略0 后面的单位


// good
div {
  padding-bottom: 0;
  margin: 0;
}

// bad 
div {
  padding-bottom: 0px;
  margin: 0em;
}

避免使用 ID 选择器及全局标签选择器防止污染全局样式


// good
.header {
  padding-bottom: 0;
  margin: 0;
}

// bad 
#header {
  padding-bottom: 0px;
  margin: 0em;
}

属性书写顺序

建议遵循以下顺序:

  • 布局定位属性:display / position / float / clear / visibility / overflow
  • 自身属性:width / height / margin / padding / border / background
  • 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
  • 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient …

.about {
    display: block;
    position: relative;
    float: left;
    width: 100px;
    height: 100px;
    margin: 0 10px;
    padding: 20px 0;
    font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
    color: #333;
    background: rgba(0,0,0,.5);
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    -o-border-radius: 10px;
    -ms-border-radius: 10px;
    border-radius: 10px;
}

CSS3浏览器私有前缀写法


.about {
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    -o-border-radius: 10px;
    -ms-border-radius: 10px;
    border-radius: 10px;
}

javascript规范

命名

1、采用小写驼峰命名 lowerCamelCase,代码中的命名均不能以下划线,也不能以下划线或美元符号结束


// good 
localValue / getHttpMessage() / inputUserId

// bad 
_name / name_ / name$ 2) 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。

2、其中 method 方法命名必须是 动词 或者 动词+名词 形式


// good
saveShopCarData /openShopCarInfoDialog

// bad
save / open / show / go

3、特此说明,增删查改,详情统一使用如下 5 个单词,不得使用其他(目的是为了统一各个端)add / update / delete / detail / get

附: 函数方法常用的动词:

get 获取/set 设置
add 增加/remove 删除
create 创建/destory 移除
start 启动/stop 停止
open 打开/close 关闭
read 读取/write 写入
load 载入/save 保存
create 创建/destroy 销毁
begin 开始/end 结束
backup 备份/restore 恢复
import 导入/export 导出
split 分割/merge 合并
inject 注入/extract 提取
attach 附着/detach 脱离
bind 绑定/separate 分离
view 查看/browse 浏览
edit 编辑/modify 修改
select 选取/mark 标记
copy 复制/paste 粘贴
undo 撤销/redo 重做
insert 插入/delete 移除
add 加入/append 添加
clean 清理/clear 清除
index 索引/sort 排序
find 查找/search 搜索,
increase 增加/decrease 减少
play 播放/pause 暂停
launch 启动/run 运行
compile 编译/execute 执行
debug 调试/trace 跟踪
observe 观察/listen 监听
build 构建/publish 发布
input 输入/output 输出
encode 编码/decode 解码
encrypt 加密/decrypt 解密
compress 压缩/decompress 解压缩
pack 打包/unpack 解包,
parse 解析/emit 生成
connect 连接/disconnect 断开,
send 发送/receive 接收
download 下载/upload 上传
refresh 刷新/synchronize 同步
update 更新/revert 复原
lock 锁定/unlock 解锁
check out 签出/check in 签入
submit 提交/commit 交付
push 推/pull 拉
expand 展开/collapse 折叠
begin 起始/end 结束
start 开始/finish 完成
enter 进入/exit 退出
abort 放弃/quit 离开
obsolete 废弃/depreciate 废旧
collect 收集/aggregate 聚集

4、常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

// good
MAX_STOCK_COUNT
// bad 
MAX_COUNT

代码格式

1、使用 2 个空格进行缩进;

// good
if (x < y) {
  x += 10;
} else {
  x += 1;
}

2、不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。

字符串

1、统一使用单引号(''),不使用双引号("")。这在创建 HTML 字符串非常有好处

// good
let str = 'foo';
let testDiv = '<div id="test"></div>';
// bad
let str = 'foo';
let testDiv = "<div id='test'></div>";

2、静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

// good
const a = 'foobar';
const b = `foo${a}bar`;
// bad
const a = "foobar";
const b = 'foo' + a + 'bar';

对象声明

1、使用字面值创建对象

// good
let user = {};
// bad 
let user = new Object();

2、使用字面量来代替对象构造器。

// good
var user = {
  age: 0,
  name: 1,
  city: 3
};
// bad
var user = new Object();
user.age = 0;
user.name = 0;
user.city = 0;

括号

下列关键字后必须有大括号(即使代码块的内容只有一行)

if, else, for, while, do, switch, try, catch, finally, with

// good
if (condition) {
  doSomething();
}
// bad
if (condition) doSomething();

undefined 判断

永远不要直接使用 undefined 进行变量判断;使用 typeof 和字符串'undefined'对变量进行判断。

// good
if (typeof person === 'undefined') {
...
}
// bad
if (person === undefined) {
...
}

条件判断和循环最多三层

条件判断能使用三目运算符和逻辑运算符解决的,就不要使用条件判断,但是谨记不要写太长的三目运算符。如果超过 3 层请抽成函数,并写清楚注释。

this 的转换命名

对上下文 this 的引用只能使用'self'来命名

块级作用域

1、let 取代 var;

ES6 提出了两个新的声明变量的命令:let 和 const。其中,let 完全可以取代 var,因为两者语义相同,而且 let 没有副作用。

所以,建议不再使用 var 命令,而是使用 let 命令取代。

2、全局常量和线程安全。

在 let 和 const 之间,建议优先使用 const,尤其是在全局环境,不应该设置变量,只应设置常量。

const 优于 let 有几个原因。一个是 const 可以提醒阅读程序的人,这个变量不应该改变;另一个是 const 比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对 const 进行优化,所以多使用 const,有利于提高程序的运行效率,也就是说 let 和 const 的本质区别,其实是编译器内部的处理不同。

const 声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。

所有的函数都应该设置为常量。

解构赋值

1、使用数组成员对变量赋值时,优先使用解构赋值

const arr = [1, 2, 3, 4];
// good
const [first, second] = arr;
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];

2、函数的参数如果是对象的成员,优先使用解构赋值

// good
function getFullName(obj) {
  const {
    firstName,
    lastName
  } = obj;
}
​
// best
function getFullName({
  firstName,
  lastName
}) {}
// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
}

3、如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。

// good
function processInput(input) {
  return {
    left,
    right,
    top,
    bottom
  };
}
const {
  left,
  right
} = processInput(input);
// bad
function processInput(input) {
  return [left, right, top, bottom];
}

对象

1、对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法;

// good
const a = { x: null };
a.x = 3;
// bad
const a = {};
a.x = 3;

// if reshape unavoidable
const a = {};
Object.assign(a, { x: 3 });

2、如果对象的属性名是动态的,可以在创造对象的时候,使用属性表达式定义;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};
// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

3、另外,对象的属性和方法,尽量采用简洁表达法,这样易于描述和书写。

// good
const atom = {
  ref,
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};
var ref = 'some value';
// bad
const atom = {
  ref: ref,
  value: 1,
  addValue: function (value) {
    return atom.value + value;
  },
};

函数

1、那些使用匿名函数当作参数的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了 this;

// good
[1, 2, 3].map((x) => {
  return x\ * x;
});
// bad
[1, 2, 3].map(function (x) {
  return x\ * x;
});

2、箭头函数取代 Function.prototype.bind,不应再用 self/_this/that 绑定 this;

// good
const boundMethod = (...params) => method.apply(this, params);
// bad
const self = this;
const boundMethod = function (...params) {
  return method.apply(self, params);
}

3、不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替。因为 rest 运算符显式表明你想要获取参数,而且 arguments 是一个类似数组的对象,而 rest 运算符可以提供一个真正的数组;

// good
function concatenateAll(...args) {
  return args.join('');
}
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

4、使用默认值语法设置函数参数的默认值。

// good
function handleThings(opts = {}) {
  // ...
}
// bad
function handleThings(opts) {
  opts = opts || {};
}

Class

1、总是用 Class,取代需要 prototype 的操作。因为 Class 的写法更简洁,更易于理解;

// good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }
  pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
  }
}
// bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
}

2、使用 extends 实现继承,因为这样更简单,不会有破坏 instanceof 运算的危险。

// good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}
// bad
const inherits = require('inherits');

function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this._queue[0];
}

Vue规范

组件名为多个单词

组件名应该始终由多个单词组成,除了根组件 App,以及 <transition><component> 之类的 Vue 内置组件。这样做可以避免与现有以及未来的 HTML 元素产生冲突,因为所有的 HTML 元素名称都是单个单词的。

// good
app.component('todo-item', {
  // ...
})

export default {
  name: 'TodoItem',
  // ...
}

Prop定义

Prop 定义应尽量详细

在提交的代码中,prop 的定义应该尽量详细,至少指定其类型。

细致的prop定义有两个优势:

  • 它们写明了组件的 API,所以组件的设计用法可以通俗易懂;
  • 在开发环境下,如果为一个组件提供了格式不正确的 prop,Vue 将会告警,以帮助你捕获潜在的错误来源。
// good
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

为 v-for设置 key 值

在组件上必须始终以 key 配合 v-for,以便维护内部组件及其子树的状态。即使对于元素,维持可预测的行为也是一种好的做法。

// good
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

避免 v-if和 v-for 一起使用

永远不要在一个元素上同时使用 v-ifv-for

一般我们在两种常见的情况下会倾向于这样做:

  • 为了对列表中的项目进行过滤 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),返回过滤后的列表。
  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ulol)。
// good
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

为组件样式设置作用域

对于应用来说,样式在顶层 App 组件和布局组件中可以是全局的,但是在其它所有组件中都应该是有作用域的。

这条规则只适用于单文件组件。

// good
<template>
  <button class="button button-close">×</button>
</template>

<!-- 使用 `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>

私有 property 名称

使用模块作用域来确保外部无法访问到私有函数。如果无法做到这一点,就始终应该为插件、mixin 等不考虑对外公开 API 的自定义私有 property 使用 $_ 前缀。并附带一个命名空间,以回避和其它作者的冲突 (比如 $_yourPluginName_)。

  • Vue 使用 _ 前缀来定义其自身的私有 property,所以使用相同的前缀 (比如 _update) 有覆写实例 property 的风险。即便你检查确认了 Vue 当前版本没有用到这个 property 名,也不能保证和将来的版本没有冲突。
  • 对于 $ 前缀来说,其在 Vue 生态系统中的目的是暴露给用户的一个特殊的实例 property,所以把它用于私有 property 并不合适。
  • 不过,我们推荐把这两个前缀结合为 $_,作为一个用户定义的私有 property 的约定,以确保不会和 Vue 自身相冲突。
// good
const myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update() {
      // ...
    }
  }
}

组件文件

只要有能够拼接文件的构建系统,就把每个组件单独分成文件。

当你需要编辑一个组件,或查阅一个组件的用法时,这种做法可以帮助你更快速地找到它。

// good
components/
|- TodoList.js
|- TodoItem.js

components/
|- TodoList.vue
|- TodoItem.vue
// bad
app.component('TodoList', {
  // ...
})

app.component('TodoItem', {
  // ...
})

单文件组件文件的大小写

单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。建议使用PascalCase格式。

// good
components/
|- MyComponent.vue
// bad
components/
|- mycomponent.vue

紧密耦合的组件名称

与父组件紧密耦合的子组件应该以父组件名作为前缀命名。

/ good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
// bad
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue

模板中使用简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。

// good
<template>
  <p>{{ normalizedFullName }}</p>
</template>

// 复杂表达式已经移入一个计算属性
computed: {
    normalizedFullName: function () {
        return this.fullName.split(' ').map(function (word) {
            return word[0].toUpperCase() + word.slice(1)
        }).join(' ')
    }
}
// bad
<template>
  <p>
    {{ fullName.split(' ').map(function (word) { return word[0].toUpperCase() +
    word.slice(1) }).join(' ') }}
  </p>
</template>

指令缩写

指令缩写 (用 : 表示 v-bind:@ 表示 v-on: 和用 # 表示 v-slot) 应该要么始终使用,要么始终不使用。

// good
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>

<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>

<input
  @input="onInput"
  @focus="onFocus"
>

<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>

<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template v-slot:footer>
  <p>Here's some contact info</p>
</template>

<template #header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>

标签顺序保持一致

单文件组件应该总是让标签顺序保持为

// good
<template>...</template>

<script>...</script>

<style>...</style>
// bad
<template>...</template>

<style>...</style>

<script>...</script>

scope 中的元素选择器

元素选择器应该避免在 scope中出现。

在 scope 样式中,类选择器要比元素选择器更好,因为大量地使用元素选择器是很慢的。

// good
<template>
  <button class="btn btn-close">×</button>
</template>

<style scoped>
.btn-close {
  background-color: red;
}
</style>
// bad
<template>
  <button>×</button>
</template>

<style scoped>
button {
  background-color: red;
}
</style>

Vue Router 规范

router 中的命名规范

path、childrenPoints 命名规范采用 camelCase命名规范(尽量 vue 文件的目录结构保持一致,因为目录、文件名都是 camelCase,这样很方便找到对应的文件)

name 命名规范采用 KebabCase 命名规范且和 component 组件名保持一致!(因为要保持 keep-alive 特性,keep-alive 按照 component 的 name 进行缓存,所以两者必须高度保持一致)

// 动态加载
export const reload = [{
        path: '/reload',
        name: 'reload',
        component: Main,
        meta: {
            title: '动态加载',
            icon: 'icon iconfont'
        },
        children: [{
            path: '/reload/smartReloadList',
            name: 'SmartReloadList',
            meta: {
                title: 'SmartReload',
                childrenPoints: [{
                        title: '查询',
                        name: 'smartReloadSearch'
                    },
                    {
                        title: '执行reload',
                        name: 'smartReloadUpdate'
                    },
                    {
                        title: '查看执行结果',
                        name: 'smartReloadResult'
                    }
                ]
            },
            component: () =>
                import('@/views/reload/smart-reload/smart-reload-list.vue')
        }]
    }
]

Vue项目目录规范

目录说明

src 源码目录
|-- api 所有 api 接口
|-- assets 静态资源,images, icons, styles 等
|-- components 公用组件
|-- config 配置信息
|-- constants 常量信息,项目所有 Enum, 全局常量等
|-- directives 自定义指令
|-- filters 过滤器,全局工具
|-- datas 模拟数据,临时存放
|-- lib 外部引用的插件存放及修改文件
|-- mock 模拟接口,临时存放
|-- plugins 插件,全局使用
|-- router 路由,统一管理
|-- store vuex, 统一管理
|-- themes 自定义样式主题
|-- views 视图目录
| |-- role role 模块名
| | |-- roleList role 列表页面文件夹
| | | |--index.vue
| | | |--components role 模块通用组件文件夹
| | | | |--RoleHeader.vue

| | |-- roleAdd role 新建页面文件夹

api目录

文件、变量命名要与后端保持一致。

此目录对应后端 API 接口,按照后端一个 controller 一个 api js 文件。若项目较大时,可以按照业务划分子目录,并与后端保持一致。

api 中的方法名字要与后端 api url 尽量保持语义高度一致性。

对于 api 中的每个方法要添加注释,注释与后端 swagger 文档保持一致。
后端:
url: EmployeeController.java
/employee/add
/employee/delete/{id}
/employee/update

前端:
employee.js
// 添加员工
addEmployee: (data) => {
    return postAxios('/employee/add', data)
},
// 更新员工信息
updateEmployee: (data) => {
    return postAxios('/employee/update', data)
},
// 删除员工
deleteEmployee: (employeeId) => {
    return postAxios('/employee/delete/' + employeeId)
},

assets 目录

assets 为静态资源,里面存放 images, styles, icons 等静态资源,静态资源命名格式为 kebab-case

|assets
|-- icons
|-- images
| |-- background-color.png
| |-- upload-header.png
|-- styles

components 目录

此目录应按照组件进行目录划分,目录命名为 PascalCase,组件命名规则也为 PascalCase

|components
|-- ErrorLog
| |-- index.vue
| |-- index.less
|-- MarkdownEditor
| |-- index.vue
| |-- index.js
|-- PascalCase

router 与 store 目录

这两个目录一定要将业务进行拆分,不能放到一个 js 文件里。

router 尽量按照 views 中的结构保持一致。

store 按照业务进行拆分不同的 js 文件。

views 目录

命名要与后端、router、api 等保持一致

components 中组件要使用 PascalCase 规则

|-- views 视图目录
| |-- role role 模块名
| | |-- roleList role 列表页面文件夹
| | | |--index.vue
| | | |--components role 模块通用组件文件夹
| | | | |--RoleHeader.vue

| | |-- roleAdd role 新建页面文件夹
| | | |--index.vue
| | | |--components role 模块通用组件文件夹
| | | | |--XoleXeader.vue

\