前端开发规范

前端开发规范

一、代码风格

1. 缩进

  • 统一使用2个空格缩进,避免使用Tab键,确保代码在不同编辑器和开发环境中的显示一致性。
    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等)和函数声明,即使只有一行代码,也必须使用大括号包裹,增强代码的可读性和可维护性。
  • 大括号位于语句行的末尾,左大括号({)与语句行在同一行,右大括号(})单独占一行。
  • 示例:
if (condition) {
  executeSomeLogic();
} else {
  executeAlternativeLogic();
}

3.括号使用

  • 函数调用和定义时,始终使用括号,即使没有参数,也保留空括号,明确表示这是一个函数调用或定义。
  • 示例:
function logMessage() {
  console.log('Message logged');
}

logMessage();

4.逗号使用

  • 对象或数组定义时,最后一个元素后不加多余的逗号,避免潜在的解析错误和兼容性问题。
  • 示例:
const person = {
  name: 'John Doe',
  age: 30,
  occupation: 'Developer' // 没有多余的逗号
};

const numbers = [1, 2, 3]; // 没有多余的逗号

5.引号使用

  • 字符串优先使用单引号(')进行包裹,只有在字符串中包含单引号字符时,才使用双引号(")。
  • 示例:
const greeting = 'Hello, World!';
const quote = "Don't worry, be happy!";

二、命名规范

1.变量命名

  • 采用驼峰命名法(camelCase),即除首个单词外,每个单词的首字母大写,其余字母小写,无下划线或其他分隔符。
  • 变量名应简洁明了,准确反映其存储的数据内容和用途,避免使用模糊、无意义的名称,如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.函数命名

  • 函数名采用驼峰命名法,且函数名应具有动词性质,能准确体现函数的功能和作用,如getUserInfo、submitForm、calculateTotal等。
  • 示例:
// 正确示例
function fetchUserData(userId) {
  // 获取用户数据的逻辑
}

function validateFormInputs(form) {
  // 验证表单输入的逻辑
}

// 错误示例
function ud(userId) { // 函数名不够清晰
  // 获取用户数据的逻辑
}

function vf(form) { // 函数名不够清晰
  // 验证表单输入的逻辑
}

4.类命名

  • 类名采用帕斯卡命名法(PascalCase),即每个单词的首字母大写,无下划线或其他分隔符,类名应能体现该类的职责和功能。
  • 示例:
class UserManager {
  // 用户管理类的逻辑
}

class DataProcessor {
  // 数据处理类的逻辑
}

三、注释规范

1.单行注释

  • 在代码的单行注释中,在注释内容前添加空格,提高注释的可读性,注释内容应简洁明了,解释代码的功能、目的或注意事项。
  • 示例:
// 用户名验证
if(userName === 'admin') {

}
// 用户名验证正则表达式,规则为:长度4-20位,只能包含字母、数字和下划线
const usernameRegex = /^[a-zA-Z0-9_]{4,20}$/;

2.多行注释

  • 对于多行注释,使用/.../包裹,每行注释内容前添加空格,并在注释的开头和结尾留出一行空行,使注释区域清晰易读,用于解释较复杂的逻辑或算法。
  • 示例:
/*
 * 用户管理类
 * 负责用户数据的增删改查操作。
 */
class UserManager {
  async getUsers() {
    // 获取用户列表
  }

  async addUser(user) {
    // 添加用户
  }

  async deleteUser(userId) {
    // 删除用户
  }
}
/*
 * 定义用户资料卡片的样式。
 * 包括背景颜色、边框、圆角和内边距。
 */
.user-profile {
  background-color: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 16px;
}

3.函数注释

  • 在每个函数上方添加注释,说明函数的功能、参数、返回值和可能的异常情况,便于其他开发人员理解和使用该函数,特别是对于公共函数或库函数。
  • 示例:
/**
* 格式化日期为指定的字符串格式
* @param {Date} date - 要格式化的日期对象
* @param {string} format - 目标日期格式,如'yyyy-MM-dd', 'MM/dd/yyyy'等
* @returns {string} 格式化后的日期字符串
* @throws {Error} 如果format参数格式不正确或不支持
 */
function formatDate(date, format) {
  // 格式化日期的逻辑
}

四、组件开发规范

1.组件命名

  • 组件名采用帕斯卡命名法,体现组件的功能和用途,确保组件名在整个项目中具有唯一性,避免与其他组件或全局元素冲突。
  • 示例:
<!-- 正确示例: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.组件结构

  • 组件代码结构清晰,包含script(定义逻辑)、template(描述模板)、style(定义样式)部分,各部分之间使用明确的分隔线(如注释)区分,提高代码的可读性和可维护性。
  • 示例:
<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 父子组件通信

  • 父组件向子组件传递数据通过props,子组件需对props进行类型验证和必要性检查,确保接收到的数据符合预期。
  • 子组件向父组件传递事件通过emits,父组件监听子组件触发的事件,执行相应的回调函数。
  • 示例:
<!-- 父组件: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); // 触发自定义事件,将更新后的用户信息传递给父组件
};
</script>

