《redux实战》第3章:调试redux应用程序(redux devTools的应用)

1,118 阅读9分钟

本章学习内容已规整到对应专栏

在debugger redux的问题时,如果想快速排查问题,提高开发效率,可以为你的项目引入redux devTools。

1、redux devTools介绍

redux devTools可简称devTools,它能够增强开发环境,实现实时可视化action,就以本专栏第2章的练习项目为例,介绍一下redux devTools的工作方式:

redux-devTools右侧面板介绍.png 前置说明: 上图左侧是我们根据第2章学到的知识构建的一个简单的任务列表练习项目(项目代码见:chapter_two)。

  • 任务状态。每项任务分为3个状态,分别是:Unstarted、In Progress、Completed;
  • 创建任务。该任务列表可以通过右上角的"+ New task"来添加一个新任务(添加新任务会派发CREATE_TASK action);
  • 修改任务状态。如果要改变任务的状态,则可以通过每个task item右上角的status dropdown来切换任务的不同状态(切换任务状态会派发CHANGE_STATE action)。

关于右侧面板说明:

在右侧面板中,我们可以看到3个高亮显示的列表,分别是:

  • @@INT,这个action代表的是redux初始化的操作,我们可以看到初始化的行为是没有action的,只有一个init state;
  • CREATE_TASK,当我们点击右上角的"+ New task"创建任务时,会触发交互事件,然后派发CREATE_TASK action,可以看到在这个列表中是包含了派发的action,以及更新后的state,更新后的state,有变化的部分,背景颜色也是高亮显示。
  • CHANGE_STATE,同理,当我们更改一个task的status时,就会触发交互事件,然后派发CHANGE_STATE,整个流程跟CREATE_TASK是一致的,只是不同的action所引起state的变化不同。

通过这个action列表,我们可以准确地知道user在应用程序中的交互行为,而不必观察用户的操作。观察上面的action列表,我们可以知道应用程序的整个交互过程:应用程序被初始化 => 创建第三个任务 => 编辑第二个任务的状态。

如此,我们可以感受到在派发每个新action时,都可以很方便地获得及时反馈,并且可以确保每个action的payload和预期是一致的。

除了action的跟踪显示之外,每个action下面都会有一份redux store的快照,通过这个快照,可以查看action产生的应用程序state。

2、时间旅行调试

在redux devTools中,还有更多的细节等我们发掘。如:点击action的标题可以切换关闭action,即使action依然展示在action列表中,也会重新计算应用程序的状态,就好像action从未派发一样,再次点击action的标题,应用程序将回到之前的状态。如下图:

时间旅行调试.png

时间旅行调试这个名称的灵感正是源于此 => action的倒回和重放。为确定在未创建第三个任务时,应用程序会是什么样子,不必刷新并重新创建状态,而是可以通过禁用action及时切换回原始状态。

此外,redux devTools还提供了一个滑块用户界面,以支持向后和向前滚动action,以及查看action对应用程序的直接影响,还可以自动播放用户在当前页面的整个交互过程,见下图:

时间旅行调试-滑块用户界面.gif

注意:使用时间旅行调试不需要额外的配置或以来,时间旅行调试只是redux devTools的功能之一。

3、redux devTools监视器的模式

  • inspector monitor

如图所示,每个action的显示方式主要以细分的action、state以及diff来显示。顾名思义,action只会现实action type以及action payload,state则显示派发本次action后引起的state变化,diff则会显示派发action之前以及派发之后state的对比。 image.png

  • log monitor

如图所示,action的显示方式将以包含action item以及state item的方式来显示。 image.png

  • chart monitor 如图所示,该模式只会显示state的树形结构图,不会显示action,并且当鼠标hover在对应的state上时,会显示该state的详细信息,state还可以点击的,当点击时,会直接跳到对应的action。 image.png

4、实现redux devTools

