parents: {
diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less
index 0f34f9cd..a95f6986 100644
--- a/libs/extension/src/components/VTree.less
+++ b/libs/extension/src/components/VTree.less
@@ -6,7 +6,7 @@
.treeItem {
width: 100%;
position: absolute;
- line-height: 18px;
+ line-height: 1.125rem;
&:hover {
background-color: @select-color;
diff --git a/libs/extension/src/injector/index.ts b/libs/extension/src/injector/index.ts
index 0d1ad023..e0372d10 100644
--- a/libs/extension/src/injector/index.ts
+++ b/libs/extension/src/injector/index.ts
@@ -10,7 +10,8 @@ import {
} from './../utils/constants';
import { VNode } from './../../../horizon/src/renderer/vnode/VNode';
import { ClassComponent } from '../../../horizon/src/renderer/vnode/VNodeTags';
-import { parseAttr } from '../parser/parseAttr';
+import { parseAttr, parseHooks } from '../parser/parseAttr';
+import { FunctionComponent } from './../../../horizon/src/renderer/vnode/VNodeTags';
const roots = [];
@@ -56,6 +57,14 @@ function parseCompAttrs(id: number) {
parsedProps,
parsedState,
});
+ } else if (tag === FunctionComponent) {
+ const { props, hooks } = vNode;
+ const parsedProps = parseAttr(props);
+ const parsedHooks = parseHooks(hooks);
+ postMessage(ComponentAttrs, {
+ parsedProps,
+ parsedHooks,
+ });
}
}
diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx
index a0455831..af6e5d9a 100644
--- a/libs/extension/src/panel/App.tsx
+++ b/libs/extension/src/panel/App.tsx
@@ -67,6 +67,8 @@ if (!isDev) {
});
}
+let reconnectTimes = 0;
+
function postMessage(type: string, data: any) {
try {
connection.postMessage(packagePayload({
@@ -75,14 +77,21 @@ function postMessage(type: string, data: any) {
}, DevToolPanel));
} catch(err) {
// 可能出现 port 关闭的场景,需要重新建立连接,增加可靠性
+ if (reconnectTimes === 20) {
+ reconnectTimes = 0;
+ console.error('reconnect failed');
+ return;
+ }
console.error(err);
+ reconnectTimes++;
+ // 重建连接
connection = chrome.runtime.connect({
name: 'panel'
});
- connection.postMessage(packagePayload({
- type: type,
- data: data,
- }, DevToolPanel));
+ // 重新发送初始化消息
+ postMessage(InitDevToolPageConnection, chrome.devtools.inspectedWindow.tabId);
+ // 初始化成功后才会重新发送消息
+ postMessage(type, data);
}
}
@@ -134,10 +143,11 @@ function App() {
}, []);
setParsedVNodeData(allTreeData);
} else if (type === ComponentAttrs) {
- const {parsedProps, parsedState} = data;
+ const {parsedProps, parsedState, parsedHooks} = data;
setComponentAttrs({
- state: parsedProps,
- props: parsedState,
+ props: parsedProps,
+ state: parsedState,
+ hooks: parsedHooks,
});
}
}
diff --git a/libs/extension/src/parser/parseAttr.ts b/libs/extension/src/parser/parseAttr.ts
index f710141e..e18f9bad 100644
--- a/libs/extension/src/parser/parseAttr.ts
+++ b/libs/extension/src/parser/parseAttr.ts
@@ -1,79 +1,133 @@
-import { IAttr } from "../components/ComponentInfo";
-// 将状态的值解析成固定格式
+import { Hook, Reducer, Ref } from './../../../horizon/src/renderer/hooks/HookType';
+
+// 展示值为 string 的可编辑类型
+type editableStringType = 'string' | 'number' | 'undefined' | 'null';
+// 展示值为 string 的不可编辑类型
+type unEditableStringType = 'function' | 'symbol' | 'object' | 'map' | 'set' | 'array'
+ | 'dom' // 值为 dom 元素的 ref 类型
+ | 'ref'; // 值为其他数据的 ref 类型
+
+type showAsStringType = editableStringType | unEditableStringType;
+
+
+export type IAttr = {
+ name: string;
+ indentation: number;
+ hIndex?: number; // 用于记录 hook 的 hIndex 值
+} & ({
+ type: showAsStringType;
+ value: string;
+} | {
+ type: 'boolean';
+ value: boolean;
+})
+
+type showType = showAsStringType | 'boolean';
+
+const parseSubAttr = (
+ attr: any,
+ parentIndentation: number,
+ attrName: string,
+ result: IAttr[],
+ hIndex?: number) => {
+ const attrType = typeof attr;
+ let value: any;
+ let showType: showType;
+ let addSubState;
+ if (attrType === 'boolean' ||
+ attrType === 'number' ||
+ attrType === 'string' ||
+ attrType === 'undefined') {
+ value = attr;
+ showType = attrType;
+ } else if (attrType === 'function') {
+ const funName = attr.name;
+ value = `f() ${funName}{}`;
+ } else if (attrType === 'symbol') {
+ value = attr.description;
+ } else if (attrType === 'object') {
+ if (attr === null) {
+ showType = 'null';
+ } else if (attr instanceof Map) {
+ showType = 'map';
+ const size = attr.size;
+ value = `Map(${size})`;
+ addSubState = () => {
+ attr.forEach((value, key) => {
+ parseSubAttr(value, parentIndentation + 2, key, result);
+ });
+ };
+ } else if (attr instanceof Set) {
+ showType = 'set';
+ const size = attr.size;
+ value = `Set(${size})`;
+ addSubState = () => {
+ let i = 0;
+ attr.forEach((value) => {
+ parseSubAttr(value, parentIndentation + 2, String(i), result);
+ });
+ i++;
+ };
+ } else if (Array.isArray(attr)) {
+ showType = 'array';
+ value = `Array(${attr.length})`;
+ addSubState = () => {
+ attr.forEach((value, index) => {
+ parseSubAttr(value, parentIndentation + 2, String(index), result);
+ });
+ };
+ } else if (attr instanceof Element) {
+ showType = 'dom';
+ value = attr.tagName;
+ } else {
+ showType = attrType;
+ value = '{...}';
+ addSubState = () => {
+ Object.keys(attr).forEach((key) => {
+ parseSubAttr(attr[key], parentIndentation + 2, key, result);
+ });
+ };
+ }
+ }
+ const item: IAttr = {
+ name: attrName,
+ type: showType,
+ value,
+ indentation: parentIndentation + 1,
+ };
+ if (hIndex) {
+ item.hIndex = hIndex;
+ }
+ result.push(item);
+ if (addSubState) {
+ addSubState();
+ }
+};
+
+// 将属性的值解析成固定格式,props 和 类组件的 state 必须是一个对象
export function parseAttr(rootAttr: any) {
const result: IAttr[] = [];
const indentation = 0;
- const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => {
- const stateType = typeof attr;
- let value: any;
- let showType;
- let addSubState;
- if (stateType === 'boolean' ||
- stateType === 'number' ||
- stateType === 'string' ||
- stateType === 'undefined') {
- value = attr;
- showType = stateType;
- } else if (stateType === 'function') {
- const funName = attr.name;
- value = `f() ${funName}{}`;
- } else if (stateType === 'symbol') {
- value = attr.description;
- } else if (stateType === 'object') {
- if (attr === null) {
- showType = 'null';
- } else if (attr instanceof Map) {
- showType = 'map';
- const size = attr.size;
- value = `Map(${size})`;
- addSubState = () => {
- attr.forEach((value, key) => {
- parseSubAttr(value, parentIndentation + 2, key);
- });
- };
- } else if (attr instanceof Set) {
- showType = 'set';
- const size = attr.size;
- value = `Set(${size})`;
- addSubState = () => {
- let i = 0;
- attr.forEach((value) => {
- parseSubAttr(value, parentIndentation + 2, String(i));
- });
- i++;
- };
- } else if (Array.isArray(attr)) {
- showType = 'array';
- value = `Array(${attr.length})`;
- addSubState = () => {
- attr.forEach((value, index) => {
- parseSubAttr(value, parentIndentation + 2, String(index));
- });
- };
- } else {
- showType = stateType;
- value = '{...}';
- addSubState = () => {
- Object.keys(attr).forEach((key) => {
- parseSubAttr(attr[key], parentIndentation + 2, key);
- });
- };
- }
- }
-
- result.push({
- name: attrName,
- type: showType,
- value,
- indentation: parentIndentation + 1,
- });
- if (addSubState) {
- addSubState();
- }
- };
+ if (typeof rootAttr === 'object' && rootAttr !== null)
Object.keys(rootAttr).forEach(key => {
- parseSubAttr(rootAttr[key], indentation, key);
+ parseSubAttr(rootAttr[key], indentation, key, result);
+ });
+ return result;
+}
+
+export function parseHooks(hooks: Hook
[]) {
+ const result: IAttr[] = [];
+ const indentation = 0;
+ hooks.forEach(hook => {
+ const { hIndex, state ,type } = hook;
+ if (type === 'useState') {
+ parseSubAttr((state as Reducer).stateValue, indentation, 'state', result, hIndex);
+ } else if (type === 'useRef') {
+ parseSubAttr((state as Ref).current, indentation, 'ref', result, hIndex);
+ } else if (type === 'useReducer') {
+ parseSubAttr((state as Reducer).stateValue, indentation, 'reducer', result, hIndex);
+ }
});
return result;
}
diff --git a/libs/horizon/src/renderer/hooks/HookType.ts b/libs/horizon/src/renderer/hooks/HookType.ts
index e965fdf1..cb8be892 100644
--- a/libs/horizon/src/renderer/hooks/HookType.ts
+++ b/libs/horizon/src/renderer/hooks/HookType.ts
@@ -3,6 +3,7 @@ import {EffectConstant} from './EffectConstant';
export interface Hook {
state: Reducer | Effect | Memo | CallBack | Ref;
hIndex: number;
+ type?: 'useState' | 'useRef' | 'useReducer';
}
export interface Reducer {
diff --git a/libs/horizon/src/renderer/hooks/UseReducerHook.ts b/libs/horizon/src/renderer/hooks/UseReducerHook.ts
index 52399713..480f43bb 100644
--- a/libs/horizon/src/renderer/hooks/UseReducerHook.ts
+++ b/libs/horizon/src/renderer/hooks/UseReducerHook.ts
@@ -87,6 +87,7 @@ export function useReducerForInit(reducer, initArg, init, isUseState?: boo
}
const hook = createHook();
+ hook.type = isUseState ? 'useState' : 'useReducer';
// 为hook.state赋值{状态值, 触发函数, reducer, updates更新数组, 是否是useState}
hook.state = {
stateValue: stateValue,
diff --git a/libs/horizon/src/renderer/hooks/UseRefHook.ts b/libs/horizon/src/renderer/hooks/UseRefHook.ts
index 754a16d2..381ef61e 100644
--- a/libs/horizon/src/renderer/hooks/UseRefHook.ts
+++ b/libs/horizon/src/renderer/hooks/UseRefHook.ts
@@ -12,6 +12,7 @@ export function useRefImpl(value: V): Ref {
if (stage === HookStage.Init) {
hook = createHook();
hook.state = {current: value};
+ hook.type = 'useRef';
} else if (stage === HookStage.Update) {
hook = getCurrentHook();
}