Match-id-645d6786f413f96296f5fbf6cc123242c12adf82

This commit is contained in:
* 2022-04-18 19:45:55 +08:00 committed by *
parent 4834261253
commit e9b9f69269
9 changed files with 176 additions and 57 deletions

View File

@ -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.');

View File

@ -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, '*');
}
}
);

View File

@ -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();

View File

@ -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>

View File

@ -4,4 +4,4 @@ import App from './App';
render(
<App />,
document.getElementById('root')
);
);

View File

@ -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;

View File

@ -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';

View File

@ -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;
}

View File

@ -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;
}