ngrx-devtool-debugger
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNgRx DevTool Debugger
NgRx DevTool 调试工具
Skill by ara.so — Devtools Skills collection.
NgRx DevTool is a comprehensive debugging and visualization tool for NgRx state management in Angular applications. It provides real-time action monitoring, effect tracking, state visualization with diff viewer, and performance metrics without requiring browser extensions. The tool runs a separate WebSocket server that your Angular app connects to, displaying all NgRx activity in a dedicated UI.
由 ara.so 提供的技能 — 开发工具技能合集。
NgRx DevTool 是一款用于Angular应用中NgRx状态管理的综合性调试与可视化工具。它无需浏览器扩展,即可提供实时动作监控、效果追踪、带差异查看器的状态可视化以及性能指标。该工具运行一个独立的WebSocket服务器,你的Angular应用可连接至该服务器,在专用UI中展示所有NgRx活动。
Installation
安装
Install the package as a development dependency:
bash
npm install --save-dev @amadeus-it-group/ngrx-devtoolOr with yarn:
bash
yarn add -D @amadeus-it-group/ngrx-devtool将该包作为开发依赖安装:
bash
npm install --save-dev @amadeus-it-group/ngrx-devtool或使用yarn:
bash
yarn add -D @amadeus-it-group/ngrx-devtoolBasic Setup
基础配置
1. Configure Your Angular Application
1. 配置你的Angular应用
Add the DevTool provider and meta-reducer to your application configuration:
typescript
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import {
provideNgrxDevTool,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import * as fromRoot from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
fromRoot.reducers,
{
metaReducers: [createDevToolMetaReducer()]
}
),
provideEffects([AppEffects]),
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000',
trackEffects: true,
enabled: true
})
]
};将DevTool提供者和元 reducer 添加到应用配置中:
typescript
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import {
provideNgrxDevTool,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import * as fromRoot from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
fromRoot.reducers,
{
metaReducers: [createDevToolMetaReducer()]
}
),
provideEffects([AppEffects]),
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000',
trackEffects: true,
enabled: true
})
]
};2. Module-Based Configuration (Legacy)
2. 基于模块的配置(旧版)
If using NgModule instead of standalone components:
typescript
// app.module.ts
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import {
NgRxDevToolModule,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import { reducers } from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
@NgModule({
imports: [
StoreModule.forRoot(reducers, {
metaReducers: [createDevToolMetaReducer()]
}),
EffectsModule.forRoot([AppEffects]),
NgRxDevToolModule.forRoot({
wsUrl: 'ws://localhost:4000',
trackEffects: true
})
]
})
export class AppModule {}如果使用NgModule而非独立组件:
typescript
// app.module.ts
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import {
NgRxDevToolModule,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import { reducers } from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
@NgModule({
imports: [
StoreModule.forRoot(reducers, {
metaReducers: [createDevToolMetaReducer()]
}),
EffectsModule.forRoot([AppEffects]),
NgRxDevToolModule.forRoot({
wsUrl: 'ws://localhost:4000',
trackEffects: true
})
]
})
export class AppModule {}CLI Commands
CLI命令
Start the DevTool Server
启动DevTool服务器
The primary command to launch both the WebSocket server and UI:
bash
npx ngrx-devtoolThis starts:
- WebSocket server on
ws://localhost:4000 - UI server on
http://localhost:3000
启动WebSocket服务器和UI的主要命令:
bash
npx ngrx-devtool这将启动:
- 位于 的WebSocket服务器
ws://localhost:4000 - 位于 的UI服务器
http://localhost:3000
Custom Port Configuration
自定义端口配置
bash
undefinedbash
undefinedChange WebSocket port
修改WebSocket端口
npx ngrx-devtool --ws-port 5000
npx ngrx-devtool --ws-port 5000
Change UI port
修改UI端口
npx ngrx-devtool --ui-port 8080
npx ngrx-devtool --ui-port 8080
Custom ports for both
同时自定义两个端口
npx ngrx-devtool --ws-port 5000 --ui-port 8080
undefinednpx ngrx-devtool --ws-port 5000 --ui-port 8080
undefinedServer Only Mode
仅服务器模式
Run only the WebSocket server without the UI:
bash
npx ngrx-devtool --server-only仅运行WebSocket服务器,不启动UI:
bash
npx ngrx-devtool --server-onlyConfiguration Options
配置选项
DevTool Configuration Interface
DevTool配置接口
typescript
interface NgRxDevToolConfig {
// WebSocket server URL (default: 'ws://localhost:4000')
wsUrl?: string;
// Enable/disable the devtool (default: true in development)
enabled?: boolean;
// Track NgRx effects (default: true)
trackEffects?: boolean;
// Maximum number of actions to store (default: 100)
maxActions?: number;
// Automatically connect on initialization (default: true)
autoConnect?: boolean;
// Reconnection attempts (default: 3)
reconnectAttempts?: number;
// Reconnection delay in ms (default: 1000)
reconnectDelay?: number;
}typescript
interface NgRxDevToolConfig {
// WebSocket服务器URL(默认值:'ws://localhost:4000')
wsUrl?: string;
// 启用/禁用开发工具(默认:开发环境下为true)
enabled?: boolean;
// 追踪NgRx效果(默认值:true)
trackEffects?: boolean;
// 存储的最大动作数量(默认值:100)
maxActions?: number;
// 初始化时自动连接(默认值:true)
autoConnect?: boolean;
// 重连尝试次数(默认值:3)
reconnectAttempts?: number;
// 重连延迟(毫秒,默认值:1000)
reconnectDelay?: number;
}Environment-Based Configuration
基于环境的配置
typescript
// app.config.ts
import { isDevMode } from '@angular/core';
import { provideNgrxDevTool, createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: isDevMode() ? [createDevToolMetaReducer()] : []
}
),
provideNgrxDevTool({
wsUrl: `ws://${window.location.hostname}:4000`,
enabled: isDevMode(),
trackEffects: true,
maxActions: 200,
reconnectAttempts: 5
})
]
};typescript
// app.config.ts
import { isDevMode } from '@angular/core';
import { provideNgrxDevTool, createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: isDevMode() ? [createDevToolMetaReducer()] : []
}
),
provideNgrxDevTool({
wsUrl: `ws://${window.location.hostname}:4000`,
enabled: isDevMode(),
trackEffects: true,
maxActions: 200,
reconnectAttempts: 5
})
]
};Production-Safe Configuration
生产环境安全配置
typescript
// app.config.ts
import { environment } from './environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: environment.devToolEnabled
? [createDevToolMetaReducer()]
: []
}
),
provideNgrxDevTool({
wsUrl: environment.devToolWsUrl,
enabled: environment.devToolEnabled,
trackEffects: environment.devToolEnabled
})
]
};typescript
// environments/environment.ts
export const environment = {
production: false,
devToolEnabled: true,
devToolWsUrl: 'ws://localhost:4000'
};
// environments/environment.prod.ts
export const environment = {
production: true,
devToolEnabled: false,
devToolWsUrl: ''
};typescript
// app.config.ts
import { environment } from './environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: environment.devToolEnabled
? [createDevToolMetaReducer()]
: []
}
),
provideNgrxDevTool({
wsUrl: environment.devToolWsUrl,
enabled: environment.devToolEnabled,
trackEffects: environment.devToolEnabled
})
]
};typescript
// environments/environment.ts
export const environment = {
production: false,
devToolEnabled: true,
devToolWsUrl: 'ws://localhost:4000'
};
// environments/environment.prod.ts
export const environment = {
production: true,
devToolEnabled: false,
devToolWsUrl: ''
};Usage Patterns
使用模式
Monitoring Actions
监控动作
Once configured, all dispatched actions are automatically tracked:
typescript
// user.actions.ts
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User List] Load Users');
export const loadUsersSuccess = createAction(
'[User API] Load Users Success',
props<{ users: User[] }>()
);
export const loadUsersFailure = createAction(
'[User API] Load Users Failure',
props<{ error: string }>()
);
// Component dispatch - automatically tracked
this.store.dispatch(loadUsers());配置完成后,所有派发的动作都会被自动追踪:
typescript
// user.actions.ts
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User List] Load Users');
export const loadUsersSuccess = createAction(
'[User API] Load Users Success',
props<{ users: User[] }>()
);
export const loadUsersFailure = createAction(
'[User API] Load Users Failure',
props<{ error: string }>()
);
// 组件派发动作 - 自动被追踪
this.store.dispatch(loadUsers());Effect Tracking
效果追踪
Effects are automatically tracked when :
trackEffects: truetypescript
// user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import * as UserActions from './user.actions';
import { UserService } from '../services/user.service';
@Injectable()
export class UserEffects {
// This effect will be tracked by NgRx DevTool
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({
error: error.message
})))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}当 时,效果会被自动追踪:
trackEffects: truetypescript
// user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import * as UserActions from './user.actions';
import { UserService } from '../services/user.service';
@Injectable()
export class UserEffects {
// 该效果会被NgRx DevTool追踪
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({
error: error.message
})))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}State Visualization
状态可视化
The DevTool automatically captures state before and after each action, showing diffs:
typescript
// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as CounterActions from './counter.actions';
export interface CounterState {
count: number;
lastUpdated: Date | null;
}
export const initialState: CounterState = {
count: 0,
lastUpdated: null
};
export const counterReducer = createReducer(
initialState,
on(CounterActions.increment, state => ({
...state,
count: state.count + 1,
lastUpdated: new Date()
})),
on(CounterActions.decrement, state => ({
...state,
count: state.count - 1,
lastUpdated: new Date()
})),
on(CounterActions.reset, () => initialState)
);DevTool会自动捕获每个动作前后的状态,并展示差异:
typescript
// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as CounterActions from './counter.actions';
export interface CounterState {
count: number;
lastUpdated: Date | null;
}
export const initialState: CounterState = {
count: 0,
lastUpdated: null
};
export const counterReducer = createReducer(
initialState,
on(CounterActions.increment, state => ({
...state,
count: state.count + 1,
lastUpdated: new Date()
})),
on(CounterActions.decrement, state => ({
...state,
count: state.count - 1,
lastUpdated: new Date()
})),
on(CounterActions.reset, () => initialState)
);Custom Action Metadata
自定义动作元数据
Add metadata to actions for better debugging:
typescript
// product.actions.ts
import { createAction, props } from '@ngrx/store';
export const addToCart = createAction(
'[Product] Add to Cart',
props<{
productId: string;
quantity: number;
metadata?: { source: string; timestamp: number }
}>()
);
// Dispatch with metadata
this.store.dispatch(addToCart({
productId: 'prod-123',
quantity: 2,
metadata: {
source: 'product-detail-page',
timestamp: Date.now()
}
}));为动作添加元数据以优化调试体验:
typescript
// product.actions.ts
import { createAction, props } from '@ngrx/store';
export const addToCart = createAction(
'[Product] Add to Cart',
props<{
productId: string;
quantity: number;
metadata?: { source: string; timestamp: number }
}>()
);
// 携带元数据派发动作
this.store.dispatch(addToCart({
productId: 'prod-123',
quantity: 2,
metadata: {
source: 'product-detail-page',
timestamp: Date.now()
}
}));Advanced Configuration
高级配置
Selective Action Tracking
选择性动作追踪
Filter which actions to track by customizing the meta-reducer:
typescript
// app.config.ts
import { ActionReducer, Action } from '@ngrx/store';
import { createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
function createFilteredDevToolMetaReducer() {
const devToolMetaReducer = createDevToolMetaReducer();
return (reducer: ActionReducer<any>) => {
const wrappedReducer = devToolMetaReducer(reducer);
return (state: any, action: Action) => {
// Skip tracking for high-frequency actions
if (action.type.includes('[Router]') ||
action.type.includes('[Internal]')) {
return reducer(state, action);
}
return wrappedReducer(state, action);
};
};
}
export const appConfig: ApplicationConfig = {
providers: [
provideStore(reducers, {
metaReducers: [createFilteredDevToolMetaReducer()]
}),
provideNgrxDevTool()
]
};通过自定义元 reducer 筛选需要追踪的动作:
typescript
// app.config.ts
import { ActionReducer, Action } from '@ngrx/store';
import { createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
function createFilteredDevToolMetaReducer() {
const devToolMetaReducer = createDevToolMetaReducer();
return (reducer: ActionReducer<any>) => {
const wrappedReducer = devToolMetaReducer(reducer);
return (state: any, action: Action) => {
// 跳过高频动作的追踪
if (action.type.includes('[Router]') ||
action.type.includes('[Internal]')) {
return reducer(state, action);
}
return wrappedReducer(state, action);
};
};
}
export const appConfig: ApplicationConfig = {
providers: [
provideStore(reducers, {
metaReducers: [createFilteredDevToolMetaReducer()]
}),
provideNgrxDevTool()
]
};Multiple Environment Setup
多环境配置
typescript
// app.config.ts
const devToolConfig = (() => {
const hostname = window.location.hostname;
if (hostname === 'localhost') {
return { wsUrl: 'ws://localhost:4000', enabled: true };
} else if (hostname.includes('staging')) {
return { wsUrl: 'ws://staging-devtool.example.com:4000', enabled: true };
}
return { enabled: false };
})();
export const appConfig: ApplicationConfig = {
providers: [
provideNgrxDevTool(devToolConfig)
]
};typescript
// app.config.ts
const devToolConfig = (() => {
const hostname = window.location.hostname;
if (hostname === 'localhost') {
return { wsUrl: 'ws://localhost:4000', enabled: true };
} else if (hostname.includes('staging')) {
return { wsUrl: 'ws://staging-devtool.example.com:4000', enabled: true };
}
return { enabled: false };
})();
export const appConfig: ApplicationConfig = {
providers: [
provideNgrxDevTool(devToolConfig)
]
};Troubleshooting
故障排除
Connection Issues
连接问题
Problem: DevTool UI shows "Disconnected" status
Solutions:
- Verify the DevTool server is running:
bash
npx ngrx-devtool- Check WebSocket URL matches server configuration:
typescript
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000' // Must match server port
})- Check browser console for connection errors:
WebSocket connection to 'ws://localhost:4000' failed: Connection refused- Verify no firewall blocking WebSocket connections
问题:DevTool UI显示"已断开连接"状态
解决方案:
- 确认DevTool服务器正在运行:
bash
npx ngrx-devtool- 检查WebSocket URL与服务器配置匹配:
typescript
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000' // 必须与服务器端口匹配
})- 检查浏览器控制台的连接错误:
WebSocket connection to 'ws://localhost:4000' failed: Connection refused- 确认没有防火墙阻止WebSocket连接
Actions Not Appearing
动作未显示
Problem: Actions are dispatched but not showing in DevTool
Solutions:
- Ensure meta-reducer is registered:
typescript
provideStore(reducers, {
metaReducers: [createDevToolMetaReducer()] // Must be present
})- Verify DevTool is enabled:
typescript
provideNgrxDevTool({
enabled: true // Check this is true
})- Check for action filtering that might be excluding actions
问题:动作已派发但未在DevTool中显示
解决方案:
- 确保已注册元 reducer:
typescript
provideStore(reducers, {
metaReducers: [createDevToolMetaReducer()] // 必须存在
})- 确认DevTool已启用:
typescript
provideNgrxDevTool({
enabled: true // 检查该值是否为true
})- 检查是否存在动作筛选规则导致动作被排除
Effects Not Tracked
效果未被追踪
Problem: Effects execute but don't appear in DevTool
Solutions:
- Enable effect tracking:
typescript
provideNgrxDevTool({
trackEffects: true // Must be true
})- Ensure effects are properly registered:
typescript
provideEffects([UserEffects, ProductEffects])- Verify effects use the function
createEffect()
问题:效果已执行但未在DevTool中显示
解决方案:
- 启用效果追踪:
typescript
provideNgrxDevTool({
trackEffects: true // 必须为true
})- 确保效果已正确注册:
typescript
provideEffects([UserEffects, ProductEffects])- 确认效果使用了 函数
createEffect()
Performance Issues
性能问题
Problem: Application slows down with DevTool enabled
Solutions:
- Reduce action history limit:
typescript
provideNgrxDevTool({
maxActions: 50 // Default is 100
})- Filter high-frequency actions:
typescript
// Use custom meta-reducer to skip router/polling actions- Disable in production builds:
typescript
provideNgrxDevTool({
enabled: !environment.production
})问题:启用DevTool后应用运行变慢
解决方案:
- 减少动作历史记录上限:
typescript
provideNgrxDevTool({
maxActions: 50 // 默认值为100
})- 筛选高频动作:
typescript
// 使用自定义元 reducer 跳过路由/轮询动作- 在生产构建中禁用:
typescript
provideNgrxDevTool({
enabled: !environment.production
})Port Conflicts
端口冲突
Problem: Port 4000 or 3000 already in use
Solutions:
- Use custom ports:
bash
npx ngrx-devtool --ws-port 5000 --ui-port 8080- Update configuration to match:
typescript
provideNgrxDevTool({
wsUrl: 'ws://localhost:5000'
})问题:端口4000或3000已被占用
解决方案:
- 使用自定义端口:
bash
npx ngrx-devtool --ws-port 5000 --ui-port 8080- 更新配置以匹配:
typescript
provideNgrxDevTool({
wsUrl: 'ws://localhost:5000'
})State Diff Not Showing
状态差异未显示
Problem: State changes occur but diff viewer is empty
Solutions:
- Ensure reducer returns new state reference:
typescript
// ❌ Bad - mutates state
on(updateUser, (state, { user }) => {
state.user = user; // Don't do this
return state;
})
// ✅ Good - returns new state
on(updateUser, (state, { user }) => ({
...state,
user
}))- Check state serialization for circular references
问题:状态已变更但差异查看器为空
解决方案:
- 确保reducer返回新的状态引用:
typescript
// ❌ 错误 - 直接修改状态
on(updateUser, (state, { user }) => {
state.user = user; // 请勿这样做
return state;
})
// ✅ 正确 - 返回新状态
on(updateUser, (state, { user }) => ({
...state,
user
}))- 检查状态序列化是否存在循环引用
Browser Compatibility
浏览器兼容性
The DevTool requires WebSocket support. All modern browsers support WebSockets, but if you encounter issues:
- Ensure browser is up to date
- Check corporate proxy/firewall settings
- Try different browser to isolate issue
- Check browser console for specific WebSocket errors
DevTool需要WebSocket支持。所有现代浏览器均支持WebSocket,但如果遇到问题:
- 确保浏览器已更新至最新版本
- 检查企业代理/防火墙设置
- 尝试使用其他浏览器以排查问题
- 检查浏览器控制台中的具体WebSocket错误