在使用redux devTools之前需要安装对应的浏览器扩展程序,然后将redux devTools连接到store。在这里,让我们以chrome浏览器为例:

  • 安装chrome浏览器扩展程序。访问chrome网上商店,搜索redux devTools,然后安装搜索到的第一个软件包。 image.png
  • 将redux devTools添加到store。
// 安装包并将其保存到开发依赖项:
npm install -D redux-devtools-extension

// 安装完依赖后,可以从依赖中导入devToolsEnhancer函数,并将其传递给store。
// redux的createStore函数最多可以接收三个参数:reducer、init state、enhancer,当只传递两个参数时,redux默认假定第二个参数是enhancer,而不是init state。
import { devToolsEnhancer } from 'redux-devtools-extension'

在完成上面两步操作之后,打开浏览器开发者工具,就可以看到redux的面板了,如下图所示:

image.png

从上图中可以看到,在进入redux devTools后,默认看到的是inspector monitor,我们可以通过最下边中间部分下划线的停留位置来看出当前是处于哪个monitor(如下图)。

image.png

当点击inspector monitor中某个action的Skip按钮时,可以关闭该action。注意:该action已经从用户界面中remove了,再次点击action的Skip按钮可以将action reopen。让我们创建两个新的task,触发CREATE_TASK action,然后看看下面最终呈现的效果:

redux_devTools的Skip.gif

当选定某个action时,inspector monitor的另一半将显示该action或redux store的相关数据,数据的具体显示情况取决于选择的筛选器类型。Diff筛选器尤其有助于可视化显示action对store的影响,可以通过点击底部中间的菜单来切换inspector monitor、log monitor、chart monitor(如下图):

redux_devTools切换monitor.gif

在靠近面板底部的地方还有一个带有秒表图标的按钮,点击这个图标可以打开滑块检查器并进行相应的操作(如下图):

redux_devTools滑块monitor.gif

5、关于redux devTools的文档解读

浏览器扩展的安装和配置

1)安装

chrome:

  • chrome应用商城中安装;
  • 在最新的版本中下载extension.zip,解压,然后打开chrome://extensions ,在左上角点击菜单图标打开开发者模式 => 点击扩展程序 => 加载已解压的扩展程序 => 选择你刚刚解压出来的extension文件夹;
  • 用npm i && npm run build:extension,然后 加载扩展文件夹 ./build/extension;
  • 在dev模式下运行npm i && npm start,并 加载扩展文件夹 ./dev。

firefox:

electron: 只需在electron-devtools-installer中指定REDUX_DEVTOOLS。

其他浏览器和非浏览器环境: 使用 remote-redux-devtools

2)安装使用

注意: 从2.7版本开始,window.devToolsExtension被命名为 window.__REDUX_DEVTOOLS_EXTENSION__ 或 window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__

2.1)Basic store

对于基础的store,只需添加如下代码:

// 2.7版本之前
import { devToolsEnhancer } from 'redux-devtools-extension'
const store = createStore(
    reducer, 
    /* preloadedState, */
    devToolsEnhancer()
);

// 2.7版本及之后
import { __REDUX_DEVTOOLS_EXTENSION__ } from 'redux-devtools-extension'
const store = createStore(
   reducer, 
   /* preloadedState, */
   __REDUX_DEVTOOLS_EXTENSION__ && __REDUX_DEVTOOLS_EXTENSION__()
);

注意: preloadedState在createStore里面是可选的,如果它不存在,则默认第二个参数是enhancer。

对于通用("isomorphic") apps,则要在它前面加上typeof window !== 'undefined' &&。

const composeEnhancers = (
    typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) || compose;

对于TypeScript,使用redux-devtools-extension npm package,它包含所有的定义,或者直接使用(window as any)。

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

如果ESLint被配置为不允许使用下划线,可以使用下面的语句这样包裹代码:

