项目规范
项目目录结构
// 项目根目录
sx-ts-template
|--src
|--assets // 资源文件配置目录
|--icons // 图标资源目录 (存放 icon & iconfont 资源文件)
|-- iconfont.js // 原则上一个项目只有一个iconfont资源文件
|--images // 图片资源目录,存放项目使用的所有图片资源
|--ssss // scss全局配置目录
|--abstracts // 存放全局 mixins & variables & placeholder & functions 声明
|--common // 基础样式 (全局样式重置)
|--components // 存放全局组件相关样式 (例: 多个页面用到相同样式或组件抽取样式写成独立组件样式文件)
|--animation // 存放全局动画样式
|--vendor // 存放第三方库的样式导入
|--config // 配置文件存放目录
|--config.ts // 后台请求连接地址配置文件 (具体链接地址通过 .env 传入)
|--const.ts // 全局常量文件
|--bus.ts // 事件总线 (vue版本)
|--elementConfig.ts // element-ui 按需导入文件
|--iviewConfig.ts // iview 按需导入文件
|--http.ts // axios 实例配置文件
|--vuexConfig.ts // vuex plugin 配置文件
|--router // 路由配置
|--index.ts // vue-router 实例配置文件
|--routes.ts // 路由配置文件
|--service // 请求接口管理
|--api.ts // 请求接口声明文件
|--store // vuex管理 (文件夹 & 文件 命名:小写肉串格式)
|--modules // vuex modules文件存放目录
|--index.ts // vuex统一配置文件(modules在这里统一引入)
|--ts // ts 配置 & 声明目录
|--declare // 全局类型声明配置目录 (包含 *.d.ts 文件)
|--type.ts // 数据结构类型声明文件
|--utils // 工具函数管理
|--index.js // 工具函数统一导出接口文件
|--layouts // 布局组件目录 (布局命名: 首字母大写驼峰格式)
|--Index.vue // 默认布局 (路由组件默认显示在该布局上)
|--Login.vue // 登陆布局
|--views // 路由组件目录
|--FloorConfig.vue // 路由组件 (命名: 首字母大写驼峰格式)
|--components // 路由组件对应的子组件存放目录 (文件夹 & 子组件 命名:小写肉串格式)
|--ui // 存放项目中必要的 ui 基础组件
|--common // 存放项目中共用的逻辑组件
|--floor-config // 路由组件对应子组件文件夹
|-- area-detail.vue // 子组件
|--.env.development // 开发环境变量声明文件
|--.env.production // 生产环境变量声明文件
|--.env.test // 测试环境变量声明文件
路由配置
- 路由组件配置统一配置在
@/src/router/routes.ts - 使用异步组件方式引入路由组件并配置
webpackChunkName(路由引入变量 = 路由组件名称 = webpackChunkName)
const FloorConfig = () => import(/* webpackChunkName: "FloorConfig" */ 'views/FloorConfig.vue')
- 二级页面 通过 动态路由匹配 & 嵌套路由 来构建 (例:一级分类列表页 & 二级分类列表页)
- 路由组件通过设置 来嵌套子组件
- 嵌套子组件存放在 components 中对应逻辑组件文件夹中
- 路由组件 & 嵌套子组件共用相同的
webpackChunkName - 嵌套子路由引入变量名带上路由组件名称并 首字母大写驼峰式
const FlowType = () =>
import(/* webpackChunkName: "FlowType" */ 'views/FlowType.vue')
const FlowTypeList = () =>
import(/* webpackChunkName: "FlowType" */ 'components/flow-type/list.vue')
const FlowTypeDetail = () =>
import(/* webpackChunkName: "FlowType" */ 'components/flow-type/detail.vue')
- Layout 布局级路由组件用做根路由组件 (其余路由组件属于 Layout 的二级路由)
/****** Layout ******/
const Index = () => import(/* webpackChunkName: "index" */ 'layouts/Index.vue')
const Login = () => import(/* webpackChunkName: "login" */ 'layouts/Login.vue')
const routes: RouteConfig[] = [
{
path: '/',
name: 'Index',
component: Index,
children: [...],
},
{
path: '/login',
name: 'Login',
component: Login,
},
]
Menu 配置
- 通过构建 menu 配置来快速生成 顶部栏 & 侧边栏 & 逻辑路由配置 (通过对应的 Builder 转化 menu 配置)
// index menu 配置
export const IndexRoutes = [
{ // 无 tab 菜单项 (默认菜单)
sides: [ // 侧边栏配置
{ // menuItem
title: '菜单项', // menuItem title
url: 'menu-item', // 组件路由 (必须为驼峰式: route.name 通过 url 自动生成 例: url: config-set => name: ConfigSet)
component: Test, // 路由对应的组件
icon: 'icon-xxx', // menuItem icon
},
{ // subMenu
title: 'subMenu',
children: [ // 子菜单配置
{ // subItem
title: '子菜单项A',
url: 'sub-item-a',
component: SubItemA,
icon: 'icon-xxx',
},
{
title: '子菜单项B',
url: 'sub-item-b',
component: SubItemB,
icon: 'icon-xxx',
/* 存在二级页面操作 (通过设置嵌套路由实现) */
children: [
{ path: '', component: SubItemList }, // 默认展示该嵌套子路由
{ path: ':id', component: SubItemDetail },
],
},
],
},
],
},
{ // 具名 tab (显示在顶部栏中)
title: 'title',
url: 'mall', // 一级路由: 与组件路由(sides‘url)拼成完整路由
hide: false, // 隐藏菜单项 (若设置, 则 tab 不在顶部显示,必须手动进入才能展示对应侧边栏)
sides: [...],
},
/* 外部链接菜单按钮 直接跳出 */
{
title: 'screen',
url: 'http://www.baidu.com',
},
]
const routes: RouteConfig[] = [
{
path: '/',
name: 'Index',
component: Index,
children: [
{ path: '', redirect: { name: 'menuItem' } },
...routeBuilder(IndexRoutes), // 使用路由生成器自动生成路由
],
},
{
path: '/login',
name: 'Login',
component: Login,
},
]
Layout 路由组件规范
- 所有布局级路由组件 统一放在
@/src/layouts文件夹中 - 所有 layout 路由组件命名 首字母大写驼峰格式 形式
- 所有 layout 路由组件 class 名称与文件名称相同
普通路由组件
- 所有普通路由组件 统一放在
@/src/views文件夹中 - 所有普通路由组件命名 首字母大写驼峰格式 形式
- 所有普通路由组件 class 名称与文件名称相同
普通(子)组件
- 通常一个路由组件逻辑被拆分成多个普通子组件
- 普通组件统一使用名称形式命名并满足 小写肉串格式
- 每个路由组件在 @/src/components 文件夹下创建同名 小写肉串格式 形式文件夹,用于存放其逻辑子组件
- 所有普通子组件 class 名称 = <路由组件名称> + 自身文件名称 (满足 首字母大写驼峰格式 形式, 名称过长可省略路由组件名称)
环境相关配置
- 在
package.json中配置不同打包脚本
"scripts": {
"dev": "vite", // dev 模式
"build": "vite build", //项目打包生成dist文件夹
"deploy:test": "export UPLOAD=true && yarn build --mode test", // production 生产模式打包并部署
"build:test": "vue-cli-service build --mode test", // test 测试模式打包并部署
"test:unit": "vue-cli-service test:unit", // 单元测试
"lint": "vite lint", // eslint 校验
},
- 通过项目中 .env.[environment] 文件来配置不同模式下的部分自定义环境变量
// .env.development
# 开发环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.209:10888'
VUE_APP_IMG_URL = ''
// .env.production
# 生产环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.210:12488'
VUE_APP_IMG_URL = ''
// .env.test
# 测试环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.209:10888'
VUE_APP_IMG_URL = ''
- 项目中通过 process.env.[XXXX] 来引用不同模式下的自定义环境变量
// @/src/config/config.ts
export const HTTP_URL = process.env.VUE_APP_HTTP_URL
export const IMAGE_URL = process.env.VUE_APP_IMG_URL
export default {
HTTP_URL,
IMAGE_URL,
}
代码规范
script中JS(TS)代码规范
牢记:
- 函数尽量不要超过15行
- 一个函数做一件事情。
- 并且在函数上方添加备注描述这个函数的功能,必要时解释函数的输入输出参数的意义
- 一个组件的代码控制在400行内,实现界面使用组件化的思想
- 遵循DRY原则,Don't repeat yourself!
- 拒绝使用魔法值(莫名其妙出现在代码中的数字,需要定义成常量并加注释)
包导入顺序(在导入时添加注释区分下列四个模块,不要堆在一起)
- 第三方库或组件
- 本地的js模块
- 本地的vue组件
- 本地的资源文件
样例
// 第三方库与组件
import Swiper from 'swiper'
// 本地js模块
import { httpUrl, imageUrl } from "@/tools/globals"
import { login, getBrandInfoList } from "@/service/restApi"
import {
appendImageUrl,
appendPrefixStaticUrl,
getImageSize,
getParameterByName }
from "@/tools/utils"
// 本地组件
import RankingList from "@/components/RankingList"
- 包导入方式
- 所有导入使用alias的绝对路径导入,禁止使用相对路径
- 按需导入,不要导入多余的函数
// 导入1~2个函数,写在一行,{} 前后各空一格
import { httpUrl, imageUrl } from "@/tools/globals"
// 导入3个以上函数时
import {
appendImageUrl,
appendPrefixStaticUrl,
getImageSize,
getParameterByName
} from "@/tools/utils"
- 组件导入时首字母大写
import RankingList from "@/components/RankingList"
- 导入实例时,首字母小写,驼峰
import store from '@/store/store'
props属性使用时候需注明对应的类型,有必要时应给出默认值,禁止使用简写形式
// 错误用例
props: ['userName', 'userPassword']
// 正确写法
props: {
// 用户名
userName: {
type: String,
default: ''
},
// 用户密码
userPassword: {
type: String,
default: '123456'
}
},
methods
- methods书写顺序,把所有的restful 请求写在methods的最前面.
- methods书写使用es6的写法
methods: {
/**
* 请求平面图和所有品牌logo
*/
async getBrandInfoList() {
let res = await getBrandInfoList()
if (res && res.code === 200) {
this.brandInfoList = res
}
},
...
}
- api的请求函数命名与restful的method名一致
- 在restful请求函数之后,书写
initControl函数,用于逻辑控制。逻辑的每一部分都用1、2、3...显示地注释
async initControl() {
// 1. 请求数据
await this.getBrandInfoList()
// 2. 获取店铺图的信息
await this.getStoreInfo()
// 3. 设置店铺图
this.renderStore()
// 4. 格式化所有的品牌以及对应热区图的信息
this.getBrandList()
// 5. Dom上设置这些品牌
// DOM是异步更新的,需要用nextTick
this.$nextTick(this.renderBrandList)
},
- 有时使用原生js或者第三方库操作DOM元素时,对于View层进行操作的函数,函数名使用
render开头
/**
* 设置店铺平面图展示element的宽高背景图
*/
renderStore() {
let storeEl = document.querySelector('.store-hot')
// 设置背景图的宽高
storeEl.style.height = this.storeHeight + 'px'
storeEl.style.width = this.storeWidth + 'px'
storeEl.style.backgroundImage = appendImageUrl(this.storeImageUrl)
storeEl.style.backgroundImage = appendImageUrl(this.storeImageUrl)
storeEl.style.backgroundSize = 'cover'
}
html书写规范
- 尽量使用 class 而非 style
- 遵守 BEM 规范, 合理书写节点 (格式: 块 - 元素 - 修饰符)
- 组件根元素设置与组件名称相同的 class (满足小写肉串形式)
- 使用抽象的概念去设计html结构,活用
v-for语法。特别是列表一类的,避免重复的元素占用过大篇幅
<template>
<div class="user-reviews">
<figure class="review">
<blockquote class="review-tip">...</blockquote>
<figcaption class="review-user">
<img src="img/user-1.jpg" alt="User 1" class="user-photo">
<div class="review-card">
<p class="user-name">Nick Smith</p>
<p class="user-date">Feb 23rd, 2017</p>
</div>
<div class="user-rating">7.8</div>
</figcaption>
</figure>
</div>
</template>
SCSS书写规范
- 统一使用scss
- scss的class命名使用BEM规则
- scss书写时层次尽量控制在3层,最多不要超过4层
- 全局变量、函数、mixins统一写在
_variables.scss、_functions.scss、_mixins.scss文件中 - 全局对iview组件的修改统一写在
_.iview.scss文件中 - 动画样式抽离到 assets/scss/animation/xxx.scss 并通过对应 index.scss 引入
- 编写每个组件的
style时,最外层使用这个组件的名称的连接线形式,比如组件名称为i18nLocation那么,我们的代码应该是
<style lang="scss" scoped>
.i18n-location {
...
}
- 组件内部 style 块 必须设置 scoped
- 组件内部 style 书写统一包在 组件根元素类 下
- 通过 :deep() 操作符更改子组件样式
<style lang="scss" scoped>
.flow-type-list { // 组件根元素类
.content {
&-bar {
@include flex($x: flex-start);
background-color: $color-white;
height: 54px;
padding: 0 30px;
box-shadow: 1px 1px 4px 0 rgba(29, 49, 82, 0.08);
}
:deep(.table-header) {
@include text(
$font-size: 12px,
$color: $text-color,
$font-family: $medium-font-family
);
}
}
.page {
@include flex($x: flex-end);
margin-top: 10px;
}
}
</style>
git工作流规范
工作流程
git提交规范
前言
多人协作开发项目中,Git 提升了项目开发进度的可控性,通过对提交、分支等浏览能快速了解项目整体进度开发流程中的各类信息。其中对于程序猿来讲比较重要的就是提交信息的准确程度,只有规范的提交信息才能在 review 时能够清楚了解提交的目的,方便后续开发。
因此通过约定规范的形式让提交信息更准确、更科学,基于 AngularJS 规范 。
提交格式
包括三个部分:Header(必填),Body 和 Footer
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
提交文本每行不得超过100个字符、各部分需空行分隔
- Header
- Type: 提交类型,用于说明
commit的提交性质。常用的有feat、fix、refactor。- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- refactor: A code change that neither fixes a bug nor adds a feature
- perf: A code change that improves performance
- test: Adding missing or correcting existing tests
- chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
- Scope: 提交内容的影响范围 (文件、路径、功能、上下文等)。
scope依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。scope可以省略。 - Subject: 概括提交内容的精简语句 (不得超过50个字符)
-
Body 对本次提交的详细描述:可以包含提交动机、版本差异等
-
Footer
备注信息, 通常是 BREAKING CHANGE、修复的 bug 的链接 或 关闭 issue 等
- 特殊格式
revert: 当前 commit 是用来撤销以前某个 commit 时,须以 revert: 开头后面跟着被撤销 commit 的 Header
工具配置说明
标准化 git commit 信息工具: 通过命令行方式快速选择提交类型按规范生成提交信息
安装适配器
commitizen 支持不同适配器的扩展,从而去满足不同的构建需求.更多适配器
# 全局安装
$ npm install -g cz-conventional-changelog
# 添加.czrc配置文件(全局设置/项目根目录)
# 内容: { "path": "cz-conventional-changelog" }
# 本地安装
$ npm install cz-conventional-changelog --save-dev
# 或使用 commitizen 工具
$ commitizen init cz-conventional-changelog --save-dev --save-exact
# package.json中添加配置相应的配置 (commitizen 工具自动添加)
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
命令行执行
# 替代 git commit 操作
$ npx git cz
# 全局模式不需要 npx
$ git cz
标准化 git commit 信息验证工具: 检验提交信息是否符合规范
commitlint 也支持多种配置,按约定使用 angular 配置。更多配置
# 全局安装
$ npm install -g @commitlint/cli @commitlint/config-angular
# 本地安装
$ npm install -D @commitlint/cli @commitlint/config-angular
# 项目中添加配置文件/package.json中添加commitlint字段
$ echo "module.exports = {extends: ['@commitlint/config-angular']};" > commitlint.config.js
Git Hooks 便捷实用工具
- 安装
$ npm install --save-dev husky
- 配置
在 package.json 配置Git提交验证
// package.json
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
代码风格规范
搜索关键字 prettier、eslint、editorconfig在代码编辑器以及项目中配置。