3.2 兄弟组件通信

  • 通过共同的祖先组件或状态管理库(如Pinia)进行通信,避免直接的操作和数据共享,确保组件之间的解耦和数据的一致性。

4.组件性能优化

  • 避免不必要的渲染
    在组件中,合理使用computed属性和watch监听器,只在必要的时候触发组件的重新渲染,避免因数据频繁变化导致的性能问题。

  • 使用keep-alive缓存组件
    对频繁切换但内容不经常变化的组件,使用<keep-alive>标签进行缓存,减少组件的销毁和重新创建的开销,提升用户体验。

  • 示例:

<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 规范

1.命名规范

  • 采用有意义的名称:CSS 类名应能直观地反映其样式的作用和用途,避免使用无意义的名称,如div1、box等,同时避免过度依赖元素的外观特征命名,如red-box、big-button,因为样式可能会随需求变化而改变。
  • 使用局部命名空间:对于组件内部的样式,采用基于组件名的命名空间,避免样式污染和冲突,增强样式的可维护性和可复用性。
  • 示例:
/* 错误示例 */
.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命名方法,有助于创建可复用的、一致的CSS样式结构。Block表示独立的、可复用的模块;Element表示Block内的组成部分,其类名以Block名称作为前缀,并用两个下划线(__)分隔;Modifier表示Element的不同状态或样式变体,其类名以Element名称作为前缀,并用两个连字符(--)分隔。

  • 使用CSS预处理器(Less)
    利用Less提供的变量、混合、函数等功能,实现样式的复用和灵活管理,提高样式的可维护性。

  • 示例:

// 变量定义
@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等)
    内外边距(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)定义不同屏幕尺寸下的样式,确保页面在各种设备上都能正常显示和操作。
  • 示例:
/* 桌面端样式 */
.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;
  }
}

六、文件名规范

1.组件文件

  • 组件文件名采用帕斯卡命名法,与组件名保持一致,文件后缀名为.vue。
  • 示例:
// 正确示例
UserCard.vue
LoginForm.vue
HomePage.vue

// 错误示例
user-card.vue
login_form.vue
home-page.vue

2.JS 文件

  • JS 文件名采用驼峰命名法,文件名应能体现文件的内容和功能,文件后缀名为.js。
  • 示例:
// 正确示例
userData.js
formValidation.js
apiService.js

// 错误示例
user-data.js
form-validation.js
api-service.js

3.CSS 文件

  • CSS 文件名采用驼峰命名法或短横线命名法,文件名应能体现样式的作用域和功能,文件后缀名为.less(使用 Less 预处理器时)或.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.图片文件

  • 图片文件名采用短横线命名法或下划线命名法,文件名简洁明了,体现图片的内容和用途,文件格式常用.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 编程规范

1.变量声明

  • 使用const或let声明变量:避免使用var,因为它会产生变量提升和作用域污染等问题。使用const声明不变的常量,使用let声明可变的变量。
  • 变量使用前必须声明:避免使用未声明的变量,防止潜在的错误和难以排查的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.对象和数组

  • 对象创建和解构
    使用对象字面量创建对象,利用对象解构赋值提取对象的属性值,提高代码的可读性和简洁性。
  • 示例:
// 创建对象
const user = {
  id: 1,
  name: 'John Doe',
  age: 30,
  email: 'john@example.com'
};

// 解构对象
const { id, name, age, email } = user;
console.log(`User ID: ${id}, Name: ${name}, Age: ${age}, Email: ${email}`);
  • 数组操作
    使用数组的内置方法(如map、filter、reduce等)操作数组,避免使用循环和下标直接操作数组元素,使代码更简洁、优雅。
  • 示例:
// 使用map方法处理数组
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(number => number * 2);
console.log(doubledNumbers); // 输出:[2, 4, 6, 8, 10]

