个人Vue项目代码风格及各类规范
本规范为个人总结的,以Vue官方的风格指南和Vue3组合式API风格指南(英文)为基础,补充的项目开发过程中最好遵从的各类代码风格和规范。会结合实际开发学习过程进行持续更新。
必要的
优先使用async/await
除非是要进行Promise化,否则优先使用async/await
代替Promise.then()
,以免Promise.then()
过度使用导致代码阅读难度增加。
文件的命名
项目中除组件文件和页面文件使用大驼峰命名法(PascalCase),其余文件均使用短横线命名法(kebab-case)。
例外:
- uni-ui组件因为要使用easycom功能而使用短横线命名法。
文件头部添加基本说明
在所有代码文件中添加类似以下的注释,以方便快速分辨文件作用:
页面文件中:
<!--
* @description: 「个人信息」页面
* @author: Ruan Jiazhen
* @date: 2024-02-02 15:13:19
-->
VScode 代码片段配置:
json"Page Header": { "scope": "vue, html", "prefix": "!page", "body": [ "<!--", " * @description: ${1:your description}", " * @author: Author Name", " * @date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND", "-->", ] }
js/ts文件中:
/**
* @description: 用户信息相关接口
* @author: Ruan Jiazhen
* @date: 2024-02-03 10:01:18
**/
VScode代码片段配置:
json"JS/TS header": { "scope": "javascript, typescript", "prefix": "!js", "body": [ "/**", " * @description: $1", " * @author: Ruan Jiazhen", " * @date: $CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND", "**/", "", "$2" ] }
给函数、变量添加注释
所有在外部使用的函数或变量均需要编写TSDoc风格的注释。
通过给函数或变量添加如下TSDoc风格注释,可以直接通过鼠标悬停就查看代码相关注释,以方便开发:
/**
* 显示弹窗
* @param title 弹窗标题
* @param icon 弹窗图标
* @param duration 弹窗持续时间
* @param mask 是否显示透明蒙层,防止触摸穿透
* @param image 自定义图标的本地路径,image 的优先级高于 icon
* @param success 成功回调
* @param fail 失败回调
* @param complete 完成回调
* @returns void
*/
export const showToast = (
title: string,
icon: 'none' | 'success' | 'loading' | 'error' | undefined = 'none',
duration = 1500,
mask = false,
image = '',
success = () => { },
fail = () => { },
complete = () => { },
) => {
uni.showToast({
title,
icon,
duration,
mask,
image,
success,
fail,
complete,
})
}
详细编写方法可以参考TSDoc官方文档。
常量的命名和保存
对于菜单列表、数据模板这类不进行任何操作的常量,请保存在src/static/constants
文件夹下对应的文件中,并且使用全大写下划线命名法,例如:
// view-constants.ts
/** 首页按钮列表 */
export const HOME_BUTTON_LIST = [
{
name: '弹窗按钮一',
},
{
name: '弹窗按钮二',
},
{
name: '弹窗按钮三',
},
]
优先使用程序化编程,而非面向对象
前端项目中并无大量需要使用到继承思想的地方,强行使用面向对象的思想创建类然后再实例化反而会增加代码复杂度。
反例:
class Bot {
public work: string;
constructor(work: string) {
this.work = work;
}
public doWork() {
console.log('I am doing ' + this.work);
}
}
const bot = new Bot('cleaning');
bot.doWork();
好例子:
const doingWork = (work: string) => {
console.log('I am doing ' + work);
}
doingWork('cleaning');
推荐的
CSS代码的顺序
css属性按照固定的顺序排序可以有效提高代码阅读效率,建议按照以下顺序书写css属性:
- 自身定位相关属性(
grid-area
、margin
、postion
等); - 自身样式相关属性(
width
、backgroud
、border
等); - 子元素定位相关属性(
padding
、grid
、align-items
等); - 内部文本相关属性(
font
、color
、word-wrap
等);
推荐使用[stylelint](https://github.com/stylelint/stylelint)
工具对css代码进行质量控制,其可配置css属性的顺序,我个人也发布了按照以上原则编写的css顺序配置,详细配置规则见:
使用对象代替switch/case
对于case
内逻辑简单的方法,优先使用对象存储相关内容,然后通过参数获取。
这样便于后续功能扩展,当需要添加新的情况时,不需要单独编写新的case
逻辑
反例:
const getAnimalInfo = (animal: string) => {
switch (animal) {
case 'cat':
return { name: 'Mittens', age: 2 };
case 'dog':
return { name: 'Fido', age: 5 };
case 'bird':
return { name: 'Polly', age: 1 };
// ...其他case
default:
return {};
}
};
好例子:
const getAnimalInfo = (animal: string) => {
const animalInfoMap = {
cat: { name: 'Mittens', age: 2 },
dog: { name: 'Fido', age: 5 },
bird: { name: 'Polly', age: 1 },
// ...其他case
};
return animalInfoMap[animal];
};
相关逻辑代码封装在同一个对象中
利用js的闭包机制和立即执行函数,可以简洁且方便地将相关的代码组织在一起,特别是在vue3的setup语法中,不用担心没有格式限制而导致代码零散。
反例:
const userInfo = ref<UserInfo>();
const getUserInfo = async (id) => {
const res = await reqGetUserInfo(id);
userInfo.value = res.data;
};
const updateUserInfo = async (newUserInfo) => {
try {
const res = await reqUpdateUserInfo(newUserInfo);
if (res.code === 200) {
userInfo.value = newUserInfo;
}
} catch (error) {
console.error(error);
}
};
好例子:
const userInfo = (() => {
const v = ref<UserInfo>();
const getUserInfo = async (id) => {
const res = await reqGetUserInfo(id);
v.value = res.data;
};
const updateUserInfo = async (newUserInfo) => {
try {
const res = await reqUpdateUserInfo(newUserInfo);
if (res.code === 200) {
v.value = newUserInfo;
}
} catch (error) {
console.error(error);
}
};
return {
v,
getUserInfo,
updateUserInfo,
};
})();
调取后端接口时使用的参数的来源
一般来说,前端项目中会将每个后端接口封装为一个方法,然后在调用这个方法时将必要的参数传入。但有时该方法中会使用传入参数以外的数据(例如store内的数据),应尽量避免该情况,以降低代码的耦合度。
反例:
import axios from 'axios';
import { useSessionStore } from './session';
const reqGetUserInfo = (id: number) => {
const sessionId = useSessionStore().sessionId;
return axios.get(`/api/user?id=${id}&sessionId=${sessionId}`);
};
好例子:
import axios from 'axios';
const reqGetUserInfo = (id: string, sessionId: string) => {
return axios.get(`/api/user?id=${id}&sessionId=${sessionId}`);
};