本文档适用于所有前端项目的开发,为了方便开发团队和个人统一规范风格而作。文档中包含了开发中需要注意的事项以及参考建议,本文档不单是一篇规范要求,同时也是前端开发手册,当你不知如何做比较好的时候,请参考本文档中推荐的建议来开发。
全部采用kebab-case
命名,字母小写,以短横分隔单词。
正例:my-project-name
反例:my_project_name / myProjectName
kebab-case
命名,字母小写,以短横分隔单词。正例:scripts / styles / components / images / assets / views / utils / custom-themes / layout / img / doc / api / router
反例:: script / style / util / imgs / docs / apis / routers
全部采用kebab-case
命名,字母小写,以短横分隔单词。
正例:render-dom.js / home.css / home-banner.png
反例:renderDom.js / Home.css / homeBanner.png
正例:
rmb / shenzhen / guangdong / henan / wuhan
logout / login / err / res
反例:
daZhePromotion 打折 / getPingFen 获得评分 / let 年龄 = 3
AbstractClass 缩写为 AbsClass / CalculateAmount 缩写为 CaAmount
文档类型声明位于 HTML
文档的第一行,大小写必须遵守。
正例:
反例:
必须在文档中声明编码集 charset
,且文件本身编码需要保持一致,推荐都使用 utf-8
。制定字符编码的 meta
必须是 head
中第一个元素。
<head>
<meta charset="utf-8" />
head>
在 head
中声明,跟随在 charset
之后,且自闭合标签。
<head>
<meta charset="utf-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="this is a description" />
head>
必须声明 title
,且紧随最后一个 meta
之后。
<head>
<meta charset="utf-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="this is a description" />
<title>页面标题title>
head>
引入了外部样式,紧随 title
之后,同时网站的标签也需要设置 icon
图标。
<head>
<meta charset="utf-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="this is a description" />
<title>页面标题title>
<link rel="icon" href="http://xxx.com/favicon.ico" />
<link href="http://xxx.com/index.css" rel="stylesheet" />
head>
由于 js
加载会阻塞页面渲染,script
元素需要声明在 body
标签的尾部,并且是需要闭合标签的。
<body>
<script src="http://xxx.com/index.js">script>
body>
以下是一个整合后 html
代码规范示例:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="this is a description" />
<title>页面标题title>
<link href="http://xxx.com/index.css" rel="stylesheet" />
head>
<body>
<h1>这是一个标题h1>
<script src="http://xxx.com/index.js">script>
body>
html>
统一使用 2
个缩进,不允许使用 Tab
制表符,全部替换为空格缩进。
<body>
<div>代码为2个空格缩进div>
body>
标签元素名统一小写,不可大小写混合,必须有结尾标签相呼应。
正例:
反例:
标签属性和自定义属性采用kebab-case
命名,字母小写,以短横分隔单词,并且必须是双引号。
正例:
<div class="text-error">div>
<div data-id="1" data-user-name="xiaoming">div>
反例:
<div class="text-error">div>
<div dataId="1" dataUserName="xiaoming">div>
标签的属性需要按照顺序来书写,以下规则是极力推荐使用的。
样式名
class
全局唯一标识
id
name
自定义属性(可复用的元素样式排首位)
data-xx
资源地址/类型(可复用的元素样式排首位)
href
src
for
type
提示性的内容
placeholder
title
alt
辅助性的内容
disabled
readonly
required
checked
selected
html
中包含了大量的语义化标签,推荐开发中合理的使用语义化的标签,便于页面结构化,易于阅读,更好的便于网页内容抓取。
<body>
<header>
header>
<main>
main>
<footer>
footer>
body>
以下列举出常见的语义化标签集合:
标签 | 描述 |
---|---|
header | 头部内容 |
nav | 导航 |
main | 主要内容 |
footer | 底部内容 |
aside | 侧边栏 |
section | 文档中的节 |
article | 文章 |
time | 时间 |
mark | 文本标记 |
除了使用语义化标签,一个网页还需要遵守一定的结构和顺序,自上而下,从左到右,保持一定的顺序。
<div class="app-container">
<div class="header-container">div>
<div class="main-container">
<div class="left-box">div>
<div class="content-box">div>
div>
<div class="footer-container">div>
div>
标签的
alt
属性指定了替代文本,在图像无法显示时候替代的文案显示。
<img src="http://www.xxx.com/dd.jpg" alt="加载中" />
<body>
<div>内容div>
body>
<body>
<div>内容div>
body>
有时我们需要用注释来标记某段代码的开头和结束,就需要用到闭合注释(本规范中叫做闭合注释)。
<body>
<div>内容div>
body>
使用 link
标签引入外部css
,写在 head
中。
<head>
<link href="//xxx.com/index.css" rel="stylesheet" />
head>
导入 css
文件使用 @import
,忽略使用 url
导入。
// 推荐
@import "./styles/index.css";
// 不推荐
@import url("./styles/index.css");
统一使用 2
个缩进,不允许使用 Tab
制表符,全部替换为空格缩进。
.page-title {
font-size: 18px;
}
样式中有很多属性是可以简写的,推荐这样的方式,易读且减少了复杂度。
正例:
.page-title {
padding: 10px 30px 10px 30px;
border: 1px solid red;
}
反例:
.page-title {
padding-top: 10px;
padding-right: 30px;
padding-bottom: 10px;
padding-left: 30px;
border-width: 1px;
border-style: solid;
border-color: red;
}
正例:
.page-title {
padding: 0;
margin: 0 10px;
}
反例:
.page-title {
padding: 0px 0px 0px 0px;
margin: 0px 10px 0px 10px;
}
在嵌套写选择器链的时候,应该减少后代选择器,改用直接子选择器,也就是多写个 >
符号定位到元素,优点是 css 渲染效率更高。
正例:
.tabs > .tabs-title {
font-size: 18px;
}
反例:
.tabs .tabs-title {
font-size: 18px;
}
应该避免使用这两个选择器,以防止污染全局样式。
正例:
<head>
<style>
.page-title {
font-size: 18px;
}
.page-title .user-name {
color: red;
}
.my-demo {
font-weight: bold;
}
style>
head>
<body>
<div class="page-title">
<div class="user-name">div>
div>
<p class="my-demo">p>
body>
反例:
<head>
<style>
#page-title {
font-size: 18px;
}
p {
font-weight: bold;
}
style>
head>
<body>
<div id="page-title">div>
<p>p>
body>
避免直接在标签写 style
行内样式,这样样式会很分散不易维护,最好是把样式提取到一个 class
中。
正例:
<style>
.text-red {
font-size: 18px;
color: red;
}
style>
<body>
<div class="text-red">div>
body>
反例:
<body>
<div style="font-size:18px; color:red;">div>
body>
非必须不要使用 !important
强制设置优先级最高,滥用出现问题会导致代码难以维护。
在使用 less 或者 scss 预编译语言时候,应该避免层级过多嵌套。过多的层级会显得很乱,难以阅读和维护,建议最多层级在 3
层最佳,同样层次多也可以拆分开。
正例:
/* 减少层级 */
.main {
.page-title {
font-size: 18px;
font-weight: bold;
}
}
/* 拆分层级也可以 */
.main {
.page {
height: 100px;
}
}
.page {
.title {
font-size: 18px;
font-weight: bold;
}
}
反例:
.main {
.page {
height: 100px;
.title {
font-size: 18px;
font-weight: bold;
}
}
}
不能使用*选择器来给元素设置样式,这样会使所有的元素都起作用。
反例:
/* 不能这样写 */
* {
padding: 0;
margin: 0;
}
不要把样式都放同一行,这样是不允许的。
反例:
/* 不能这样写 */
.text-red { font-size:18px;color:red; }
非必须避免使用 !important
,因为会强制设置为优先级最高,滥用出现问题导致代码难以维护。
rgba
颜色值,最后一个透明度值需要写完整的小数。正例:
.demo {
color: #abcdef;
color: #333;
color: #ccc;
color: rgba(255, 255, 255, 0.8);
}
反例:
.demo {
color: #abcdef;
color: #333333;
color: #cccccc;
}
按照一定的顺序书写样式,可以提升浏览器渲染 dom
的性能,本规范中该项非必须。
.hotel-content {
/* 1. 定位 */
display: block;
position: absolute;
left: 0;
top: 0;
/* 2. 盒子模型 */
width: 50px;
height: 50px;
margin: 10px;
border: 1px solid black;
/* 3. 文本 */
font-size: 18px;
color: red;
text-align: center;
/* 4. 背景 */
background: #ccc;
/* 5. 其他 */
transition: all 0.2s;
}
第一种:
BEM
命名规范,BEM
命名就是通过 块(block)
、元素(element)
、修饰符(modifier)
来连接组合元素之间的关系,例如: type-block__element--modifier
。- 单中划线:连接某个块与下面的子模块。
__ 双下划线:连接块和块的子元素
– 双中划线:描述该块或者子元素的一种状态
<head>
<style>
/* 原生CSS写法 */
.tabs {
}
.tabs .tabs-inner .tabs-inner__button {
}
.tabs .tabs-inner .tabs-inner__button .tabs-inner__button--primary {
}
/* Less或者Scss写法 */
.tabs {
&-inner {
&__button {
&--primary {
}
}
}
}
style>
head>
<body>
<div class="tabs">
<div class="tabs-inner">
<button class="tabs-inner__button tabs-inner__button--primary">
选项1
button>
<button class="tabs-inner__button">选项2button>
div>
div>
body>
第二种(推荐):
<head>
<style>
/* 原生CSS写法 */
.message .title {
}
.message .content {
}
/* Less或者Scss写法 */
.message {
.title {
}
.content {
}
}
style>
head>
<body>
<div class="message">
<div class="title">div>
<div class="content">div>
div>
body>
/*
开头,以 */
结束,注意前后各空一格。less
或者 scss
中也可以使用双斜杠 //
作为单行注释/* 单行注释 */
.tabs {
}
多行需要换行,每行前面没有 *
号,每行对齐前面空一格。
/*
多行注释1
多行注释2
*/
.tabs {
}
- 块注释以 /*
开头,以 */
结束,前后各空一格。
- 块注释用于划分某个模块的标记,需要写上块描述。
/* 用户模块样式 */
.user {
}
.user .user-name {
}
/* 弹框模块样式 */
.modal {
}
.modal .modal-title {
}
用于标注修改、待办等信息,需要写上作者和时间等信息。
/* TODO: 标签页样式待补充 by 小红 2022-03-13 18:32 */
/* BUGFIX: 修复标签页的bug by 小红 2022-03-13 18:32 */
.tabs {
}
文件注释以 /**
开头,以 */
结束。
/**
* css文件描述
* @author: 小红
* @update: 2021-04-13 18:32
*/
/* 标签页 */
.tabs {
}
在编写页面时候,class
样式起名是件很痛苦的事,多写几层就没词可用了,以下列举出不同场景常用的名称,帮你找到合适你页面的样式名称。
使用 kebab-case 命名,字母一律小写,用短横分隔单词。
如果单词过长,需要使用言简意赅的缩写单词表示
index.css // 一般用于首页建立样式
head.css // 头部样式,当多个页面头部设计风格相同时使用。
base.css // 共用样式。
style.css // 独立页面所使用的样式文件。
global.css // 页面样式基础,全局公用样式,页面中必须包含。
layout.css // 布局、版面样式,公用类型较多时使用,一般用在首页级页面和产品类页面中
module.css // 模块,用于产品类页,也可与其它样式配合使用。
master.css // 主要的样式表
columns.css // 专栏样式
themes.css // 主体样式
forms.css // 表单样式
mend.css // 补丁,基于以上样式进行的私有化修补。
print.css // 打印
page // 代表整个页面,用于最外层。
wrap // 外套,将所有元素包在一起的一个外围包,用于最外层
wrapper // 页面外围控制整体布局宽度,用于最外层
container // 一个整体容器,用于最外层
head|header // 页头区域,用于头部
nav // 导航条
content // 内容,网站中最重要的内容区域,用于网页中部主体
main // 网站中的主要区域(表示最重要的一块位置),用于中部主体内容
column // 栏目
sidebar // 侧栏
foot|footer // 页尾、页脚。网站一些附加信息放置区域,(或命名为 copyright)用于底部
left|right|center // 左右中
nav|navbar|navigation|nav-wrapper // 导航条或导航包,代表横向导航
topnav // 顶部导航
mainbav // 主导航
subnav // 子导航
sidebar // 边导航
left-sidebar|sidebar-l // 左导航
right-sidebar|sidebar-r // 右导航
title // 标题
summary // 摘要
menu // 菜单,区域包含一般的链接和菜单
submenu // 子菜单
drop // 下拉
dorp-menu // 下拉菜单
links // 链接菜单
logo // 标记网站logo标志
banner // 标语、广告条、顶部广告条
login // 登陆,(例如: 登录表单 form-login)
loginbar // 登录条
register // 注册
tool|toolbar // 工具条
search // 搜索
searchbar // 搜索条
searchlnput // 搜索输入框
shop // 功能区,表示现在的
icon // 小图标
label // 商标
homepage // 首页
subpage // 二级页面子页面
hot // 热门热点
list // 文章列表,(例如: 新闻列表 list-news)
scroll // 滚动
tab // 标签
sitemap // 网站地图
msg|message // 提示信息
current // 当前的
joinus // 加入
status // 状态
btn // 按钮,(例如: 搜索按钮可写成 btn-search)
tips // 小技巧
note // 注释
guild // 指南
arr|arrow // 标记箭头
service // 服务
breadcrumb // (即页面所处位置导航提示)
download // 下载
vote // 投票
news // 新闻
siteinfo // 网站信息
partner // 合作伙伴
link|friendlink // 友情链接
copyright // 版权信息
siteinfoCredits // 信誉
siteinfoLegal // 法律信息
文件名不得包含空格。
采用 kebab-case
命名,字母小写,以短横分隔单词。
引入资源推荐使用相对路径,不指定具体的协议(http:
,https:
)。
<script src="//cdn.com/render-dom.min.js">script>
全部采用 kebab-case
命名,字母小写,以短横分隔单词。
正例:render-dom.js / home.js / get-user-info.js
反例:renderDom.js / Home.js / getUserInfo.js
camelCase
驼峰命名。类型+对象描述
的规则。let userId = 1;
var userName = "小红";
let tableTitle = "表格标题";
常量名使用全部大写命名,多个单词中间用_
下划线分隔。
正例:
const AMOUNT = 10;
const MAX_COUNT = 10;
const APPROVE_TYPE = { DOING: 1, APPROVED: 2 };
反例:
const amount = 10;
const MAXCOUNT = 10;
const APPROVETYPE = { DOING: 1, APPROVED: 2 };
形参本身是可以任意定义的,统一是为了更方便地复用/合并代码。
参数 | 描述 | 示例 |
---|---|---|
i | 索引 | switchTab(i) {} |
i,v | 值 | list.forEach((i,v)=>{}) |
e | 事件 | onInput(e) {} |
obj | 对象 | for(let obj of userList){} |
arr | 数组 | let arr = []; |
fun | 函数 | callback(fun){} |
item | 列表项 | list.map(item=>{}) |
item,index | vue 遍历数组 | v-for="(item,index) in arr |
key,value | 键值对 | for (let [key, value] of arr) {} |
函数名使用 camelCase
驼峰命名,命名为动词
形式,或者动词+名词
形式。
function getName() {}
function setName() {}
function canEdit() {}
function addUser() {}
function hasCode() {}
如果是内部使用的私有函数,需要用 _
符号作为前缀,写到文件最下面,并用按照功能进行分组,写上块注释。
/* 格式化内容 */
function _formatText() {}
function _formatMoney() {}
/* 设置日期 */
function _setDate() {}
function _setMonth() {}
增删改查和查看详情,统一使用下面的单词。
add /delete /update / get / detail
附常用的动词列表:
get 获取 | set 设置
add 增加 | delete 删除
start 启动 | launch 启动 | run 运行 | ready 准备 | stop 停止
open 打开 | close 关闭
read 读取 | write 写入 | save 保存
load 载入 | unload 不加载
create 创建 | destroy 销毁
begin 开始 | start 开始 | end 结束
from 从.. | to 到..
backup 备份 | restore 恢复
import 导入 | export 导出
print 打印 | afterprint 打印机关闭
split 分割 | merge 合并
touch 轻按 | click 点击 | dblclick 双击 | drag 拖拽
inject 注入 | extract 提取
attach 附着 | detach 脱离
bind 绑定 | separate 分离
view 查看 | browse 浏览
edit 编辑 | modify 修改 | update 更新 | upgrade 升级
select 选取 | change 改变 | mark 标记
copy 复制 | paste 粘贴
do 做 | undo 撤销 | redo 重做
insert 插入 | remove 移除 | revoke 撤销 | revert 复原 | reset 重置
clean 清理 | clear 清除
index 索引 | sort 排序
find 查找 | search 搜索
increase 增加 | decrease 减少
rise 上涨 | fall 下降
play 播放 | pause 暂停
forward 前进 | rewind 后退
compile 编译 | execute 执行
suspend 挂起 | complete 完成
debug 调试 | trace 跟踪
observe 观察 | listen 监听
build 构建 | publish 发布
input 输入 | output 输出
encode 编码 | decode 解码
encrypt 加密 | decrypt 解密
compress 压缩 | decompress 解压缩
pack 打包 | unpack 解包
parse 解析 | emit 生成
connect 连接 | disconnect 断开
send 发送 | receive 接收
upload 上传 | download 下载
refresh 刷新 | synchronize 同步
lock 锁定 | unlock 解锁
check out 签出 | check in 签入
submit 提交 | commit 交付
push 推 | pull 拉
expand 展开 | collapse 折叠
active 激活 | deactive 未激活
wait 等待 | process 进程中 | finish 完成
enter 进入 | exit 退出
abort 中止 | quit 离开
obsolete 废弃 | depreciate 废旧
collect 收集 | aggregate 聚集
统一使用 2
个缩进,不允许使用 Tab
制表符,全部替换为空格缩进。
let userId = 2;
function say() {
console.log("hello world!");
}
统一每行最大 80
个字符,超出自动换行,VsCode编辑器
默认是 80
个字符。
声明变量推荐使用 let
代替 var
声明。
var
会有变量提升问题,容易造成变量污染。
let userId = 2;
let userName = "小红";
const
除了声明常量,也可以声明一些不可改变的变量名。
正例:
const name = "小红";
function setUserInfo(name) {}
const userList = [
{ id: 1, name: "小红" },
{ id: 2, name: "小明" },
];
for (let i in userList) {
const user = userList[i];
// do other something
}
同时声明多个变量,不需要每个都用 let
起一行声明,可以合并到一起。
正例:
let id = 2,
total = 20,
isRead = true,
hasNameIndex = 2;
反例:
let id = 2;
let total = 20;
let isRead = true;
let hasNameIndex = 2;
在有变量拼接的场景下,尽量使用模板字符串来代替普通的“+”拼接,这样会使得代码结构更清晰。
正例:
let id = 2,
name = "小红",
sex = "女",
age = 18;
let str = `我叫${name},性别${sex},我今年${age}岁了。`;
反例:
let id = 2,
name = "小红",
sex = "女",
age = 18;
let str = "我叫" + name + ",性别" + sex + ",我今年" + age + "岁了。";
使用 this
关键字时候,统一用 self
来表示并在上下文中使用。
let self = this;
setTimeout(function () {
self.say();
}, 1000);
避免大量使用 console.log
,这样很影响代码执行效率。打印日志只作用看调试内容输出,不能代码里随意到处都写,在代码调试完成之后必须要删除,如果是脚手架项目,需要在打包的时候过滤掉打印日志。
在简单的判断条件下,使用三元运算符能更加的精简代码,增加可读性。
正例:
let flag = 1;
console.log(flag == 1 ? "是" : "否");
// 甚至再多一层判断也可以
let status = 0;
console.log(status == 0 ? "代办" : status == 1 ? "已办" : "无");
反例:
// 这里 6 行代码,用三元运算符 2 行搞定
let flag = 1;
if (flag == 1) {
console.log("是");
} else {
console.log("否");
}
使用字面量创建对象,更加简洁。
正例:
let user = {
id: 1,
name: "小红",
sex: "女",
age: 18,
};
反例:
let user = new Object();
user.id = 1;
user.name = "小红";
user.sex = "女";
user.age = 18;
变量名是对象的属性值,推荐使用简写方式,属性和变量名相同的可以直接简写赋值,同时对象中的其他属性也要用简称,使对象的结构更加的清晰明了。
正例:
let name = "小红",
age = 18;
let user = {
name,
age,
sex: "女",
mobile: "1300000000",
};
反例:
let userName = "小红",
userAge = 18;
let user = {
userName: userName,
userAge: userAge,
userSex: "女",
userMobile: "1300000000",
};
浅拷贝对象
let age = 18;
let user = {
id: 1,
name: "小红",
};
let copy = { ...user, age };
浅拷贝数组
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [...arr1, ...arr2];
如果是脚手架项目,需要本身配置的babel和eslint支持。
let user = null;
// 如果直接取 user.sex 是会报错的,用了可选链后可以不用判断直接取了
console.log(user?.sex);
`
function getTimestamp(date = new Date()) {
return date.getTime();
}
console.log(getTimestamp());
如果传参超过 3 个,建议封装为对象传参。
正例:
let user = {
id: 1,
name: "小红",
sex: "女",
age: 18,
};
function addUser({ id, name, sex, age }) {}
反例:
let id = 1;
let name = "小红";
let sex = "女";
let age = 18;
function addUser(id, name, sex, age) {}
正例:
const currentDate = new Date();
["小红", "小明", "小花"].forEach((name) => sayHello(name));
反例:
const cD = new Date();
["小红", "小明", "小花"].forEach((a) => sayHello(a));
正例:
// 表示3分钟的毫秒数
setTimeout(getCount, 3 * 60 * 1000);
反例:
// 只有一个比较大的数字,无法直观的知道到底多久
setTimeout(getCount, 180000);
在一个函数中,如果处理逻辑很多,需要拆分多个方法来处理。
正例:
function main() {
// 逻辑拆分为不同的方法来处理
foo1();
foo2();
foo3();
}
// 逻辑1
function foo1() {}
// 逻辑2
function foo2() {}
// 逻辑3
function foo3() {}
反例:
function main() {
// 逻辑1
// 逻辑2
// 逻辑3
}
如果条件比较少,条件通过则执行函数,这种可以在单行用 &&
符号判断并执行代码。
正例:
let div = document.getElementById("div");
div && div.append("child");
反例:
let div = document.getElementById("div");
if (div) {
div.append("child");
}
推荐使用箭头函数,箭头函数能避免 this
作用域不确定的问题,而且使代码更加简洁。
const getUserInfo = () => {};
当有比较多判断的情况下,把判断前置,使我们的代码结构更加清晰分明。
正例:
function submit() {
// 校验
if (!this.listQuery.name) return;
if (!this.listQuery.status) return;
if (!this.listQuery.type) return;
// 校验通过,正常执行代码
addUser();
}
反例:
function submit() {
// 校验 name
if (this.listQuery.name) {
// 校验 status
if (this.listQuery.status) {
// 校验 type
if (this.listQuery.type) {
// 校验通过,正常执行代码
addUser();
}
}
}
}
// 使用Promise
function getMsg(res) {
return new Promise((resolve, reject) => {
if (res.code == 200) {
resolve(res);
} else {
reject(res);
}
});
}
getMsg({ code: 200, result: [] }).then((res) => {});
// 使用ssync/await
async function login() {
// 先执行获取用户信息
let user = await getUserInfo();
// 再执行获取菜单,传入用户的id
let menu = await getMenuList(user.id);
console.log(user, menu);
}
使用 Eslint
做代码检查,格式化统一用Prettier Code
插件。
//
开头,可以在代码末尾,也可以单独一行。function setId(id) {
// 可以在代码上面注释
this.id = id;
}
let name = "小红"; // 如果是声明变量的注释,推荐写在行尾
let birthday = "2000-10-02"; // 生日
/*
开头,以 */
结束。/*
开头与后面内容空一格。/* xx */
注释内容都在同一行,推荐使用 //
单行注释。/*
注释内容1
注释内容2
*/
setTitle();
/*
开头,以 */
结束,前后各空一格。/* 获取列表 */
function getList() {}
/* 新增和修改用户 */
function addUser() {}
function updateUser() {}
方法注释以 /**
开头,以 */
结束,方法注释要使用注释标签
说明方法的参数,返回结果等等内容。
/**
* 获取用户信息
* @param {Number} id 用户id
* @returns {Object} 返回用户对象
*/
function getUserInfo(id) {}
方法的注释标签
必须要写,参考JSDoc 文档,以下是常用的标签说明。
标签 | 语法 |
---|---|
@description | @description 描述说明 |
@param | @param {参数类型} 参数说明 |
@returns | @returns {返回类型} 返回结果说明 |
@author | @author 作者 |
@version | @version 版本号 |
@example | @example 示例代码 |
@todo | @todo 待办的内容描述 |
/**
* 文件说明
* @author 作者名
* @date 2022-03-23
*/
/*!
* @lime-util/core v3.0.10
* Copyright 2021-2022, Gaoshiwei <[email protected]>
* Licensed under the MIT License.
*/
项目基于 vue-cli4
开发,项目目录结构如下:
├── .vscode # vscode 的项目配置
├── build # 构建相关
├── dist # 打包文件
├── mock # 项目mock 模拟数据
├── public # 静态资源
│ │── favicon.ico # favicon 图标
│ └── index.html # html模板
├── src # 源代码目录
│ ├── api # 所有 api接口
│ ├── assets # 图片,字体静态资源
│ ├── components # 全局公用组件
│ ├── constants # 全局常量
│ ├── directives # 全局指令
│ ├── filters # 全局过滤器
│ ├── mixins # 全局混入
│ ├── plugin # 全局插件
│ ├── icons # 全局 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ │── themes # 主题
│ └── index.less # 样式主入口
│ ├── utils # 全局公用方法
│ ├── vendor # 公用 vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ ├── permission.js # 权限管理
│ └── settings.js # 项目设置文件
├── test # 测试
├── .env.xxx # 环境变量配置
├── .eslintignore # eslint 忽略配置
├── .eslintrc.js # eslint 配置项
├── .gitignore # git 忽略配置
├── .prettierrc # prettier 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化 CI 配置
├── LICENSE # license证书
├── postcss.config.js # postcss 配置
├── vue.config.js # vue config 配置
├── package.json # package.json
├── package-lock.json # package-lock.json
└── README.md # README 文件
views
目录下单文件组件统一采用kebab-case
命名,字母小写,以短横分隔单词。html标签
元素冲突。描述性单词
开头,以修饰性单词
结尾。2~3
个单词最佳,单词不能过长。正例:
login.vue
home.vue
user-info.vue
base-config.vue
search-button-clear.vue
反例:
# 以下命名是不规范的,快看有没有中招
Login.vue
Home.vue
span.vue
userInfo.vue
base_Config.vue
search_button_clear.vue
基础组件文件名为 base
开头,使用完整单词而不是缩写,表示基础展示类、无逻辑或无状态的组件。
正例:
components/
├─ base-button.vue
└─ base-table.vue
反例:
components/
├─ my-button.vue
└─ my-table.vue
单例组件文件名为 the
开头,作用是在每个页面只用一次,比如网页顶部的导航栏
,底部的版权地址信息
,这些组件不会接受 prop 传参。
正例:
components/
├─ the-header.vue
└─ the-footer.vue
反例:
components/
├─ header.vue
└─ footer.vue
如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上,因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。
正例:
components/
├─ todo-list.vue
├─ todo-list-item.vue
└─ todo-list-item-button.vue
反例:
components/
├─ todo-list.vue
├─ todo-item.vue
└─ todo-button.vue
在当前功能的目录新建 components
文件夹,所有声明的组件放在里面,采用 PascalCase 帕斯卡
命名,并且功能目录需要有 index.vue
入口文件。
src/views/user-info
├─ index.vue
└─ components/
├─ BaseInfo.vue
└─ UserInfo.vue
统一放到工程目录的 src/components
下面,目录名称采用 PascalCase 帕斯卡
命名,并且每个公用组件目录必须有 index.vue
入口文件和 README.md
文件 Api 说明文档。
在编写 README.md
文档时也需要注意,Api 文档中Attribute
属性 和Event
事件的命名都是要用kebab-case
命名,不能用驼峰命名。
src/components/
├─ PageHeader
├─ index.vue
└─ README.md
├─ UploadData
├─ index.vue
└─ README.md
自定义组件的使用按照以下规范,除了在Dom模板
中用kebab-case
命名,其他都是用PascalCase 帕斯卡
命名。
如果是用 viewUI
,element-ui
,ant-vue
等 UI 框架中的组件则按照官方示例的命名规则使用。
在单文件组件
中用 PascalCase 帕斯卡
命名,如果组件中没有内容则需要自闭合标签。
<template>
<div class="app-container">
<UserInfo :user-detail="userDetail" />
div>
template>
在字符串模板
template 中使用组件,采用 PascalCase 帕斯卡
命名,如果组件中没有内容则需要自闭合标签。
Vue Vue({
template:`<UserInfo :user-detail="userDetail" />`
});
在 JS/JSX
的 render 函数中使用组件,采用 PascalCase 帕斯卡
命名,如果组件中没有内容则需要自闭合标签。
new Vue({
el: '#app',
render: function (h) {
return (
<UserInfo :user-detail="userDetail" />
)
}
})
在 DOM 模板中需要用kebab-case
命名,因为是直接在 html
页面中使用,html
是会忽略大小写,并且不能自闭合
标签。
<html>
<body>
<div class="app-container">
<user-info :user-detail="userDetail">user-info>
div>
body>
html>
布尔值是两种逻辑状态的变量真
和假
,对应 true
和 false
,也可以用数字 1
和 0
表示真假。
推荐命名方式为 is
+ 动词(现在进行时)
/ 形容词
,但是有些场景下也可以不用写 is
开头。
{
"isShow": "是否显示",
"isVisible": "是否可见",
"isLoading": "是否处于加载中",
"isConnecting": "是否处于连接中",
"isValidating": "正在验证中",
"isDoing": "正在进行中",
"isRunning": "正在运行中",
"isListening": "正在监听中"
}
{
"isDisabled": "是否禁用",
"isEdit": "是否可编辑",
"isAdd": "是否可增加",
"isUpdate": "是否可修改",
"isRemove": "是否可删除",
"isClearable": "是否可清空",
"isReadonly": "是否只读",
"isExpand": "是否可展开",
"isShrink": "是否可收缩",
"isChecked": "是否选中",
"isClick": "是否可点击",
"isDrag": "是否可拖拽",
"isOpen": "是否打开",
"isClose": "是否打开",
"isChoice": "是否选择(复选框,单选框)",
"isSelect": "是否选择(下拉框,列表)",
"isConfig": "是否可配置"
}
{
// 组合1-基础表单
"addModal": "新增弹框",
"editModal": "编辑弹框",
"detailModal": "详情弹框",
// 组合2-业务相关
"syncDataModal": "同步数据弹框",
"addUserModal": "新增用户弹框",
"userInfoModal": "用户信息弹框",
"assignPermissionModal": "分配权限弹框",
"departTreeModal": "部门树弹框"
}
和时间相关的变量,必须带有date / month / year / time / hour / minutes / seconds
等等和时间相关的。
{
"now": "此刻",
"today": "今天",
"yesterday": "昨天",
"tomorrow": "明天",
"workday": "工作日",
"weekend": "周末(周六和周日)"
}
{
"startDate": "是否禁用",
"endDate": "是否可编辑",
"startTime": "是否可增加",
"endTime": "是否可修改"
}
{
"prevMonth": "上一月",
"nextMonth": "下一月",
"prevYear": "上一年",
"nextYear": "下一年",
"beforeCurrentDate": "当前日期之前",
"afterCurrentDate": "当前日期之后"
}
数组主要有 复数形式
和 xxxList
形式,简单的区别是 复数形式
的适合前端本身声明的数组,xxxList 形式的适合后台接口返回的数组。
{
// 前端声明变量
"users": "用户列表",
"menus": "菜单列表",
"selectNodes": "选中节点列表",
// 后台返回数据接收变量
"userList": "用户列表",
"menuList": "菜单列表",
"statusList": "状态列表"
}
主要针对的是单选框,复选框,下拉框,选项卡等元素数据。
常见的词汇有:title
, name
,label
,field
,text
,id
,key
,value
,children
,index
,nodes
等。
其中 title
,name
,label
,text
,field
作为选项显示名,id
,key
,value
用于唯一标识,children
,nodes
用于包含子节点内容。
{
// 常用组合
"label": "标题",
"value": "值",
// 组合1
"title": "标题",
"value": "值",
// 组合2
"text": "文本",
"value": "值",
// 组合3
"id": "标题",
"name": "值",
// 组合4
"field": "标题",
"value": "值",
"index": "标题"
}
{
"activeName": "激活的项名称",
"activeTab": "激活的选项卡",
"currentPage": "当前页",
"currentIndex": "当前选项的下标"
}
主要用作 axios 请求后台接口封装,接口方法使用 camelCase
驼峰命名,命名为动词+名词
形式。
后台一般采用 Restful Api
规范,如下示例:
/api/user/list
/api/user/detail/${id}
/api/user/add
/api/user/update
/api/user/delete/${id}
前端对应的接口方法命名用 getXxx
,addXxx
,updateXxx
,deleteXxx
这几种命名形式,并且一定要写注释,注释格式为模块名-方法描述
,如下示例:
/**
* 用户管理-分页列表
*/
export const getUserList = (params) => {
return axios.get("/api/user/list", params);
};
/**
* 用户管理-详情
*/
export const getUserDetail = (params) => {
return axios.get(`/api/user/detail/${params.id}`);
};
/**
* 用户管理-新增
*/
export const addUser = (data) => {
return axios.post("/api/user/add", data);
};
/**
* 用户管理-修改
*/
export const updateUser = (params) => {
return axios.post("/api/user/update", data);
};
/**
* 用户管理-删除
*/
export const deleteUser = (params) => {
return axios.get(`/api/user/delete/${params.id}`);
};
主要针对 Vue
组件中 attribute
传参,采用kebab-case
命名,字母小写,以短横分隔单词。
名词
或者 名词+修饰词
形式。{
"class-name": "类名",
"z-index": "层级",
"fullscreen": "全屏显示",
"show-upload-list": "是否展示上传文件列表"
}
function
,采用介词+动词/形容词
形式,注意这里事件名是允许用 on-xx
形式的,用来区分该 props
传递的是个函数。{
"on-success": "上传成功",
"on-progress": "上传进度",
"on-error": "上传失败",
"on-preview": "预览",
"before-upload": "上传之前",
"before-close": "关闭之前",
"after-close": "关闭之后"
}
场景:绑定事件监听
绑定的事件名采用kebab-case
命名,字母小写,以短横分隔单词。因为在 Dom 模板
中的事件监听,由于 HTML
中是不区分大小写的,Vue
会自动转换事件监听为全小写,所以推荐 始终使用 kebab-case
的命名。
事件的命名采用 动词
形式,事件绑定本身就是 v-on
指令,不能写成v-on:on-close
,所以绑定的事件统一都不用 on-xx
的形式。
{
// 原生事件监听
"input": "输入",
"change": "改变",
"blur": "失去焦点",
"focus": "聚焦",
"submit": "提交",
"keyup": "提交",
"keydown": "提交",
"enter": "提交",
"submit": "提交",
// 自定义事件监听
"preview": "预览",
"clear": "清空",
"cancel": "关闭",
"open": "打开",
"select-change": "选择",
"select-all": "选择所有",
"selection-change": "选项改变",
"sort-change": "排序",
"row-click": "单击行",
"row-dblclick": "双击行"
}
在声明 事件监听函数
或者声明 props 绑定函数
的时候有些是需要用 onXxx
的形式,有些则可以不带 on
,具体要看当前环境中的作用和语义。
// 组合1
function onInput() {}
function onPreview() {}
function onSelectAll() {}
function onRowClick() {}
function onSelectionChange() {}
// 组合2
function beforeUpload() {}
function beforeOpen() {}
function beforeClose() {}
场景:自定义事件
自定义事件名使用 camelCase
驼峰命名,命名为动词+名词
形式。
名称不能过长,要明确表达此事件处理的作用,避免望文不知义。
可以参考 JavaScript 规范
中 函数命名
章节,使用常用的动词来命名。
一般由页面直接或者间接发起的操作,用 handleXxx
的形式。
function handleSearch() {}
function handleReset() {}
function handleResize() {}
由方法发起调用的操作,用动词
开头的形式,命名看方法的用途决定。
function resetQuery() {}
function resetForm() {}
function resetSelect() {}
改变数据结果,切换状态的显示的操作,也以动词+形容词/名词
形式,比如 Modal 的展示隐藏,分页条的改变。
在其他场景下,很多命名方式也都可以用动词+形容词/名词
形式,例如select+范围
,元素+click
,change+元素
,clear+元素
,这些动词开头,用在改变元素,改变分页,改变业务状态,切换 Modal 弹框显示等等。
// 组合1-元素操作
function selectOne() {}
function selectAll() {}
function tabClick() {}
function switchTab() {}
function clearName() {}
// 组合2-分页
function changePageNum() {}
function changePageSize() {}
function jumpPage() {}
// 组合3-业务弹框
function showAddModal() {}
function showEditModal() {}
function showDetailModal() {}
function showSyncDataModal() {}
function showUserInfoModal() {}
function showDepartTreeModal() {}
// 组合4-业务状态
function changeUserStatus() {}
function changeUserStatus() {}
function changeUserPermission() {}
场景:异步处理方法
在了解过接口命名规则之后,前端页面中使用 Api 接口
的异步事件,这些一般比较固定,以下组合列举出了一些场景。
// 组合1
// 查询的是后台返回的列表,推荐是用 queryXxxList 这种形式
function queryList() {}
function queryMenuList() {}
function queryUserList() {}
function queryDetail() {}
// 组合2
// 查询的是后台返回的数据信息,详情等,推荐用 queryXxx+Data/Info/Detail 这种形式
function queryProcessData() {}
function queryStepInfo() {}
function queryTaskDetail() {}
// 组合3
// 页面增删查改的操作,用 handleXxx 的形式
function handleAdd() {}
function handleEdit() {}
function handleDelete() {}
function handleSubmit() {}
场景:路由页面跳转
路由页面的跳转一般只有 goXxx
和 toXxx
两种,两者使用差别不大,只在少部分页面跳转上有语义的差别。
比如 去首页,返回上级 这种表示只跳到这些页面就结束行为的用 goXxx
比较合适。
比如 跳转到详情页面,跳转到审批页面 这种表示跳到有数据信息的页面,并且可能带传参的用 toXxx
比较合适。
{
// 组合1
"goHome": "去首页",
"goBack": "返回页面",
"goMailBox": "去邮箱",
// 组合2
"toUserDetail": "跳转到用户详情",
"toApprove": "跳转到审批页面",
// 组合3
"navigateToHome": "导航到首页",
"redirectToLogin": "重定向到登录页",
"switchHome": "切换到首页",
"switchOrder": "切换到订单页"
}
场景:数据的加工
针对数据在不同场景下的转换处理操作,比如格式化,排序,过滤,转换,切换,比较,去除空格,添加,区间,相差等等。
{
// 组合1
"formatThousand": "格式化数字千分位",
"formatRmbChinese": "格式化人民币金额大写",
"formatStartOfName": "格式化姓名中间为星号",
// 组合2
"sortUserList": "用户列表排序",
"sortOrder": "订单排序",
"sortByName": "根据名称排序",
// 组合3
"filterUser": "过滤用户",
"filterByName": "通过名称过滤",
// 组合4
"convertCurrency": "转换货币单位",
"convertAmount": "转换金额",
// 组合5
"toggleClass": "切换演示",
// 组合6
"compareDate": "比较日期",
"compareAge": "比较年龄大小",
// 组合7
"trimStart": "去除字符串开始位置的空格",
"trimEnd": "去除字符串结束位置的空格",
"trimAll": "去除字符串中全部的空格",
// 组合8
"addDate": "日期加天",
"addMonth": "日期加月",
"addYear": "日期加年",
// 组合8
"diffDay": "相差的天数",
"diffWeek": "相差的周数",
"diffMonth": "相差的月数"
}
方法中参数名称和数量是根据具体使用来决定,但是还是有一些固定的模式可以参考。比如监听属性的新值和旧值,reduce 方法的上个值和当前值,回调函数的参数值等等。
Vue
的 watch
属性监听的回调参数。{
"newVal": "新值",
"oldVal": "旧值"
}
{
// 组合1
"from": "从...",
"to": "到...",
// 组合2
"prev": "上一个...",
"next": "下一个...",
"cur": "当前",
// 组合3
"source": "源",
"target": "目标",
// 组合4
"start": "开始",
"end": "结束",
// 组合5
"before": "在之前",
"after": "在之后",
// 组合6
"prefix": "前缀",
"suffix": "后缀"
}
Promise
的 then
方法参数,await
返回的值等,可选择的词汇有:res / data / json
,推荐使用 response
简写的 res
。// 示例1
getUserInfo().then((res) => {});
// 示例2
const res = await getUserInfo();
{
// 组合1
"str": "字符串",
"char": "单个字符",
"num": "数字",
"arr": "数组值",
"obj": "对象值",
// 组合2
"len": "数组长度",
"index": "下标",
"size": "大小",
"unit": "单位",
"count": "数量",
"amount": "金额",
"timer": "定时器",
// 组合2
"n": "次数",
"p": "数值的精度",
"x": "x轴坐标",
"y": "y轴坐标",
"radix": "基数",
"decimals": "小数位数",
// 组合3
"rate": "比例",
"status": "状态",
"bool": "布尔值",
"asc": "升序",
"desc": "降序",
"pid": "父id",
"cid": "子id",
// 组合4
"reg": "正则",
"func": "函数",
"callback": "回调函数",
// 组合5
"source": "数据源",
"target": "目标源",
"root": "根",
"elem": "元素",
"children": "子级",
"parent": "父级"
}
接口一般是后台来给,前端如果用 nodejs
做后台服务器,接口响应报文体也需要制定一套规则。
Restful Api 命名
在接口命名
章节中已经列举了 api 的命名和调用方法规范,前端一般用 nodejs+express/koa2
就可以实现 Restful Api
方式。
/api/user/list
/api/user/detail/${id}
/api/user/add
/api/user/update
/api/user/delete/${id}
code 码
前端用 nodejs
做后台,业务场景不会太过复杂。 简单的场景可以用以下的定义 code
,复杂的业务需要指定单独的业务 code
。
200 # 成功
400 # 请求参数错误
403 # 无权限访问
404 # 请求路径无效
500 # 服务器内部异常
502 # 网关访问错误
504 # 网关访问超时
响应数据结构
分页列表
{
"code": 200,
"message": "查询成功",
"data": {
"result": [
{ "id": 1, "name": "小红" },
{ "id": 1, "name": "小红" }
],
"total": 0,
"page": 1,
"size": 10
}
}
增删改,详情等返回非数组结果
{
"code": 200,
"message": "操作成功",
"data": { "id": 1, "name": "小红" }
}
在 HTML 规范
的 结构顺序
这一章节,已经大致描述了编写页面的结构和顺序,在 Vue 模板
中也一样,自上而下,从左到右,保持一定的顺序。
Vue 模板
中,采用了 容器
和 盒子
结合的语义化结构,先划分不同的容器空间,每个容器里再划分具体的盒子内容。
通过上面的 vue 项目中的不同种类的命名规则,以下是一段代码示例模板。
查询
重置
添加
{{ listQuery.pageSize * (listQuery.pageNo - 1) + ($index + 1) }}
{{ row.name }}
{{ row.createTime }}
编辑
删除
取消
确定
统一使用 2
个缩进,不允许使用 Tab
制表符,全部替换为空格缩进。
指令推荐都是用缩写形式
:
表示 v-bind
@
表示 v-on#
表示 v-slot正例:
this is header!
反例:
this is header!
当在组件中使用 data
属性的时候,它的值必须是返回一个对象的函数,因为如果直接是一个对象的话,子组件之间的属性值会互相影响。
正例:
反例:
在组件上总是必须用 key
配合 v-for
,以便维护内部组件及其子树的状态。
{{ item.name }}
v-if
和 v-show
都可以用于显示隐藏效果,但是本质上还是有区别的。
v-if
是惰性的,只有条件成立时候才渲染,比较适合根据条件显示隐藏的;
v-show
是不管条件真假,直接就会被渲染上,比较适合频繁切换的场景。
永远不要把 v-if
和 v-for
同时用在同一个元素上,v-for
的优先级比 v-if
高,所以会导致先循环遍历,然后依次根据 v-if
的判断显示隐藏,会比较浪费性能。
在组件中属性传参使用kebab-case
命名,字母小写,以短横分隔单词。
正例:
反例:
多个 attribute 的元素应该分多行,如果属性比较多,推荐封装个到对象中传参。
正例:
模板中 attribute
的值需要带引号,并且需要与前面空一格,另外模板中也是可以使用模板字符串。
模板中 attribute
的值是需要逻辑判断结果的时候,只允许用 等于
,大于
,小于
这些一元运算
,或者简单的三元运算
判断,复杂的需要拆分为单独的计算属性或者方法。
正例:
反例:
元素的 attribute
应该有统一的顺序,如果有使用 eslint 时候也会按照一定规则校验。
样式名(可复用的元素样式排首位)
class
列表渲染 (创建多个变化的相同元素)
v-for
条件渲染 (元素是否渲染/显示)
v-if
v-else-if
v-else
v-show
v-cloak
渲染方式 (改变元素的渲染方式)
v-pre
v-once
自定义指令 (改变元素的属性和效果)
v-loading
v-input-number
全局唯一 (唯一性的标识)
id
唯一的 attribute (需要唯一值的 attribute)
ref
key
双向绑定 (把绑定和事件结合起来)
v-model
自定义属性 (所有自定义绑定的 attribute)
:user-name
user-age
事件 (组件事件监听器)
v-on
自定义事件 (所有自定义绑定的 event)
@row-click
@row-dblclick
元素内容 (覆写元素的内容)
v-html
v-text
camelCase
驼峰命名。default
默认值。required
,如果有业务校验也需要加上 validator
。正例:
// 简洁的
export default{
props: {
userDetail: Object,
}
}
// 符合eslint推荐的
export default{
props: {
userDetail: {
type: Object,
default: () => {}
},
}
}
// 更加详情,带业务校验的
export default{
props: {
userDetail: {
type: Object,
required: true,
validator: function (value) {
// 校验
}
}
}
}
反例:
props: ["userDetail"];
组件模板中应该只包含简单的表达式,比较复杂的处理需要用计算属性或者方法。
正例:
{{ normalizedFullName }}
反例:
{{
fullName
.split(" ")
.map(function (word) {
return word[0].toUpperCase() + word.slice(1);
})
.join(" ")
}}
计算属性应该保持单一性,处理多个逻辑的需要拆分为多个计算属性。
正例:
export default {
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin);
},
discount: function () {
return this.basePrice * (this.discountPercent || 0);
},
finalPrice: function () {
return this.basePrice - this.discount;
},
},
};
反例:
export default {
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin);
return basePrice - basePrice * (this.discountPercent || 0);
},
},
};
在模板中引入资源是有顺序的,一般是先公用
后局部
,资源引入顺序为js资源 > 组件 > css资源
,如果引入资源比较多,需要合理的分组并写上注释。
// 引入第三方工具类
import util from "@lime-util/util";
// 引入第三方组件
import VueScroll from "vuescroll";
// 引入公用方法
import { uploadFile } from "@/common";
// 引入公用组件
import PageHeader from "@/components/PageHeader";
// 引入api
import {
getUserList,
getUserDetail,
addUser,
updateUser,
deleteUser,
} from "@/api/user";
// 引入局部组件
import UserInfo from "./components/UserInfo";
export default {};
声明组件的 name
使用PascalCase 帕斯卡
命名,尽量每个组件页面都写,方便 vue
识别,或者在使用 keep-alive
时候做缓存。
正例:
export default {
name: "UserInfo",
};
反例:
export default {
name: "userInfo",
};
export default {
name: "user-info",
};
在单文件组件
、字符串模板
和 JSX
中没有内容的组件应该是自闭合的,但在 DOM 模板
里不要这样做。
在单文件组件中
在字符串模板中
Vue Vue({
template:`<UserInfo :user-detail="userDetail" />`
});
在 JS/JSX 中
new Vue({
el: '#app',
render: function (h) {
return (
<UserInfo :user-detail="userDetail" />
)
}
})
在 Dom 模板中,需要有结束标签
<body>
<div class="app-container">
<user-info :user-detail="userDetail"></user-info>
</div>
</body>
有时候 props
或者 computed
中有很多选项,中间间隔挨着就会不容易区分,这时候就需要添加空行来让代码更加可观一些。添加合理的空行也适用于在 filters
,data
,methods
中。
单文件组件应该使,
和
标签保持顺序,并且
标签要放在最后,因为另外两个标签至少都是有的。
正例:
反例:
编写组件页面的样式,一定要加上 scoped
,使用独立的作用域,避免污染全局样式。
<style lang="less" scoped>style>
不能直接使用$parent
访问父组件的上下文,这种做法是不被允许的。可以改为 $emit
,.sync同步
,props 传参,
vue bus` 方式操作父组件的属性和方法。
在项目开发中尽量使用 Vue
的数据驱动
更新 Dom
,尽量不要手动操作 DOM,包括:增删改 Dom 元素,更改样式,添加事件等等。
在模板中,如果事件监听的方法不需要传参,则需要省略括号。
正例:
<template>
<el-button type="primary" @click="handleAdd" />
<el-button type="primary" @click="handleDelete(1)" />
template>
反例:
<template>
<el-button type="primary" @click="handleAdd()" />
<el-button type="primary" @click="handleOpen()" />
template>
Modal 弹框组件是特别常见的,为了规范开发和方便阅读,使用时候不能随意写在模板的任意位置,必须统一声明放在模板结尾标签位置,并加上注释区分弹框模块的用途。
<template>
<div class="app-container">
<div class="table-box">div>
<el-dialog
title="新增/编辑"
:visible.sync="editModal"
width="900px"
>el-dialog>
<el-dialog
title="详情"
:visible.sync="detailModal"
width="900px"
>el-dialog>
div>
template>
在某些场景下,需要修改第三方组件样式,由于 scoped
属性的样式隔离,需要用到样式穿透,统一使用 ::v-deep
。
index.vue
入口文件vue-cli
会自动识别。正例:
const routes = [
{
path: "/home",
meta: {
title: "首页",
},
component: () => import("@/views/home"),
},
{
path: "/user-info",
meta: {
title: "用户信息",
},
component: () => import("@/views/user-info"),
},
];
反例:
const routes = [
{
path: "/home",
meta: {
title: "首页",
},
component: () => import("@/views/home.vue"),
},
{
path: "/user-info",
meta: {
title: "用户信息",
},
component: () => import("@/views/user-info/index.vue"),
},
];
使用 $router
的 query
传参,不使用 params
,这样参数会在地址栏中不会刷新消失。
// 跳转路由
this.$router.push({
path: "home",
query: {
id: 1,
},
});
// 在 home/index.vue 中接收参数
let id = this.$route.query.id;
path
路径采用kebab-case
命名,字母小写,以短横分隔单词。name
采用 PascalCase 帕斯卡
命名。path
可以不用拼父路由的地址,vue-router
会自动识别。{
path: "/base",
name: "Base",
component: () => import("@/layout"),
meta: {
title: "基础管理"
},
children: [
{
path: "user-manage",
name: "UserManage",
component: () => import("@/views/base/user-manage"),
meta: {
title: "用户列表"
}
}
]
}
SET / ADD / DEL / UPDATE / CHANGE / RESET
动词开头。state
必须在 actions
的方法中完成。/* index.js store模板 */
// state
const state = {
token: "",
};
// mutations
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token;
},
};
// actions
const actions = {
saveToken({ commit }, token) {
commit("SET_TOKEN", token);
},
};
// export
export default {
namespaced: true,
state,
mutations,
actions,
};