•
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/Truffle1、 初始化項目
# 使用Vite建立React項目(推薦)
npm create vite@latest dapp-frontend --template react
cd dapp-frontend
# 安裝核心依賴
npm install ethers @web3-react/core @web3-react/injected-connector2、項目結構規範
/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模擬使用者流程 (技術探索驛站)