高质量前端代码

好的前端代码应该具备哪些特征

问题

什么样的前端代码是好的?如何评判代码质量?

解答

好的前端代码具备以下特征:可读、可维护、可测试、高性能、健壮。

1. 可读性

差的代码:

// 命名模糊,逻辑混乱
function fn(a, b, c) {
  return a.filter(x => x.d > b && x.e < c).map(x => ({ ...x, f: x.d * 0.8 }))
}

好的代码:

// 命名清晰,意图明确
function filterProductsByPriceRange(products, minPrice, maxPrice) {
  const isInPriceRange = (product) => 
    product.price >= minPrice && product.price <= maxPrice

  const applyDiscount = (product) => ({
    ...product,
    discountedPrice: product.price * 0.8
  })

  return products
    .filter(isInPriceRange)
    .map(applyDiscount)
}

2. 可维护性

差的代码:

// 高耦合,难以修改
class UserPage {
  async loadData() {
    const res = await fetch('/api/user')
    const user = await res.json()
    document.getElementById('name').innerText = user.name
    document.getElementById('avatar').src = user.avatar
    localStorage.setItem('user', JSON.stringify(user))
  }
}

好的代码:

// 职责分离,低耦合
// api.js - 数据获取
export const fetchUser = () => fetch('/api/user').then(res => res.json())

// storage.js - 存储逻辑
export const saveUser = (user) => localStorage.setItem('user', JSON.stringify(user))

// UserPage.js - 页面逻辑
class UserPage {
  constructor(api, storage, renderer) {
    this.api = api
    this.storage = storage
    this.renderer = renderer
  }

  async loadData() {
    const user = await this.api.fetchUser()
    this.storage.saveUser(user)
    this.renderer.render(user)
  }
}

3. 可测试性

差的代码:

// 依赖全局状态,无法单元测试
function calculateTotal() {
  const items = window.cartItems
  const discount = window.userDiscount
  return items.reduce((sum, item) => sum + item.price, 0) * discount
}

好的代码:

// 纯函数,易于测试
function calculateTotal(items, discount = 1) {
  if (!Array.isArray(items)) return 0
  
  const subtotal = items.reduce((sum, item) => sum + (item.price || 0), 0)
  return subtotal * discount
}

// 测试用例
console.assert(calculateTotal([{ price: 100 }, { price: 200 }], 0.9) === 270)
console.assert(calculateTotal([], 1) === 0)
console.assert(calculateTotal(null) === 0)

4. 性能

差的代码:

// 每次渲染都创建新函数和新对象
function ProductList({ products }) {
  return (
    <ul>
      {products.map(p => (
        <li 
          key={p.id} 
          style={{ color: 'blue' }}
          onClick={() => console.log(p.id)}
        >
          {p.name}
        </li>
      ))}
    </ul>
  )
}

好的代码:

// 缓存函数和样式
const itemStyle = { color: 'blue' }

function ProductItem({ product, onClick }) {
  return (
    <li style={itemStyle} onClick={onClick}>
      {product.name}
    </li>
  )
}

function ProductList({ products }) {
  const handleClick = useCallback((id) => {
    console.log(id)
  }, [])

  return (
    <ul>
      {products.map(p => (
        <ProductItem 
          key={p.id} 
          product={p} 
          onClick={() => handleClick(p.id)}
        />
      ))}
    </ul>
  )
}

5. 健壮性

差的代码:

// 没有错误处理
async function getUserName(userId) {
  const res = await fetch(`/api/users/${userId}`)
  const data = await res.json()
  return data.profile.name
}

好的代码:

// 完善的错误处理
async function getUserName(userId) {
  if (!userId) {
    throw new Error('userId is required')
  }

  try {
    const res = await fetch(`/api/users/${userId}`)
    
    if (!res.ok) {
      throw new Error(`HTTP error: ${res.status}`)
    }
    
    const data = await res.json()
    return data?.profile?.name ?? 'Unknown'
    
  } catch (error) {
    console.error('Failed to fetch user:', error)
    throw error
  }
}

关键点

  • 命名清晰:变量、函数名能准确表达意图,不用缩写和单字母
  • 职责单一:每个函数/模块只做一件事,便于复用和修改
  • 纯函数优先:减少副作用,输入输出可预测,方便测试
  • 防御性编程:处理边界情况、空值、异常,代码不会意外崩溃
  • 适度抽象:不过早优化,不过度设计,在可读性和复用性间平衡