【區塊鏈】去中心化應用(DApp)開發——DApp 前端開發

一、DApp 前端開發基礎概念

1、 什麼是 DApp 前端?

DApp(去中心化應用)前端是與區塊鏈網路互動的使用者介面層,主要特點包括: • 使用 Web3.js 或 Ethers.js 等庫連接區塊鏈

• 通過錢包外掛(如 MetaMask)實現身份驗證

• 直接與智能合約互動而非傳統後端 API

2、推薦技術組合:

1. 框架:React/Vue
2. 區塊鏈互動:Ethers.js(比Web3.js更輕量)
3. 狀態管理:Redux/Zustand
4. UI庫:Material UI/TailwindCSS
5. 開發工具:Hardhat/Truffle

二、開發環境搭建

1、 初始化項目

# 使用Vite建立React項目(推薦)
npm create vite@latest dapp-frontend --template react
cd dapp-frontend

# 安裝核心依賴
npm install ethers @web3-react/core @web3-react/injected-connector

2、項目結構規範

/src
|-- /components    # 可復用元件
|-- /contracts     # 合約ABI和地址配置
|-- /hooks         # 自訂Hook
|-- /pages         # 頁面元件
|-- /styles        # 全域樣式
|-- /utils         # 工具函數

三、錢包連接實現(最佳實踐)

1、 錢包連接器配置

// src/utils/walletConnectors.js
import { InjectedConnector } from '@web3-react/injected-connector'

/**
 * 配置支援的區塊鏈網路
 * @constant
 * @type {object}
 * @default
 * @property {number[]} supportedChainIds - 支援的鏈ID列表
 */

export const injected = new InjectedConnector({
  supportedChainIds: [1, 5, 56] // 主網/Goerli/BSC
})

2、 錢包連接上下文

// src/contexts/Web3Context.jsx
import { createContext, useContext } from'react'
import { useWeb3React } from'@web3-react/core'

/**
 * Web3上下文提供者
 * @component
 * @param {object} props - 元件屬性
 * @returns {JSX.Element} 上下文提供者
 */

const Web3Context = createContext()

exportfunction Web3Provider({ children }) {
const { activate, deactivate, ...web3Data } = useWeb3React()

return (
    <Web3Context.Provider value={{ activate, deactivate, ...web3Data }}>
      {children}
    </Web3Context.Provider>
  )
}

/**
 * 自訂Hook:訪問Web3上下文
 * @hook
 * @returns {object} Web3上下文值
 */

exportfunction useWeb3() {
return useContext(Web3Context)
}

四、智能合約互動封裝

1、 合約Hook封裝

// src/hooks/useContract.js
import { useMemo } from'react'
import { Contract } from'ethers'

/**
 * 合約實例化Hook
 * @hook
 * @param {string} address - 合約地址
 * @param {Array} abi - 合約ABI
 * @returns {Contract|null} 合約實例
 */

exportdefaultfunction useContract(address, abi) {
const { library, account } = useWeb3()

return useMemo(() => {
    if (!address || !abi || !library) returnnull
    
    try {
      returnnew Contract(
        address,
        abi,
        account ? library.getSigner(account) : library
      )
    } catch (error) {
      console.error('合約實例化失敗', error)
      returnnull
    }
  }, [address, abi, library, account])
}

2、 帶重試機制的合約呼叫

// src/utils/contractCall.js
/**
 * 帶重試機制的合約呼叫
 * @async
 * @function
 * @param {function} callFn - 合約呼叫函數
 * @param {number} [retries=3] - 最大重試次數
 * @param {number} [delay=1000] - 重試間隔(ms)
 * @returns {Promise} 呼叫結果
 */

exportasyncfunction contractCallWithRetry(callFn, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
    try {
      returnawait callFn()
    } catch (err) {
      if (i === retries - 1) throw err
      awaitnewPromise(res => setTimeout(res, delay))
    }
  }
}

五、交易狀態管理方案

1、 交易狀態列舉

// src/constants/transactionState.js
/**
 * 交易狀態列舉
 * @enum {string}
 */

export const TX_STATE = {
  IDLE: 'idle',
  SIGNING: 'signing',
  MINING: 'mining',
  SUCCESS: 'success',
  ERROR: 'error'
}

2、 交易狀態跟蹤Hook

// src/hooks/useTransaction.js
import { useState } from'react'
import { TX_STATE } from'../constants/transactionState'

/**
 * 交易狀態管理Hook
 * @hook
 * @returns {Array} [txState, handleTransaction]
 */

exportfunction useTransaction() {
const [txState, setTxState] = useState(TX_STATE.IDLE)

/**
   * 執行交易並跟蹤狀態
   * @async
   * @function
   * @param {function} txFn - 交易執行函數
   * @param {object} [options] - 配置選項
   * @param {function} [options.onSuccess] - 成功回呼
   * @param {function} [options.onError] - 失敗回呼
   */

asyncfunction handleTransaction(txFn, { onSuccess, onError } = {}) {
    try {
      setTxState(TX_STATE.SIGNING)
      const tx = await txFn()
      
      setTxState(TX_STATE.MINING)
      const receipt = await tx.wait()
      
      setTxState(TX_STATE.SUCCESS)
      onSuccess?.(receipt)
    } catch (err) {
      setTxState(TX_STATE.ERROR)
      onError?.(err)
    } finally {
      setTimeout(() => setTxState(TX_STATE.IDLE), 3000)
    }
  }

return [txState, handleTransaction]
}

六、最佳實踐建議

1、 錯誤處理原則

• 所有區塊鏈呼叫必須包含try/catch

• 區分使用者拒絕簽名和真實錯誤

• 提供友好的錯誤提示

2、 性能最佳化方案

// 使用Promise.all平行查詢
const [balance, allowance] = await Promise.all([
  contract.balanceOf(account),
  contract.allowance(account, spender)
])

3、安全注意事項

• 永遠不要在前端硬編碼私鑰

• 合約地址應該可配置(不同環境不同地址)

• 對使用者輸入進行嚴格驗證

4、 測試策略

- 單元測試:Jest測試工具函數
- 整合測試:測試合約互動元件
- E2E測試:使用Cypress模擬使用者流程 (技術探索驛站)