// 使用filter方法过滤数组
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // 输出:[2, 4]

// 使用reduce方法计算数组的和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出:15

3.函数

  • 函数表达式和箭头函数
    优先使用函数表达式和箭头函数定义函数,避免使用函数声明语句,特别是对于回调函数和简短的函数处理。
  • 示例:
// 函数表达式
const greet = function(name) {
  console.log(`Hello, ${name}!`);
};
greet('John');

// 箭头函数
const square = x => x * x;
console.log(square(5)); // 输出:25

const add = (a, b) => a + b;
console.log(add(3, 7)); // 输出:10
  • 函数参数和默认值
    函数参数数量尽量控制在合理范围内,避免过多参数导致函数难以维护和理解。使用函数参数的默认值简化函数调用。
  • 示例:
// 函数参数过多的示例(不推荐)
function createPerson(firstName, lastName, age, gender, occupation) {
  // 创建人物对象的逻辑
}

// 使用对象作为参数的示例(推荐)
function createPerson({ firstName, lastName, age, gender, occupation }) {
  // 创建人物对象的逻辑
}

// 调用函数
createPerson({
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
  gender: 'male',
  occupation: 'developer'
});

// 函数参数默认值示例
function multiply(a, b = 1) {
  return a * b;
}

console.log(multiply(5)); // 输出:5
console.log(multiply(5, 3)); // 输出:15

4.异步编程

  • 使用async/await
    对于异步操作,优先使用async/await语法,使异步代码的书写更接近同步代码,提高代码的可读性和可维护性。
  • 示例:
// 使用async/await获取用户数据
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;
  }
}

// 调用异步函数
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获取用户数据
function fetchUserDataPromise(userId) {
  return new Promise((resolve, reject) => {
    axios.get(`/api/users/${userId}`)
      .then(response => {
        resolve(response.data);
      })
      .catch(error => {
        reject(error);
      });
  });
}

// 调用Promise
fetchUserDataPromise(1)
  .then(userData => {
    console.log('User Data:', userData);
  })
  .catch(error => {
    console.error('Error fetching user data:', error);
  });

5.防御性编程

  • 判空处理
    在操作对象、数组、字符串等数据类型时,进行判空处理,避免因数据为空导致的运行时错误。
  • 示例:
// 对象判空
function getUserRole(user) {
  if (!user) {
    console.warn('User object is null or undefined');
    return null;
  }
  return user.role || 'guest';
}

// 对象判空,链式调用
function getUserRole(user) {
  return user?.role || 'guest';
}

// 数组判空
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;
}

// 函数调用示例
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'
  • 类型检查
    在函数参数和关键变量处进行类型检查,确保数据类型符合预期,避免因类型错误导致的逻辑问题。
  • 示例:
// 函数参数类型检查
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);
}

// 调用函数示例
try {
  const prices = [10, 20, 30];
  const total = calculateTotal(prices);
  console.log('Total:', total); // 输出:60
} catch (error) {
  console.error('Error calculating total:', error.message);
}

// 错误调用示例
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语句捕获和处理可能发生的异常,防止程序因未处理的异常而崩溃,提高程序的健壮性和用户体验。
  • 示例:
// 使用try/catch处理异常
try {
  const result = divide(10, 0);
  console.log('Result:', result);
} catch (error) {
  console.error('Error:', error.message);
}

/**
 * 除法函数
 * @param {number} a - 被除数
 * @param {number} b - 除数
 * @returns {number} - 结果
 */
function divide(a, b) {
  if (b === 0) {
    throw new Error('Cannot divide by zero');
  }
  return a / b;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、目录结构 1.1 只列出了src下的目录结构,项目环境目录自行配置 二、代码风格 2.1 代码缩进tab为4个...
    LazyG阅读 1,144评论 2 10
  • 一、编程规范 (一)命名规范 1.1.1 项目命名 全部采用小写方式, 以中划线分隔。正例:mall-manage...
    谢小胖阅读 713评论 0 0
  • 前端代码规范 Front Standard Guide 前端 JS 项目开发规范 规范的目的是为了编写高质量的代码...
    养樂多_566c阅读 330评论 0 0
  • https://www.runoob.com/w3cnote/front-end-standards.html[h...
    想成为咸鱼的张三阅读 258评论 0 0
  • 前端开发规范 规范目的 命名规范 结构化规范 注释规范 编码规范 CSS 规范 规范目的 为提高团队协作效率 便于...
    妹妹十六阅读 182评论 0 0