0.整体架构
MetaMask UI端采用了分层架构设计,每一层都有明确的职责:
- UI层:负责用户界面的展示和交互
- Hooks层:封装业务逻辑和状态管理
- Selectors层:提供高效的数据访问和派生
- Ducks层:模块化的状态管理,每个功能域独立
- Store层:统一的Redux状态管理中心
- Background Connection:UI与后台的通信桥梁
- 后台脚本:核心业务逻辑和数据处理
graph TB
subgraph "MetaMask UI 架构"
subgraph "UI层"
A1[账户管理组件]
A2[交易确认组件]
A3[设置页面组件]
A4[代币列表组件]
end
subgraph "Hooks层"
B1[useTokenTracker]
B2[useGasEstimates]
B3[useNotifications]
B4[useAccountBalance]
end
subgraph "Selectors层"
C1[Account Selectors]
C2[Transaction Selectors]
C3[Network Selectors]
C4[UI State Selectors]
end
subgraph "Ducks层"
D1[Metamask Duck]
D2[App State Duck]
D3[Send Duck]
D4[Swaps Duck]
D5[Gas Duck]
D6[History Duck]
end
subgraph "Store层"
E1[Redux Store]
E2[Root Reducer]
E3[Middleware]
end
subgraph "Background Connection"
F1[submitRequestToBackground]
F2[onNotification]
F3[setBackgroundConnection]
end
subgraph "后台脚本"
G1[Controllers]
G2[Services]
G3[Utils]
end
end
A1 --> B1
A2 --> B2
A3 --> B3
A4 --> B4
B1 --> C1
B2 --> C2
B3 --> C3
B4 --> C4
C1 --> D1
C2 --> D3
C3 --> D1
C4 --> D2
D1 --> E2
D2 --> E2
D3 --> E2
D4 --> E2
D5 --> E2
D6 --> E2
E2 --> E1
E1 --> F1
E1 --> F2
E1 --> F3
F1 --> G1
F2 --> G2
F3 --> G3
1.UI → Background 交互机制
核心通信桥梁:background-connection.ts
typescript
// metamask-extension/ui/store/background-connection.ts
export const callBackgroundMethod = <R>(
method: string, // Background 方法名
args: unknown[], // 参数数组
callback: (error: Error | null, result?: R) => void // 回调函数
): void => {
// 通过 postMessage 与 Background 通信
const message = {
id: generateId(),
method,
args,
origin: window.location.origin,
};
// 发送到 Background
window.postMessage(message, '*');
// 监听 Background 的响应
window.addEventListener('message', handleResponse);
};
通信流程
css
UI 组件 → callBackgroundMethod → postMessage → Background Service Worker
↓
Background 处理请求 → 调用对应 Controller → 返回结果
↓
Background → postMessage → UI → 回调处理结果
实际调用示例
typescript
// 在 UI 中调用 Background 方法
callBackgroundMethod(
'addNewAccount', // Background 方法名
[], // 参数
(error, result) => { // 回调
if (error) {
console.error('添加账户失败:', error);
} else {
console.log('新账户地址:', result);
}
}
);
2. UI 核心模块详解
2.1 ui/store
- 状态管理中心
actions.ts
- 后台方法桥接
typescript
// metamask-extension/ui/store/actions.ts
export function addNewAccount() {
return (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication()); // 显示加载状态
return new Promise<void>((resolve, reject) => {
// 调用 Background 方法
callBackgroundMethod('addNewAccount', [], (err) => {
if (err) {
dispatch(displayWarning(err)); // 显示错误
reject(err);
return;
}
dispatch(showAccountsPage()); // 跳转账户页
resolve();
});
})
.then(() => dispatch(hideLoadingIndication()))
.catch(() => dispatch(hideLoadingIndication()));
};
}
作用:
- 封装
callBackgroundMethod
调用 - 管理 UI 状态(加载、错误、成功)
- 提供 Redux Thunk 接口
store.ts
- Redux Store 配置
typescript
// metamask-extension/ui/store/store.ts
export function configureStore(initialState = {}) {
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(thunk, logger),
// 其他中间件...
)
);
return store;
}
作用:
- 配置 Redux Store
- 集成中间件(thunk、logger 等)
- 提供状态管理基础设施
2.2 ui/selectors
- 数据选择与派生
选择器的作用
typescript
// metamask-extension/ui/selectors/metamask-notifications/metamask-notifications.ts
export const getValidNotificationAccounts = createSelector(
[getMetamask], // 依赖选择器
(metamask): string[] => metamask.subscriptionAccountsSeen // 派生逻辑
);
// 使用 reselect 进行 memoization
export const getMetamaskNotificationsUnreadCount = createSelector(
[getMetamaskNotifications],
(notifications): number => {
return notifications
? notifications.filter((notification) => !notification.isRead).length
: 0;
},
);
作用:
- 数据投影:从复杂状态中提取所需数据
- 性能优化:memoization 避免不必要的重计算
- 数据转换:将原始状态转换为 UI 友好的格式
- 依赖管理:声明式地管理数据依赖关系
选择器层次结构
typescript
// 基础选择器
const getMetamask = (state: AppState) => state.metamask;
// 派生选择器
export const getMetamaskNotifications = createSelector(
[getMetamask],
(metamask): Notification[] => metamask.metamaskNotificationsList,
);
// 复合选择器
export const getUnreadNotifications = createSelector(
[getMetamaskNotifications],
(notifications) => notifications.filter(n => !n.isRead)
);
2.3 ui/hooks
- 业务逻辑封装
自定义 Hook 的作用
typescript
// metamask-extension/ui/hooks/useNotificationAccounts.ts
function useNotificationAccounts() {
// 使用选择器获取数据
const accountAddresses = useSelector(getValidNotificationAccounts);
const internalAccounts = useSelector(getInternalAccounts);
// 业务逻辑:地址映射为账户对象
const accounts = useMemo(() => {
return accountAddresses
.map((addr) => {
const account = internalAccounts.find(
(a) => a.address.toLowerCase() === addr.toLowerCase(),
);
return account;
})
.filter(<T,>(val: T | undefined): val is T => Boolean(val));
}, [accountAddresses, internalAccounts]);
return accounts;
}
作用:
- 逻辑复用:封装常用的业务逻辑
- 状态管理:管理组件内部状态
- 副作用处理:处理异步操作、事件订阅等
- 数据转换:将选择器数据转换为组件所需格式
Hook 与选择器的协作
typescript
// 选择器:纯数据获取
const getAccountBalance = createSelector(
[getSelectedAccount, getCurrentNetwork],
(account, network) => ({ account, network })
);
// Hook:业务逻辑 + 状态管理
function useAccountBalance() {
const { account, network } = useSelector(getAccountBalance);
const [balance, setBalance] = useState(null);
useEffect(() => {
if (account && network) {
fetchBalance(account.address, network.chainId)
.then(setBalance);
}
}, [account, network]);
return balance;
}
2.4 ui/ducks
- 功能模块化
Duck 模式的作用
typescript
// metamask-extension/ui/ducks/notifications/notifications.js
// 一个 Duck 包含:actions、reducers、selectors、types
// Actions
export const SHOW_NOTIFICATION = 'notifications/SHOW_NOTIFICATION';
export const HIDE_NOTIFICATION = 'notifications/HIDE_NOTIFICATION';
// Action Creators
export const showNotification = (message) => ({
type: SHOW_NOTIFICATION,
payload: message,
});
// Reducer
const initialState = { messages: [] };
export default function notificationsReducer(state = initialState, action) {
switch (action.type) {
case SHOW_NOTIFICATION:
return { ...state, messages: [...state.messages, action.payload] };
case HIDE_NOTIFICATION:
return { ...state, messages: state.messages.filter(m => m.id !== action.payload) };
default:
return state;
}
}
// Selectors
export const getNotifications = (state) => state.notifications.messages;
作用:
- 模块化:将相关功能组织在一起
- 可维护性:降低代码耦合度
- 可测试性:独立测试功能模块
- 可重用性:在不同页面间复用功能
3. 完整的数据流示例
场景:用户添加新账户
typescript
// 1. UI 组件触发
function AddAccountButton() {
const dispatch = useDispatch();
const handleClick = () => {
dispatch(addNewAccount()); // 派发 action
};
return <button onClick={handleClick}>添加账户</button>;
}
// 2. Action 处理
export function addNewAccount() {
return (dispatch) => {
dispatch(showLoadingIndication()); // 更新 UI 状态
return new Promise((resolve, reject) => {
// 调用 Background
callBackgroundMethod('addNewAccount', [], (err, result) => {
if (err) {
dispatch(displayWarning(err));
reject(err);
} else {
dispatch(showAccountsPage());
resolve(result);
}
});
});
};
}
// 3. Background 处理
// app/scripts/metamask-controller.js
async addNewAccount(accountCount, _keyringId) {
const addedAccountAddress = await this.keyringController.withKeyring(
{ type: KeyringTypes.hd, index: 0 },
async ({ keyring }) => {
const [newAddress] = await keyring.addAccounts(1);
return newAddress;
},
);
return addedAccountAddress;
}
// 4. 状态更新
// KeyringController 更新状态 → 触发 stateChange 事件
// UI 通过选择器获取新状态 → 重新渲染
4. 模块间的协作关系
sequenceDiagram
participant UI as UI Component
participant Hook as Custom Hook
participant Selector as Selector
participant Duck as Duck (Actions/Reducers)
participant Store as Redux Store
participant BC as Background Connection
participant BG as Background Script
participant State as State Update
UI->>Hook: 1. 用户操作触发Hook
Hook->>Selector: 2. 获取当前状态
Selector->>Store: 3. 从Store读取状态
Store->>Selector: 4. 返回状态数据
Selector->>Hook: 5. 提供状态数据
Hook->>Duck: 6. 派发Action
Duck->>Store: 7. dispatch action到Store
Store->>Duck: 8. 调用Reducer更新状态
Duck->>BC: 9. 调用后台方法
BC->>BG: 10. 发送请求到后台
BG->>State: 11. 处理业务逻辑
State->>BG: 12. 返回处理结果
BG->>BC: 13. 响应结果
BC->>Duck: 14. 返回后台响应
Duck->>Store: 15. 更新Store状态
Store->>Selector: 16. 通知状态变化
Selector->>Hook: 17. 提供新状态
Hook->>UI: 18. 触发组件重新渲染
5. 总结
MetaMask UI 架构的核心是:
ui/store
:状态管理中心,通过 actions 桥接 UI 与 Backgroundui/selectors
:数据选择与派生,提供 memoized 的数据访问ui/hooks
:业务逻辑封装,管理组件状态和副作用ui/ducks
:功能模块化,组织相关功能代码
这种架构实现了:
- 关注点分离:UI、状态、逻辑各司其职
- 性能优化:memoization 避免无效重渲染
- 可维护性:模块化设计降低耦合度
- 可测试性:纯函数和独立模块便于测试
学习交流请添加vx: gh313061
下期预告:一次交易的全流程分析