/* eslint-disable no-underscore-dangle */
const store = createStore(
    reducer, /* preloadedState, */
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
/* eslint-enable */

注意: 将enhancer作为最后一个参数传递需要redux@>=3.1.0。对于较老的版本,可以在 herehere 看到相关的代码应用。不要把旧的Redux API和新的混在一起。

当使用extension(这是一个不同的库)时,你不需要npm安装redux-devtools。

2.2)Advanced store setup

如果你用middleware和enhancer,设置你的store,你要做如下更改:

import { createStore, applyMiddleware, compose } from 'redux';

// 注意,当extension未安装时,我们在这里使用Redux compose。
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
    reducer,
    /* preloadedState, */
    composeEnhancers(applyMiddleware(...middleware))
);

如果要指定extension's的options,可以这样使用它:

const composeEnhancers =
    typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
        ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
            // Specify extension’s options like name, actionsDenylist, actionsCreators, serialize...
          })
        : compose;

const enhancer = composeEnhancers(
    applyMiddleware(...middleware)
    // other store enhancers if any
);
const store = createStore(reducer, enhancer);

更多细节请参见文章

2.3)使用npm的 @redux-devtools/extension 包

先安装 @redux-devtools/extension 包

npm install --save @redux-devtools/extension

然后像下面这样导入使用

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from '@redux-devtools/extension';

const store = createStore(
    reducer,
    composeWithDevTools(
        applyMiddleware(...middleware)
        // other store enhancers if any
    )
);

如果要指定extension's的options

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from '@redux-devtools/extension';

const composeEnhancers = composeWithDevTools({
    // Specify name here, actionsDenylist, actionsCreators and other options if needed
});
const store = createStore(
    reducer,
    /* preloadedState, */ 
    composeEnhancers(
        applyMiddleware(...middleware)
        // other store enhancers if any
    )
);

关于 composeWithDevTools,只有几行代码添加到您的bundle中。

如果你没有包含其他enhancer和middleware,只需要导入使用devToolsEnhancer:

import { createStore } from 'redux';
import { devToolsEnhancer } from '@redux-devtools/extension';

const store = createStore(
    reducer,
    /* preloadedState, */
    devToolsEnhancer()
    // Specify name here, actionsDenylist, actionsCreators and other options if needed
);

2.4)在production中使用 在production中包含extension也很有用。但通常情况下,是将其用于开发,具体详情点这里。如果你想限制它,使用 composeWithDevToolsLogOnlyInProduction 或 devToolsEnhancerLogOnlyInProduction

import { createStore } from 'redux';
import { devToolsEnhancerLogOnlyInProduction } from '@redux-devtools/extension';

const store = createStore(
    reducer,
    /* preloadedState, */ 
    devToolsEnhancerLogOnlyInProduction()
    // options like actionSanitizer, stateSanitizer
);

或者使用middlewares和enhancers:

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension';

const composeEnhancers = composeWithDevToolsLogOnlyInProduction({
    // options like actionSanitizer, stateSanitizer
});
const store = createStore(
    reducer,
    /* preloadedState, */ 
    composeEnhancers(
        applyMiddleware(...middleware)
        // other store enhancers if any
    )
);

你必须添加'process.env.NODE_ENV': JSON.stringify('production')在您production bundle的Webpack配置中(看这部分)。如果你用create-react-app,它已经帮你做了。

如果你在creating store时已经检查 process.env.NODE_ENV,为生产环境导入composeWithDevToolsLogOnly或devToolsEnhancerLogOnly。

如果你不想在production中允许extension,只需使用composeWithDevToolsDevelopmentOnly或devToolsEnhancerDevelopmentOnly。

这部分了解更多详情。

2.5)适用于React native、hybrid、desktop和server side Redux app

对于React Native,我们可以使用react-native-debugger,它已经包含了与Redux DevTools Extension相同的API
对于大多数平台,包括Remote Redux DevTools的store enhancer,并从扩展的上下文菜单中选择“打开远程DevTools”进行远程监控。