From 58d5869a581b282fac5e69fca509b3882015b145 Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 25 Apr 2022 19:55:21 +0800 Subject: [PATCH] Match-id-e56b18c31b44e4091ce6cf98af6502f369a02364 --- libs/extension/src/background/index.ts | 2 +- .../src/components/ComponentInfo.tsx | 75 +++++++++++----- libs/extension/src/injector/index.ts | 30 ++----- libs/extension/src/panel/App.tsx | 85 +++++++------------ libs/extension/src/parser/parseAttr.ts | 65 +++++++++++++- libs/extension/src/utils/constants.ts | 10 ++- 6 files changed, 165 insertions(+), 102 deletions(-) diff --git a/libs/extension/src/background/index.ts b/libs/extension/src/background/index.ts index ed9698fb..cc8bd778 100644 --- a/libs/extension/src/background/index.ts +++ b/libs/extension/src/background/index.ts @@ -1,6 +1,6 @@ import { checkMessage, packagePayload, changeSource } from '../utils/transferTool'; import { RequestAllVNodeTreeInfos, InitDevToolPageConnection, DevToolBackground } from '../utils/constants'; -import { DevToolPanel, DevToolContentScript } from './../utils/constants'; +import { DevToolPanel, DevToolContentScript } from '../utils/constants'; // 多个页面、tab页共享一个 background,需要建立连接池,给每个tab建立连接 const connections = {}; diff --git a/libs/extension/src/components/ComponentInfo.tsx b/libs/extension/src/components/ComponentInfo.tsx index f5ff0051..328642bd 100644 --- a/libs/extension/src/components/ComponentInfo.tsx +++ b/libs/extension/src/components/ComponentInfo.tsx @@ -5,17 +5,19 @@ import Copy from '../svgs/Copy'; import Triangle from '../svgs/Triangle'; import { useState, useEffect } from 'horizon'; import { IData } from './VTree'; -import { IAttr } from '../parser/parseAttr'; +import { buildAttrModifyData, IAttr } from '../parser/parseAttr'; +import { postMessageToBackground } from '../panelConnection'; +import { ModifyAttrs } from '../utils/constants'; type IComponentInfo = { name: string; attrs: { - props?: IAttr[]; - context?: IAttr[]; - state?: IAttr[]; - hooks?: IAttr[]; + parsedProps?: IAttr[], + parsedState?: IAttr[], + parsedHooks?: IAttr[], }; parents: IData[]; + id: number; onClickParent: (item: IData) => void; }; @@ -26,11 +28,18 @@ function collapseAllNodes(attrs: IAttr[]) { }); } -function ComponentAttr({ name, attrs }: { name: string, attrs: IAttr[] }) { +function ComponentAttr({ attrsName, attrsType, attrs, id }: { + attrsName: string, + attrsType: string, + attrs: IAttr[], + id: number}) { const [collapsedNode, setCollapsedNode] = useState(collapseAllNodes(attrs)); + const [editableAttrs, setEditableAttrs] = useState(attrs); useEffect(() => { setCollapsedNode(collapseAllNodes(attrs)); + setEditableAttrs(attrs); }, [attrs]); + const handleCollapse = (item: IAttr) => { const nodes = [...collapsedNode]; const i = nodes.indexOf(item); @@ -44,7 +53,7 @@ function ComponentAttr({ name, attrs }: { name: string, attrs: IAttr[] }) { const showAttr = []; let currentIndentation = null; - attrs.forEach((item, index) => { + editableAttrs.forEach((item, index) => { const indentation = item.indentation; if (currentIndentation !== null) { if (indentation > currentIndentation) { @@ -53,17 +62,40 @@ function ComponentAttr({ name, attrs }: { name: string, attrs: IAttr[] }) { currentIndentation = null; } } - const nextItem = attrs[index + 1]; + const nextItem = editableAttrs[index + 1]; const hasChild = nextItem ? nextItem.indentation - item.indentation > 0 : false; const isCollapsed = collapsedNode.includes(item); showAttr.push( -
(handleCollapse(item))}> +
handleCollapse(item)}> {hasChild && } {`${item.name}`} {' :'} - {item.type === 'string' || item.type === 'number' - ? {item.value} - : {item.value}} + {item.type === 'string' || item.type === 'number' ? ( + { + const nextAttrs = [...editableAttrs]; + const nextItem = {...item}; + nextItem.value = event.target.value; + nextAttrs[index] = nextItem; + setEditableAttrs(nextAttrs); + }} + onKeyUp={(event) => { + const value = (event.target as HTMLInputElement).value; + if (event.key === 'Enter') { + if(isDev) { + console.log('post attr change', value); + } else { + const data = buildAttrModifyData(attrsType,attrs, value,item, index, id); + postMessageToBackground(ModifyAttrs, data); + } + } + }} + /> + ) : ( + {item.value} + )}
); if (isCollapsed) { @@ -74,7 +106,7 @@ function ComponentAttr({ name, attrs }: { name: string, attrs: IAttr[] }) { return (
- {name} + {attrsName} @@ -86,8 +118,7 @@ function ComponentAttr({ name, attrs }: { name: string, attrs: IAttr[] }) { ); } -export default function ComponentInfo({ name, attrs, parents, onClickParent }: IComponentInfo) { - const { state, props, context, hooks } = attrs; +export default function ComponentInfo({ name, attrs, parents, id, onClickParent }: IComponentInfo) { return (
@@ -104,10 +135,14 @@ export default function ComponentInfo({ name, attrs, parents, onClickParent }: I }
- {context && } - {props && props.length !== 0 && } - {state && state.length !== 0 && } - {hooks && hooks.length !== 0 && } + {Object.keys(attrs).map(attrsType => { + const parsedAttrs = attrs[attrsType]; + if (parsedAttrs && parsedAttrs.length !== 0) { + const attrsName = attrsType.slice(6); // parsedState => State + return ; + } + return null; + })}
{name &&
parents: { @@ -122,4 +157,4 @@ export default function ComponentInfo({ name, attrs, parents, onClickParent }: I
); -} \ No newline at end of file +} diff --git a/libs/extension/src/injector/index.ts b/libs/extension/src/injector/index.ts index e0372d10..17725af9 100644 --- a/libs/extension/src/injector/index.ts +++ b/libs/extension/src/injector/index.ts @@ -1,5 +1,5 @@ import parseTreeRoot, { clearVNode, queryVNode } from '../parser/parseVNode'; -import { packagePayload, checkMessage } from './../utils/transferTool'; +import { packagePayload, checkMessage } from '../utils/transferTool'; import { RequestAllVNodeTreeInfos, AllVNodeTreesInfos, @@ -7,11 +7,9 @@ import { ComponentAttrs, DevToolHook, DevToolContentScript -} from './../utils/constants'; -import { VNode } from './../../../horizon/src/renderer/vnode/VNode'; -import { ClassComponent } from '../../../horizon/src/renderer/vnode/VNodeTags'; -import { parseAttr, parseHooks } from '../parser/parseAttr'; -import { FunctionComponent } from './../../../horizon/src/renderer/vnode/VNodeTags'; +} from '../utils/constants'; +import { VNode } from '../../../horizon/src/renderer/vnode/VNode'; +import { parseVNodeAttrs } from '../parser/parseAttr'; const roots = []; @@ -48,24 +46,8 @@ function postMessage(type: string, data) { function parseCompAttrs(id: number) { const vNode: VNode = queryVNode(id); - const tag = vNode.tag; - if (tag === ClassComponent) { - const { props, state } = vNode; - const parsedProps = parseAttr(props); - const parsedState = parseAttr(state); - postMessage(ComponentAttrs, { - parsedProps, - parsedState, - }); - } else if (tag === FunctionComponent) { - const { props, hooks } = vNode; - const parsedProps = parseAttr(props); - const parsedHooks = parseHooks(hooks); - postMessage(ComponentAttrs, { - parsedProps, - parsedHooks, - }); - } + const parsedAttrs = parseVNodeAttrs(vNode); + postMessage(ComponentAttrs, parsedAttrs); } function injectHook() { diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx index af6e5d9a..993c3a66 100644 --- a/libs/extension/src/panel/App.tsx +++ b/libs/extension/src/panel/App.tsx @@ -9,13 +9,16 @@ import { FilterTree } from '../hooks/FilterTree'; import Close from '../svgs/Close'; import Arrow from './../svgs/Arrow'; import { - InitDevToolPageConnection, AllVNodeTreesInfos, RequestComponentAttrs, ComponentAttrs, - DevToolPanel, -} from './../utils/constants'; -import { packagePayload } from './../utils/transferTool'; +} from '../utils/constants'; +import { + addBackgroundMessageListener, + initBackgroundConnection, + postMessageToBackground, removeBackgroundMessageListener, +} from '../panelConnection'; +import { IAttr } from '../parser/parseAttr'; const parseVNodeData = (rawData) => { const idIndentationMap: { @@ -59,45 +62,13 @@ const getParents = (item: IData | null, parsedVNodeData: IData[]) => { return parents; }; -let connection; -if (!isDev) { - // 与 background 的唯一连接 - connection = chrome.runtime.connect({ - name: 'panel' - }); -} - -let reconnectTimes = 0; - -function postMessage(type: string, data: any) { - try { - connection.postMessage(packagePayload({ - type: type, - data: data, - }, DevToolPanel)); - } catch(err) { - // 可能出现 port 关闭的场景,需要重新建立连接,增加可靠性 - if (reconnectTimes === 20) { - reconnectTimes = 0; - console.error('reconnect failed'); - return; - } - console.error(err); - reconnectTimes++; - // 重建连接 - connection = chrome.runtime.connect({ - name: 'panel' - }); - // 重新发送初始化消息 - postMessage(InitDevToolPageConnection, chrome.devtools.inspectedWindow.tabId); - // 初始化成功后才会重新发送消息 - postMessage(type, data); - } -} - function App() { const [parsedVNodeData, setParsedVNodeData] = useState([]); - const [componentAttrs, setComponentAttrs] = useState({}); + const [componentAttrs, setComponentAttrs] = useState<{ + parsedProps?: IAttr[], + parsedState?: IAttr[], + parsedHooks?: IAttr[], + }>(); const [selectComp, setSelectComp] = useState(null); const treeRootInfos = useRef<{id: number, length: number}[]>([]); // 记录保存的根节点 id 和长度, @@ -119,14 +90,11 @@ function App() { const parsedData = parseVNodeData(mockParsedVNodeData); setParsedVNodeData(parsedData); setComponentAttrs({ - state: parsedMockState, - props: parsedMockState, + parsedProps: parsedMockState, + parsedState: parsedMockState, }); } else { - // 页面打开后发送初始化请求 - postMessage(InitDevToolPageConnection, chrome.devtools.inspectedWindow.tabId); - // 监听 background消息 - connection.onMessage.addListener(function (message) { + const handleBackgroundMessage = (message) => { const { payload } = message; if (payload) { const { type, data } = payload; @@ -145,13 +113,21 @@ function App() { } else if (type === ComponentAttrs) { const {parsedProps, parsedState, parsedHooks} = data; setComponentAttrs({ - props: parsedProps, - state: parsedState, - hooks: parsedHooks, + parsedProps, + parsedState, + parsedHooks, }); } } - }); + }; + console.log('handle connection'); + // 在页面渲染后初始化连接 + initBackgroundConnection(); + // 监听 background消息 + addBackgroundMessageListener(handleBackgroundMessage); + return () => { + removeBackgroundMessageListener(handleBackgroundMessage); + }; } }, []); @@ -162,11 +138,11 @@ function App() { const handleSelectComp = (item: IData) => { if (isDev) { setComponentAttrs({ - state: parsedMockState, - props: parsedMockState, + parsedProps: parsedMockState, + parsedState: parsedMockState, }); } else { - postMessage(RequestComponentAttrs, item.id); + postMessageToBackground(RequestComponentAttrs, item.id); } setSelectComp(item); }; @@ -216,6 +192,7 @@ function App() { name={selectComp ? selectComp.name : null} attrs={selectComp ? componentAttrs : {}} parents={parents} + id={selectComp ? selectComp.id : null} onClickParent={handleClickParent} />
diff --git a/libs/extension/src/parser/parseAttr.ts b/libs/extension/src/parser/parseAttr.ts index e18f9bad..38238eda 100644 --- a/libs/extension/src/parser/parseAttr.ts +++ b/libs/extension/src/parser/parseAttr.ts @@ -1,5 +1,8 @@ -import { Hook, Reducer, Ref } from './../../../horizon/src/renderer/hooks/HookType'; +import { Hook, Reducer, Ref } from '../../../horizon/src/renderer/hooks/HookType'; +import { ModifyHooks, ModifyProps, ModifyState } from '../utils/constants'; +import { VNode } from '../../../horizon/src/renderer/vnode/VNode'; +import { ClassComponent, FunctionComponent } from '../../../horizon/src/renderer/vnode/VNodeTags'; // 展示值为 string 的可编辑类型 type editableStringType = 'string' | 'number' | 'undefined' | 'null'; @@ -12,7 +15,7 @@ type showAsStringType = editableStringType | unEditableStringType; export type IAttr = { - name: string; + name: string | number; indentation: number; hIndex?: number; // 用于记录 hook 的 hIndex 值 } & ({ @@ -131,3 +134,61 @@ export function parseHooks(hooks: Hook[]) { }); return result; } + +export function parseVNodeAttrs(vNode: VNode) { + const tag = vNode.tag; + if (tag === ClassComponent) { + const { props, state } = vNode; + const parsedProps = parseAttr(props); + const parsedState = parseAttr(state); + return { + parsedProps, + parsedState, + }; + } else if (tag === FunctionComponent) { + const { props, hooks } = vNode; + const parsedProps = parseAttr(props); + const parsedHooks = parseHooks(hooks); + return { + parsedProps, + parsedHooks, + }; + } +} + +// 计算属性的访问顺序 +function calculateAttrAccessPath(item: IAttr, index: number, attrs: IAttr[]) { + let currentIndentation = item.indentation; + const path = [item.name]; + for(let i = index - 1; i >= 0; i--) { + const lastItem = attrs[i]; + const lastIndentation = lastItem.indentation; + if (lastIndentation < currentIndentation) { + path.push(lastItem.name); + currentIndentation = lastIndentation; + } + } + path.reverse(); + return path; +} + +export function buildAttrModifyData(parsedAttrsType: string, attrs: IAttr[], value, item: IAttr, index: number, id: number) { + const path = calculateAttrAccessPath(item, index, attrs); + let type; + if (parsedAttrsType === 'parsedProps') { + type = ModifyProps; + } else if (parsedAttrsType === 'parsedState') { + type = ModifyState; + path[0] = item.hIndex; + } else if (parsedAttrsType === 'parsedHooks') { + type = ModifyHooks; + } else { + return null; + } + return { + id: id, + type: type, + value: value, + path: path, + }; +} diff --git a/libs/extension/src/utils/constants.ts b/libs/extension/src/utils/constants.ts index e779a143..1be5c8dc 100644 --- a/libs/extension/src/utils/constants.ts +++ b/libs/extension/src/utils/constants.ts @@ -11,6 +11,14 @@ export const RequestComponentAttrs = 'get component attrs'; // 返回组件属性 export const ComponentAttrs = 'component attrs'; +export const ModifyAttrs = 'modify attrs'; + +export const ModifyProps = 'modify props'; + +export const ModifyState = 'modify state'; + +export const ModifyHooks = 'modify hooks'; + // 传递消息来源标志 export const DevToolPanel = 'dev tool panel'; @@ -19,4 +27,4 @@ export const DevToolBackground = 'dev tool background'; export const DevToolContentScript = 'dev tool content script'; -export const DevToolHook = 'dev tool hook'; \ No newline at end of file +export const DevToolHook = 'dev tool hook';