前端開發(fā)規(guī)范
一、代碼風(fēng)格
1. 縮進
- 統(tǒng)一使用2個空格縮進,避免使用Tab鍵,確保代碼在不同編輯器和開發(fā)環(huán)境中的顯示一致性。
Vue模板中使用2個空格縮進,保持與JavaScript一致。 - 示例:
<script setup>
function fetchData() {
return axios.get('/api/data')
.then(response => {
return response.data;
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
</script>
2.大括號使用
- 對于控制語句(如if、for、while等)和函數(shù)聲明,即使只有一行代碼,也必須使用大括號包裹,增強代碼的可讀性和可維護性。
- 大括號位于語句行的末尾,左大括號({)與語句行在同一行,右大括號(})單獨占一行。
- 示例:
if (condition) {
executeSomeLogic();
} else {
executeAlternativeLogic();
}
3.括號使用
- 函數(shù)調(diào)用和定義時,始終使用括號,即使沒有參數(shù),也保留空括號,明確表示這是一個函數(shù)調(diào)用或定義。
- 示例:
function logMessage() {
console.log('Message logged');
}
logMessage();
4.逗號使用
- 對象或數(shù)組定義時,最后一個元素后不加多余的逗號,避免潛在的解析錯誤和兼容性問題。
- 示例:
const person = {
name: 'John Doe',
age: 30,
occupation: 'Developer' // 沒有多余的逗號
};
const numbers = [1, 2, 3]; // 沒有多余的逗號
5.引號使用
- 字符串優(yōu)先使用單引號(')進行包裹,只有在字符串中包含單引號字符時,才使用雙引號(")。
- 示例:
const greeting = 'Hello, World!';
const quote = "Don't worry, be happy!";
二、命名規(guī)范
1.變量命名
- 采用駝峰命名法(camelCase),即除首個單詞外,每個單詞的首字母大寫,其余字母小寫,無下劃線或其他分隔符。
- 變量名應(yīng)簡潔明了,準確反映其存儲的數(shù)據(jù)內(nèi)容和用途,避免使用模糊、無意義的名稱,如temp、data等。
- 示例:
// 正確示例
let userName = 'John Doe';
let userAge = 30;
let isLoggedIn = true;
// 錯誤示例
let uName = 'John Doe'; // 變量名不夠清晰
let data = 30; // 變量名過于模糊
let flag = true; // 變量名不夠具體
2.常量命名
- 常量使用全大寫字母,單詞之間用下劃線(_)分隔,明確標識該變量的值不會改變。
- 示例:
const API_URL = '<https://api.example.com>';
const MAX_LIMIT = 100;
const DAYS_IN_WEEK = 7;
3.函數(shù)命名
- 函數(shù)名采用駝峰命名法,且函數(shù)名應(yīng)具有動詞性質(zhì),能準確體現(xiàn)函數(shù)的功能和作用,如getUserInfo、submitForm、calculateTotal等。
- 示例:
// 正確示例
function fetchUserData(userId) {
// 獲取用戶數(shù)據(jù)的邏輯
}
function validateFormInputs(form) {
// 驗證表單輸入的邏輯
}
// 錯誤示例
function ud(userId) { // 函數(shù)名不夠清晰
// 獲取用戶數(shù)據(jù)的邏輯
}
function vf(form) { // 函數(shù)名不夠清晰
// 驗證表單輸入的邏輯
}
4.類命名
- 類名采用帕斯卡命名法(PascalCase),即每個單詞的首字母大寫,無下劃線或其他分隔符,類名應(yīng)能體現(xiàn)該類的職責(zé)和功能。
- 示例:
class UserManager {
// 用戶管理類的邏輯
}
class DataProcessor {
// 數(shù)據(jù)處理類的邏輯
}
三、注釋規(guī)范
1.單行注釋
- 在代碼的單行注釋中,在注釋內(nèi)容前添加空格,提高注釋的可讀性,注釋內(nèi)容應(yīng)簡潔明了,解釋代碼的功能、目的或注意事項。
- 示例:
// 用戶名驗證
if(userName === 'admin') {
}
// 用戶名驗證正則表達式,規(guī)則為:長度4-20位,只能包含字母、數(shù)字和下劃線
const usernameRegex = /^[a-zA-Z0-9_]{4,20}$/;
2.多行注釋
- 對于多行注釋,使用/.../包裹,每行注釋內(nèi)容前添加空格,并在注釋的開頭和結(jié)尾留出一行空行,使注釋區(qū)域清晰易讀,用于解釋較復(fù)雜的邏輯或算法。
- 示例:
/*
* 用戶管理類
* 負責(zé)用戶數(shù)據(jù)的增刪改查操作。
*/
class UserManager {
async getUsers() {
// 獲取用戶列表
}
async addUser(user) {
// 添加用戶
}
async deleteUser(userId) {
// 刪除用戶
}
}
/*
* 定義用戶資料卡片的樣式。
* 包括背景顏色、邊框、圓角和內(nèi)邊距。
*/
.user-profile {
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
3.函數(shù)注釋
- 在每個函數(shù)上方添加注釋,說明函數(shù)的功能、參數(shù)、返回值和可能的異常情況,便于其他開發(fā)人員理解和使用該函數(shù),特別是對于公共函數(shù)或庫函數(shù)。
- 示例:
/**
* 格式化日期為指定的字符串格式
* @param {Date} date - 要格式化的日期對象
* @param {string} format - 目標日期格式,如'yyyy-MM-dd', 'MM/dd/yyyy'等
* @returns {string} 格式化后的日期字符串
* @throws {Error} 如果format參數(shù)格式不正確或不支持
*/
function formatDate(date, format) {
// 格式化日期的邏輯
}
四、組件開發(fā)規(guī)范
1.組件命名
- 組件名采用帕斯卡命名法,體現(xiàn)組件的功能和用途,確保組件名在整個項目中具有唯一性,避免與其他組件或全局元素沖突。
- 示例:
<!-- 正確示例:UserCard.vue -->
<template>
<div class="user-card">
<img :src="user.avatar" :alt="user.name" class="user-avatar" />
<h3 class="user-name">{{ user.name }}</h3>
<p class="user-info">{{ user.info }}</p>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
user: {
type: Object,
required: true
}
});
</script>
<style scoped>
.user-card {
/* 樣式定義 */
}
</style>
2.組件結(jié)構(gòu)
- 組件代碼結(jié)構(gòu)清晰,包含script(定義邏輯)、template(描述模板)、style(定義樣式)部分,各部分之間使用明確的分隔線(如注釋)區(qū)分,提高代碼的可讀性和可維護性。
- 示例:
<template>
<!-- 組件模板部分 -->
<div class="login-form">
<h2>{{ onLogin }}</h2>
</div>
</template>
<script setup>
// props
const props = defineProps({
onLogin: {
type: string,
default: '登錄',
required: true
}
});
</script>
<style scoped>
/* 組件樣式部分 */
.login-form {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
</style>
3.組件交互
3.1 父子組件通信
- 父組件向子組件傳遞數(shù)據(jù)通過props,子組件需對props進行類型驗證和必要性檢查,確保接收到的數(shù)據(jù)符合預(yù)期。
- 子組件向父組件傳遞事件通過emits,父組件監(jiān)聽子組件觸發(fā)的事件,執(zhí)行相應(yīng)的回調(diào)函數(shù)。
- 示例:
<!-- 父組件:ParentComponent.vue -->
<template>
<div>
<child-component :user="selectedUser" @update-user-info="handleUserInfoUpdate" />
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from 'vue';
const selectedUser = ref({
id: 1,
name: 'John Doe',
email: 'john@example.com'
});
const handleUserInfoUpdate = (updatedUser) => {
// 處理用戶信息更新的邏輯
console.log('User info updated:', updatedUser);
selectedUser.value = updatedUser;
};
</script>
<!-- 子組件:ChildComponent.vue -->
<template>
<div>
<h3>User Information</h3>
<p>Name: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
<button @click="updateUserInfo">Update Info</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
user: {
type: Object,
required: true
}
});
const emit = defineEmits(['update-user-info']);
const updateUserInfo = () => {
const updatedUser = {
...props.user,
name: 'Updated Name',
email: 'updated@example.com'
};
emit('update-user-info', updatedUser); // 觸發(fā)自定義事件,將更新后的用戶信息傳遞給父組件
};
</script>
3.2 兄弟組件通信
- 通過共同的祖先組件或狀態(tài)管理庫(如Pinia)進行通信,避免直接的操作和數(shù)據(jù)共享,確保組件之間的解耦和數(shù)據(jù)的一致性。
4.組件性能優(yōu)化
避免不必要的渲染
在組件中,合理使用computed屬性和watch監(jiān)聽器,只在必要的時候觸發(fā)組件的重新渲染,避免因數(shù)據(jù)頻繁變化導(dǎo)致的性能問題。使用keep-alive緩存組件
對頻繁切換但內(nèi)容不經(jīng)常變化的組件,使用<keep-alive>標簽進行緩存,減少組件的銷毀和重新創(chuàng)建的開銷,提升用戶體驗。示例:
<template>
<div>
<button @click="activeTab = 'TabA'">Tab A</button>
<button @click="activeTab = 'TabB'">Tab B</button>
<button @click="activeTab = 'TabC'">Tab C</button>
<keep-alive>
<component :is="activeTabComponent" />
</keep-alive>
</div>
</template>
<script setup>
import TabA from './TabA.vue';
import TabB from './TabB.vue';
import TabC from './TabC.vue';
import { ref, computed } from 'vue';
const activeTab = ref('TabA');
const activeTabComponent = computed(() => {
return activeTab.value;
});
</script>
五、CSS 規(guī)范
1.命名規(guī)范
- 采用有意義的名稱:CSS 類名應(yīng)能直觀地反映其樣式的作用和用途,避免使用無意義的名稱,如div1、box等,同時避免過度依賴元素的外觀特征命名,如red-box、big-button,因為樣式可能會隨需求變化而改變。
- 使用局部命名空間:對于組件內(nèi)部的樣式,采用基于組件名的命名空間,避免樣式污染和沖突,增強樣式的可維護性和可復(fù)用性。
- 示例:
/* 錯誤示例 */
.red-button {
background-color: red;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 正確示例 */
.primary-button {
background-color: #4CAF50;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 基于組件名的命名空間示例 */
.user-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 16px;
margin-bottom: 16px;
}
.user-card-avatar {
width: 64px;
height: 64px;
border-radius: 50%;
object-fit: cover;
}
2.樣式組織
采用BEM命名方法(可選)
BEM(Block, Element, Modifier)是一種流行的CSS命名方法,有助于創(chuàng)建可復(fù)用的、一致的CSS樣式結(jié)構(gòu)。Block表示獨立的、可復(fù)用的模塊;Element表示Block內(nèi)的組成部分,其類名以Block名稱作為前綴,并用兩個下劃線(__)分隔;Modifier表示Element的不同狀態(tài)或樣式變體,其類名以Element名稱作為前綴,并用兩個連字符(--)分隔。使用CSS預(yù)處理器(Less)
利用Less提供的變量、混合、函數(shù)等功能,實現(xiàn)樣式的復(fù)用和靈活管理,提高樣式的可維護性。示例:
// 變量定義
@primary-color: #4CAF50;
@secondary-color: #2196F3;
@border-radius: 4px;
@box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
// 混合定義
.mixin-box-shadow(@shadow: @box-shadow) {
box-shadow: @shadow;
}
// 樣式定義
.primary-button {
background-color: @primary-color;
color: white;
padding: 8px 16px;
border: none;
border-radius: @border-radius;
cursor: pointer;
.mixin-box-shadow();
}
.secondary-button {
background-color: @secondary-color;
color: white;
padding: 8px 16px;
border: none;
border-radius: @border-radius;
cursor: pointer;
.mixin-box-shadow();
}
3.樣式順序
- 在定義樣式屬性時,按照一定的順序排列,通常遵循以下順序:
定位(position、top、right、bottom、left、z-index等)
自身尺寸(width、height、min-width、max-width、min-height、max-height等)
內(nèi)外邊距(margin、padding等)
定義寬高(border、border-radius等)
背景(background-color、background-image等)
文本(color、font-size、font-weight、line-height、text-align等)
其他(transition、transform、cursor等) - 示例:
.card {
position: relative;
width: 300px;
height: 200px;
margin: 20px auto;
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: white;
color: #333;
font-size: 16px;
line-height: 1.5;
text-align: left;
transition: transform 0.3s ease;
cursor: pointer;
}
.card:hover {
transform: translateY(-5px);
}
4.媒體查詢
- 對于移動端適配,使用媒體查詢(media queries)定義不同屏幕尺寸下的樣式,確保頁面在各種設(shè)備上都能正常顯示和操作。
- 示例:
/* 桌面端樣式 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
}
/* 平板端樣式 */
@media (max-width: 1024px) {
.container {
max-width: 800px;
}
}
/* 移動端樣式 */
@media (max-width: 768px) {
.container {
max-width: 100%;
padding: 0 12px;
}
}
六、文件名規(guī)范
1.組件文件
- 組件文件名采用帕斯卡命名法,與組件名保持一致,文件后綴名為.vue。
- 示例:
// 正確示例
UserCard.vue
LoginForm.vue
HomePage.vue
// 錯誤示例
user-card.vue
login_form.vue
home-page.vue
2.JS 文件
- JS 文件名采用駝峰命名法,文件名應(yīng)能體現(xiàn)文件的內(nèi)容和功能,文件后綴名為.js。
- 示例:
// 正確示例
userData.js
formValidation.js
apiService.js
// 錯誤示例
user-data.js
form-validation.js
api-service.js
3.CSS 文件
- CSS 文件名采用駝峰命名法或短橫線命名法,文件名應(yīng)能體現(xiàn)樣式的作用域和功能,文件后綴名為.less(使用 Less 預(yù)處理器時)或.css。
- 示例:
/* 正確示例(駝峰命名法) */
globalStyles.less
componentStyles.css
themeVariables.less
/* 正確示例(短橫線命名法) */
global-styles.less
component-styles.css
theme-variables.less
/* 錯誤示例 */
global_styles.less
component_styles.css
theme_variables.less
4.圖片文件
- 圖片文件名采用短橫線命名法或下劃線命名法,文件名簡潔明了,體現(xiàn)圖片的內(nèi)容和用途,文件格式常用.png、.jpg、.jpeg、.gif、.svg 等。
- 示例:
<!-- 正確示例(短橫線命名法) -->
<img src="images/user-avatar.png" alt="User Avatar" />
<img src="images/logo.svg" alt="Logo" />
<!-- 正確示例(下劃線命名法) -->
<img src="images/user_avatar.png" alt="User Avatar" />
<img src="images/logo.svg" alt="Logo" />
<!-- 錯誤示例 -->
<img src="images/useravatar.png" alt="User Avatar" />
<img src="images/logo-svg.svg" alt="Logo" />
七、JS 編程規(guī)范
1.變量聲明
- 使用const或let聲明變量:避免使用var,因為它會產(chǎn)生變量提升和作用域污染等問題。使用const聲明不變的常量,使用let聲明可變的變量。
- 變量使用前必須聲明:避免使用未聲明的變量,防止?jié)撛诘腻e誤和難以排查的bug。
- 示例:
// 使用const聲明常量
const API_URL = 'https://api.example.com';
const MAX_LIMIT = 100;
// 使用let聲明變量
let count = 0;
let userLoggedIn = false;
// 錯誤示例(使用var)
var userName = 'John Doe';
var itemCount = 5;
// 變量使用前必須聲明
let userInput;
userInput = prompt('Enter your name');
console.log('Hello, ' + userInput);
// 錯誤示例(變量使用前未聲明)
console.log(userInput);
userInput = prompt('Enter your name');
2.對象和數(shù)組
- 對象創(chuàng)建和解構(gòu)
使用對象字面量創(chuàng)建對象,利用對象解構(gòu)賦值提取對象的屬性值,提高代碼的可讀性和簡潔性。 - 示例:
// 創(chuàng)建對象
const user = {
id: 1,
name: 'John Doe',
age: 30,
email: 'john@example.com'
};
// 解構(gòu)對象
const { id, name, age, email } = user;
console.log(`User ID: ${id}, Name: ${name}, Age: ${age}, Email: ${email}`);
- 數(shù)組操作
使用數(shù)組的內(nèi)置方法(如map、filter、reduce等)操作數(shù)組,避免使用循環(huán)和下標直接操作數(shù)組元素,使代碼更簡潔、優(yōu)雅。 - 示例:
// 使用map方法處理數(shù)組
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(number => number * 2);
console.log(doubledNumbers); // 輸出:[2, 4, 6, 8, 10]
// 使用filter方法過濾數(shù)組
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // 輸出:[2, 4]
// 使用reduce方法計算數(shù)組的和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 輸出:15
3.函數(shù)
- 函數(shù)表達式和箭頭函數(shù)
優(yōu)先使用函數(shù)表達式和箭頭函數(shù)定義函數(shù),避免使用函數(shù)聲明語句,特別是對于回調(diào)函數(shù)和簡短的函數(shù)處理。 - 示例:
// 函數(shù)表達式
const greet = function(name) {
console.log(`Hello, ${name}!`);
};
greet('John');
// 箭頭函數(shù)
const square = x => x * x;
console.log(square(5)); // 輸出:25
const add = (a, b) => a + b;
console.log(add(3, 7)); // 輸出:10
- 函數(shù)參數(shù)和默認值
函數(shù)參數(shù)數(shù)量盡量控制在合理范圍內(nèi),避免過多參數(shù)導(dǎo)致函數(shù)難以維護和理解。使用函數(shù)參數(shù)的默認值簡化函數(shù)調(diào)用。 - 示例:
// 函數(shù)參數(shù)過多的示例(不推薦)
function createPerson(firstName, lastName, age, gender, occupation) {
// 創(chuàng)建人物對象的邏輯
}
// 使用對象作為參數(shù)的示例(推薦)
function createPerson({ firstName, lastName, age, gender, occupation }) {
// 創(chuàng)建人物對象的邏輯
}
// 調(diào)用函數(shù)
createPerson({
firstName: 'John',
lastName: 'Doe',
age: 30,
gender: 'male',
occupation: 'developer'
});
// 函數(shù)參數(shù)默認值示例
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); // 輸出:5
console.log(multiply(5, 3)); // 輸出:15
4.異步編程
- 使用async/await
對于異步操作,優(yōu)先使用async/await語法,使異步代碼的書寫更接近同步代碼,提高代碼的可讀性和可維護性。 - 示例:
// 使用async/await獲取用戶數(shù)據(jù)
async function fetchUserData(userId) {
try {
const response = await axios.get(`/api/users/${userId}`);
return response.data;
} catch (error) {
console.error('Error fetching user data:', error);
throw error;
}
}
// 調(diào)用異步函數(shù)
async function displayUserData(userId) {
try {
const userData = await fetchUserData(userId);
console.log('User Data:', userData);
} catch (error) {
console.error('Failed to display user data:', error);
}
}
displayUserData(1);
- Promise 使用
對于不支持async/await的場景或兼容性要求較高的情況,使用Promise進行異步編程,確保異步操作的正確處理和錯誤捕獲。 - 示例:
// 使用Promise獲取用戶數(shù)據(jù)
function fetchUserDataPromise(userId) {
return new Promise((resolve, reject) => {
axios.get(`/api/users/${userId}`)
.then(response => {
resolve(response.data);
})
.catch(error => {
reject(error);
});
});
}
// 調(diào)用Promise
fetchUserDataPromise(1)
.then(userData => {
console.log('User Data:', userData);
})
.catch(error => {
console.error('Error fetching user data:', error);
});
5.防御性編程
- 判空處理
在操作對象、數(shù)組、字符串等數(shù)據(jù)類型時,進行判空處理,避免因數(shù)據(jù)為空導(dǎo)致的運行時錯誤。 - 示例:
// 對象判空
function getUserRole(user) {
if (!user) {
console.warn('User object is null or undefined');
return null;
}
return user.role || 'guest';
}
// 對象判空,鏈式調(diào)用
function getUserRole(user) {
return user?.role || 'guest';
}
// 數(shù)組判空
function getFirstItem(items) {
if (!items || !items.length) {
console.warn('Items array is empty or not an array');
return null;
}
return items[0];
}
// 字符串判空
function displayMessage(message) {
if (!message || message.trim() === '') {
console.warn('Message is empty or contains only whitespace');
return 'No message to display';
}
return message;
}
// 函數(shù)調(diào)用示例
const user = {
name: 'John Doe',
role: 'admin'
};
console.log(getUserRole(user)); // 輸出:'admin'
const items = ['apple', 'banana', 'orange'];
console.log(getFirstItem(items)); // 輸出:'apple'
console.log(displayMessage('Hello, World!')); // 輸出:'Hello, World!'
console.log(displayMessage('')); // 輸出:'No message to display'
- 類型檢查
在函數(shù)參數(shù)和關(guān)鍵變量處進行類型檢查,確保數(shù)據(jù)類型符合預(yù)期,避免因類型錯誤導(dǎo)致的邏輯問題。 - 示例:
// 函數(shù)參數(shù)類型檢查
function calculateTotal(prices) {
if (!Array.isArray(prices)) {
throw new TypeError('Expected prices to be an array');
}
if (!prices.every(price => typeof price === 'number')) {
throw new TypeError('All elements in prices array must be numbers');
}
return prices.reduce((total, price) => total + price, 0);
}
// 調(diào)用函數(shù)示例
try {
const prices = [10, 20, 30];
const total = calculateTotal(prices);
console.log('Total:', total); // 輸出:60
} catch (error) {
console.error('Error calculating total:', error.message);
}
// 錯誤調(diào)用示例
try {
const invalidPrices = [10, '20', 30];
calculateTotal(invalidPrices);
} catch (error) {
console.error('Error calculating total:', error.message); // 輸出:All elements in prices array must be numbers
}
- 異常處理
使用try/catch語句捕獲和處理可能發(fā)生的異常,防止程序因未處理的異常而崩潰,提高程序的健壯性和用戶體驗。 - 示例:
// 使用try/catch處理異常
try {
const result = divide(10, 0);
console.log('Result:', result);
} catch (error) {
console.error('Error:', error.message);
}
/**
* 除法函數(shù)
* @param {number} a - 被除數(shù)
* @param {number} b - 除數(shù)
* @returns {number} - 結(jié)果
*/
function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}