fl427's Studio.

前端调试 - VSCode断点与Chrome Devtool使用指南

字数统计: 1.2k阅读时长: 5 min
2023/01/21

靠一个console.log走天下的时间终归是有限的,这篇文章总结了一下对前端开发有益的调试技巧,由三部分组成:第一部分介绍断点调试技巧,第二部介绍Chrome DevTool使用,第三部分简要总结了console系列。

  1. 开始进行断点
  2. 一步步查看当前的内容,(不需要把内容全部贴上来,贴一部分就行)

前端工程断点调试技巧

断点操作的基本使用

VSCode断点调试MobX源码

MobX是。。。

我们用MobX进行VSCode调试实操,目标是源码层解释官网示例。我自己写这篇文章的时候是第一次接触MobX,因此如果内容中有不正确或者不严谨的地方希望大家能帮忙指出来。

MobX是个相对复杂的项目,尤其是对于第一次接触的人来说。想要直接嗯看源码的话,不太可能理顺其中的运行逻辑,这种时候断点调试是一个有效的手段。

所涉及代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// store/post-editor.ts
import { makeAutoObservable } from "mobx";

export class PostEditor {
currentStage = 0;

constructor() {
makeAutoObservable(this);
}

increase() {
this.currentStage += 1;
}

reset() {
this.currentStage = 0;
}
}

// store/index.ts
import { PostEditor } from './post-editor';

export const RootStore = {
postEditor: new PostEditor()
}

// useStore.ts
import { useContext } from 'react';
import { RootStore } from "@src/store";
import { MobXProviderContext } from 'mobx-react';

// 根据RootStore来实现参数的自动获取和返回值的自动推导
const useStore = <T extends typeof RootStore, V extends keyof T>(name: V): T[V] => {
const store = useContext(MobXProviderContext) as T;
return store[name];
}

export default useStore;


// upload.tsx
import React, { useCallback, useEffect, useRef, useState } from 'react';
import useStore from "@src/hooks/useStore";
import { observer } from "mobx-react";
const Upload: React.FC = () => {

const postEditor = useStore('postEditor');

const handleIncrease = (): void => {
postEditor.increase();
}

return (
<div className={styles.upload}>
<div className={styles.onlyTest}>
<div onClick={handleIncrease}>
Increase, {postEditor.currentStage}
</div>
</div>
</div>
)
};

export default observer(Upload);

MobX提供的makeAutoObservable在数据类的构造函数中被调用,observer包裹组件,达到的效果就是,任何地方修改了PostEditor实例的currentStage属性,组价函数都会rerender完成更新。

在调试源码之前,我们先看一些基础知识

  • MobX基本介绍

设置VSCode调试配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
```

![截屏2023-01-21 22.52.12](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.52.12.png)



比方说我们想看下increase是这么实现的,

在`handleIncrease`的地方添加断点,代码会在执行到这里的时候断住:

![截屏2023-01-21 22.53.09](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.53.09.png)

我们可以看到调用堆栈:

![截屏2023-01-21 22.55.25](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.55.25.png)

选择第三个选项进入函数内部调试:

![截屏2023-01-21 22.56.27](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.56.27.png)

发现对应执行executeAction

![截屏2023-01-21 22.57.19](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.57.19.png)

代表执行传入函数`fn`

![截屏2023-01-21 22.59.03](https://fl427-pics.oss-cn-hangzhou.aliyuncs.com/uPic/%E6%88%AA%E5%B1%8F2023-01-21%2022.59.03.png)

而fn是来自于createAction,链路在这里断掉了,我们需要将断点提前来看完整的链路

```javascript
export function createAction(
actionName: string,
fn: Function,
autoAction: boolean = false,
ref?: Object
): Function {
function res() {
return executeAction(actionName, autoAction, fn, ref || this, arguments)
}
return res
}

截屏2023-01-21 23.05.47

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export class PostEditor {
currentStage = 0;

constructor() {
makeAutoObservable(this);
}

increase() {
this.currentStage += 1;
}

reset() {
this.currentStage = 0;
}
}

进入makeAutoObservable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
export function makeAutoObservable<T extends object, AdditionalKeys extends PropertyKey = never>(
target: T,
overrides?: AnnotationsMap<T, NoInfer<AdditionalKeys>>,
options?: CreateObservableOptions
): T {
// 此时当我们遇到这里明显不需要进入内部的场景,我们点击第二个选项
if (__DEV__) {
if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target))) {
die(`'makeAutoObservable' can only be used for classes that don't have a superclass`)
}
if (isObservableObject(target)) {
die(`makeAutoObservable can only be used on objects not already made observable`)
}
}

// Optimization: avoid visiting protos
// Assumes that annotation.make_/.extend_ works the same for plain objects
if (isPlainObject(target)) {
return extendObservable(target, target, overrides, options)
}

const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]

// Optimization: cache keys on proto
// Assumes makeAutoObservable can be called only once per object and can't be used in subclass
if (!target[keysSymbol]) {
const proto = Object.getPrototypeOf(target)
const keys = new Set([...ownKeys(target), ...ownKeys(proto)])
keys.delete("constructor")
keys.delete($mobx)
addHiddenProp(proto, keysSymbol, keys)
}

startBatch()
try {
target[keysSymbol].forEach(key =>
adm.make_(
key,
// must pass "undefined" for { key: undefined }
!overrides ? true : key in overrides ? overrides[key] : true
)
)
} finally {
endBatch()
}
return target
}
CATALOG
  1. 1. 前端工程断点调试技巧
    1. 1.1. 断点操作的基本使用
    2. 1.2. VSCode断点调试MobX源码