Match-id-645d6786f413f96296f5fbf6cc123242c12adf82
This commit is contained in:
parent
4834261253
commit
e9b9f69269
|
@ -1,30 +1,41 @@
|
|||
import { checkData, packagePayload } from '../utils/transferTool';
|
||||
import { requestAllVNodeTreeInfos, initDevToolPageConnection } from '../utils/constants';
|
||||
|
||||
// 多个页面、tab页共享一个 background,需要建立连接池,给每个tab建立连接
|
||||
const connections = {};
|
||||
|
||||
// panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数
|
||||
chrome.runtime.onConnect.addListener(function (port) {
|
||||
|
||||
// The original connection event doesn't include the tab ID of the
|
||||
// DevTools page, so we need to send it explicitly.
|
||||
function extensionListener(message, sender, sendResponse) {
|
||||
// 在backgroundPageConnection创建后会发送初始化请求,这样就可以获取tabId,给连接编号
|
||||
if (message.name === 'init') {
|
||||
// 获取 panel 所在 tab 页的tabId
|
||||
connections[message.tabId] = port;
|
||||
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
|
||||
chrome.tabs.sendMessage(tabs[0].id, {tag: 'init horizon info'}, function(response) {
|
||||
console.log(response.farewell);
|
||||
});
|
||||
function extensionListener(message) {
|
||||
const isHorizonMessage = checkData(message);
|
||||
if (isHorizonMessage) {
|
||||
console.log('received message', message);
|
||||
const { payload } = message;
|
||||
const { type, data } = payload;
|
||||
let passMessage;
|
||||
if (type === initDevToolPageConnection) {
|
||||
if (!connections[data]) {
|
||||
// 获取 panel 所在 tab 页的tabId
|
||||
connections[data] = port;
|
||||
}
|
||||
passMessage = packagePayload({ type: requestAllVNodeTreeInfos });
|
||||
} else {
|
||||
passMessage = message;
|
||||
}
|
||||
console.log('post message:', passMessage);
|
||||
// 查询参数有 active 和 currentWindow, 如果开发者工具与页面分离,会导致currentWindow为false才能找到
|
||||
// 所以只用 active 参数查找,但不确定这么写是否会引发查询错误的情况
|
||||
// 或许需要用不同的查询参数查找两次
|
||||
chrome.tabs.query({ active: true }, function (tabs) {
|
||||
if (tabs.length) {
|
||||
chrome.tabs.sendMessage(tabs[0].id, passMessage);
|
||||
console.log('post message end');
|
||||
} else {
|
||||
console.log('do not find message');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.name === 'update') {
|
||||
return;
|
||||
}
|
||||
// other message handling
|
||||
}
|
||||
|
||||
// Listen to messages sent from the DevTools page
|
||||
port.onMessage.addListener(extensionListener);
|
||||
|
||||
|
@ -42,11 +53,11 @@ chrome.runtime.onConnect.addListener(function (port) {
|
|||
});
|
||||
|
||||
// 监听来自 content script 的消息,并将消息发送给对应的 devTools page,也就是 panel
|
||||
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
||||
// Messages from content scripts should have sender.tab set
|
||||
if (sender.tab) {
|
||||
const tabId = sender.tab.id;
|
||||
if (tabId in connections) {
|
||||
if (tabId in connections && checkData(request)) {
|
||||
connections[tabId].postMessage(request);
|
||||
} else {
|
||||
console.log('Tab not found in connection list.');
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { injectCode } from '../utils/injectUtils';
|
||||
import { checkData } from '../utils/transferTool';
|
||||
|
||||
// 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook
|
||||
injectCode(chrome.runtime.getURL('/injector.js'));
|
||||
|
@ -10,12 +11,10 @@ window.addEventListener('message', event => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS')) {
|
||||
console.log('Content script received: ' + JSON.stringify(event.data.vNode));
|
||||
const data = event.data;
|
||||
if (checkData(data)) {
|
||||
// 传递给background
|
||||
chrome.runtime.sendMessage(event.data.vNode, function (response) {
|
||||
console.log(response);
|
||||
});
|
||||
chrome.runtime.sendMessage(data);
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
@ -23,14 +22,12 @@ window.addEventListener('message', event => {
|
|||
|
||||
// 监听来自background的消息
|
||||
chrome.runtime.onMessage.addListener(
|
||||
function (request, sender, sendResponse) {
|
||||
console.log(sender.tab ?
|
||||
'from a content script:' + sender.tab.url :
|
||||
'from the extension');
|
||||
if (request.tag === 'init horizon info') {
|
||||
function (request, sender) {
|
||||
// 该方法可以监听页面 contentScript 和插件的消息
|
||||
// 没有 tab 信息说明消息来自插件
|
||||
if (!sender.tab && checkData(request)) {
|
||||
// 传递消息给页面
|
||||
console.log('start pass info to webpage');
|
||||
window.postMessage({type: 'HORIZON_DEV_TOOLS', id: 1}, '*');
|
||||
window.postMessage(request, '*');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import parseTreeRoot from "../parser/parseVNode";
|
||||
import parseTreeRoot, { deleteVNode } from '../parser/parseVNode';
|
||||
import { packagePayload, checkData } from './../utils/transferTool';
|
||||
import { oneVNodeTreeInfos, allVNodeTreesInfos, requestAllVNodeTreeInfos } from './../utils/constants';
|
||||
|
||||
const roots = [];
|
||||
|
||||
function injectHook() {
|
||||
if (window.__HORIZON_DEV_HOOK__) {
|
||||
|
@ -7,26 +11,49 @@ function injectHook() {
|
|||
Object.defineProperty(window, '__HORIZON_DEV_HOOK__', {
|
||||
enumerable: false,
|
||||
value: {
|
||||
roots: [],
|
||||
addIfNotInclude: function( treeRoot: any) {
|
||||
if (!roots.includes(treeRoot)) {
|
||||
roots.push(treeRoot);
|
||||
}
|
||||
},
|
||||
send: function (vNode: any) {
|
||||
const result = parseTreeRoot(vNode);
|
||||
window.postMessage({
|
||||
type: 'HORIZON_DEV_TOOLS', vNode: result
|
||||
}, '*');
|
||||
window.postMessage(packagePayload({
|
||||
data: result,
|
||||
type: oneVNodeTreeInfos
|
||||
}), '*');
|
||||
},
|
||||
listen: function (id: number) {
|
||||
window.addEventListener('message', function(event) {
|
||||
// We only accept messages from ourselves
|
||||
if (event.source !== window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS') && event.data.id === id) {
|
||||
console.log('todo');
|
||||
}
|
||||
});
|
||||
delete: function (vNode: any) {
|
||||
// 开发工具中保存了 vNode 的引用,在清理 VNode 的时候需要一并删除
|
||||
deleteVNode(vNode);
|
||||
const index = roots.indexOf(vNode);
|
||||
if (index !== -1) {
|
||||
roots.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
window.addEventListener('message', function (event) {
|
||||
// We only accept messages from ourselves
|
||||
if (event.source !== window) {
|
||||
return;
|
||||
}
|
||||
const request = event.data;
|
||||
if (checkData(request)) {
|
||||
const { payload } = request;
|
||||
const { type, data } = payload;
|
||||
if (type === requestAllVNodeTreeInfos) {
|
||||
const result = roots.reduce((pre, current) => {
|
||||
const info = parseTreeRoot(current);
|
||||
pre.push(info);
|
||||
return pre;
|
||||
}, []);
|
||||
window.postMessage(packagePayload({
|
||||
data: result,
|
||||
type: allVNodeTreesInfos
|
||||
}), '*');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
injectHook();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'horizon';
|
||||
import { useState, useEffect, useRef } from 'horizon';
|
||||
import VTree, { IData } from '../components/VTree';
|
||||
import Search from '../components/Search';
|
||||
import ComponentInfo from '../components/ComponentInfo';
|
||||
|
@ -8,6 +8,8 @@ import { mockParsedVNodeData, parsedMockState } from '../devtools/mock';
|
|||
import { FilterTree } from '../hooks/FilterTree';
|
||||
import Close from '../svgs/Close';
|
||||
import Arrow from './../svgs/Arrow';
|
||||
import { initDevToolPageConnection, allVNodeTreesInfos, requestComponentAttrs } from './../utils/constants';
|
||||
import { packagePayload } from './../utils/transferTool';
|
||||
|
||||
const parseVNodeData = (rawData) => {
|
||||
const idIndentationMap: {
|
||||
|
@ -55,6 +57,7 @@ function App() {
|
|||
const [parsedVNodeData, setParsedVNodeData] = useState([]);
|
||||
const [componentAttrs, setComponentAttrs] = useState({});
|
||||
const [selectComp, setSelectComp] = useState(null);
|
||||
const treeRootInfos = useRef<{[id: string]: number}>({}); // 记录保存的根节点 id 和长度
|
||||
|
||||
const {
|
||||
filterValue,
|
||||
|
@ -77,6 +80,34 @@ function App() {
|
|||
state: parsedMockState,
|
||||
props: parsedMockState,
|
||||
});
|
||||
} else {
|
||||
const connection = chrome.runtime.connect({
|
||||
name: 'panel'
|
||||
});
|
||||
// 页面打开后发送初始化请求
|
||||
connection.postMessage(packagePayload({
|
||||
type: initDevToolPageConnection,
|
||||
data: chrome.devtools.inspectedWindow.tabId
|
||||
}));
|
||||
// 监听 background消息
|
||||
connection.onMessage.addListener(function (message) {
|
||||
const { payload } = message;
|
||||
if (payload) {
|
||||
const { type, data } = payload;
|
||||
if (type === allVNodeTreesInfos) {
|
||||
const allTreeData = data.reduce((pre, current) => {
|
||||
const parsedTreeData = parseVNodeData(current);
|
||||
const length = parsedTreeData.length;
|
||||
if (length) {
|
||||
const treeRoot = parsedTreeData[0];
|
||||
treeRootInfos.current[treeRoot.id] = length;
|
||||
}
|
||||
return pre.concat(parsedTreeData);
|
||||
}, []);
|
||||
setParsedVNodeData(allTreeData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -85,10 +116,17 @@ function App() {
|
|||
};
|
||||
|
||||
const handleSelectComp = (item: IData) => {
|
||||
setComponentAttrs({
|
||||
state: parsedMockState,
|
||||
props: parsedMockState,
|
||||
});
|
||||
if (isDev) {
|
||||
setComponentAttrs({
|
||||
state: parsedMockState,
|
||||
props: parsedMockState,
|
||||
});
|
||||
} else {
|
||||
connection.postMessage({
|
||||
name: requestComponentAttrs,
|
||||
data: item.id
|
||||
});
|
||||
}
|
||||
setSelectComp(item);
|
||||
};
|
||||
|
||||
|
@ -134,8 +172,8 @@ function App() {
|
|||
</div>
|
||||
<div className={styles.right}>
|
||||
<ComponentInfo
|
||||
name={selectComp ? selectComp.name: null}
|
||||
attrs={selectComp ? componentAttrs: {}}
|
||||
name={selectComp ? selectComp.name : null}
|
||||
attrs={selectComp ? componentAttrs : {}}
|
||||
parents={parents}
|
||||
onClickParent={handleClickParent} />
|
||||
</div>
|
||||
|
|
|
@ -4,4 +4,4 @@ import App from './App';
|
|||
render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
);
|
||||
|
|
|
@ -57,4 +57,12 @@ function parseTreeRoot(treeRoot: VNode) {
|
|||
return result;
|
||||
}
|
||||
|
||||
export function deleteVNode(vNode: VNode) {
|
||||
if (VNodeToIdMap.has(vNode)) {
|
||||
const id = VNodeToIdMap.get(vNode);
|
||||
VNodeToIdMap.delete(vNode);
|
||||
IdToVNodeMap.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
export default parseTreeRoot;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// panel 页面打开后初始化连接标志
|
||||
export const initDevToolPageConnection = 'init dev tool page connection';
|
||||
// background 解析全部 root VNodes 标志
|
||||
export const requestAllVNodeTreeInfos = 'request all vNodes tree infos';
|
||||
// vNodes 全部树解析结果标志
|
||||
export const allVNodeTreesInfos = 'vNode trees Infos';
|
||||
// 一棵树的解析
|
||||
export const oneVNodeTreeInfos = 'one vNode tree';
|
||||
// 获取组件属性
|
||||
export const requestComponentAttrs = 'get component attrs';
|
||||
// 返回组件属性
|
||||
export const componentAttrs = 'component attrs';
|
|
@ -0,0 +1,21 @@
|
|||
const devTools = 'HORIZON_DEV_TOOLS';
|
||||
|
||||
interface payLoadType {
|
||||
type: string,
|
||||
data?: any,
|
||||
}
|
||||
|
||||
export function packagePayload(payload: payLoadType) {
|
||||
return {
|
||||
type: devTools,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function checkData(data: any) {
|
||||
if (data?.type === devTools) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -275,6 +275,11 @@ function renderFromRoot(treeRoot) {
|
|||
// 2. 提交变更
|
||||
submitToRender(treeRoot);
|
||||
|
||||
if (window.__HORIZON_DEV_HOOK__) {
|
||||
const hook = window.__HORIZON_DEV_HOOK__;
|
||||
hook.addIfNotInclude(treeRoot);
|
||||
hook.send(treeRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue