// ==UserScript== // @name ElementRaid Test // @namespace https://bbs.tampermonkey.net.cn/ // @version 0.0.3 // @description ElementRaid Test // @author EmpyrealTear // @match .*://.* // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://scriptcat.org/lib/513/2.0.0/ElementGetter.js // @require https://scriptcat.org/lib/2628/6.2.0.1/moduleRaid.js // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // ==/UserScript== class ReactRaid { constructor(opts) { let options = { target: document.body, // 目标 DOM 节点 entrypoint: 'react', // React 内部实例的关键字 children: [], // 子节点 }; // 合并用户配置 if (typeof opts === 'object') { options = { ...options, ...opts }; } // 初始化属性 this.target = options.target; this.entrypoint = options.entrypoint; this.children = [...options.children]; // 如果未提供子节点,则从目标节点的子节点中查找 if (this.target.children.length > 0 && this.children.length == 0) { Array.from(this.target.children).forEach((v) => { this.children.push(...this.closest(v)); }); } // 获取 React 实例和父组件 this.instance = this.getInstance(this.target); this.parentComponent = this.findParent(this.instance); this.props = this.getProperties(this.target); this.events = this.getEvents(this.instance); } /** * 查找目标节点及其子节点中的 React 实例 * @param {HTMLElement} target - 目标 DOM 节点 * @returns {Array} - 包含 React 实例的节点列表 */ closest(target) { let queue = [target]; let children = []; while (queue.length > 0) { let cur = queue.pop(); let elmObjects = Object.keys(cur); elmObjects = elmObjects.filter((v) => v.includes(this.entrypoint)); if (elmObjects.length > 0) { children.push(new ReactRaid({ target: cur })); } else { queue.push(...cur.children); } } return children; } /** * 获取目标节点的 React 属性 * @param {HTMLElement} elm - 目标 DOM 节点 * @returns {Object} - React 属性 */ getProperties(elm) { let props = {}; // 遍历节点的属性,提取 React 相关属性 Object.keys(elm).forEach((key) => { if (key.includes(this.entrypoint)) { const propName = key .replace(/\$.+$/, '') // 去除后缀 .replace(/Listening.*$/, 'Listening'); // 处理事件监听器 props[propName] = elm[key]; } }); // 提取嵌套属性(如 props.children) if (elm._reactProps) { Object.assign(props, elm._reactProps); } return props; } /** * 获取目标节点的 React 实例 * @param {HTMLElement} elm - 目标 DOM 节点 * @returns {Object|null} - React 实例 */ getInstance(elm) { // 查找 React 15 或 React 16+ 的内部实例 const key = Object.keys(elm).find( (key) => key.startsWith('__reactInternalInstance$') || // React 15 key.startsWith('__reactFiber$') // React 16+ ); return key ? elm[key] : null; } /** * 获取 React 实例的父组件 * @param {Object} internalInstance - React 实例 * @returns {Object|null} - 父组件 */ findParent(internalInstance) { if (internalInstance == null) return null; // 兼容不同版本的 React return ( internalInstance._debugOwner ?? // React 16+ 开发模式 internalInstance.return ?? // React 16+ internalInstance._currentElement?._owner // React 15 ); } /** * 获取 React 实例的事件监听器 * @param {Object} internalInstance - React 实例 * @returns {Object|null} - 事件监听器 */ getEvents(internalInstance) { if (internalInstance == null) return null; // 提取事件监听器 const events = {}; Object.keys(internalInstance).forEach((key) => { if (key.includes('Listener') || key.includes('Handler')) { events[key] = internalInstance[key]; } }); // 提取嵌套事件(如 props.onClick) if (internalInstance.memoizedProps) { Object.keys(internalInstance.memoizedProps).forEach((key) => { if (key.startsWith('on') && typeof internalInstance.memoizedProps[key] === 'function') { events[key] = internalInstance.memoizedProps[key]; } }); } return events; } /** * 获取 React 组件的类型 * @param {Object} internalInstance - React 实例 * @returns {string|null} - 组件类型 */ getComponentType(internalInstance) { if (internalInstance == null) return null; // 从 Fiber 节点中提取组件类型 const type = internalInstance.type ?? internalInstance.elementType; if (typeof type === 'string') { // 宿主组件(如 div、span) return type; } else if (typeof type === 'function') { // 函数组件或类组件 return type.name || 'AnonymousComponent'; } else if (type?.$$typeof === Symbol.for('react.memo')) { // React.memo 组件 return type.type.name || 'MemoComponent'; } else if (type?.$$typeof === Symbol.for('react.forward_ref')) { // React.forwardRef 组件 return type.render.name || 'ForwardRefComponent'; } else { // 其他类型 return 'UnknownComponent'; } } /** * 遍历 children 并返回符合筛选条件的子节点 * @param {Object|Function} filter - 筛选条件,可以是对象或函数 * @returns {Array} - 符合筛选条件的子节点列表 */ filterChildren(filter) { // 如果 filter 是函数,则直接使用该函数进行筛选 if (typeof filter === 'function') { return this.children.filter(filter); } // 如果 filter 是对象,则将其作为属性匹配条件 if (typeof filter === 'object' && filter !== null) { return this.children.filter((child) => { // 检查 child 的属性是否与 filter 对象中的属性匹配 return Object.keys(filter).every((key) => { // 如果 filter 中的属性值是函数,则调用该函数进行匹配 if (typeof filter[key] === 'function') { return filter[key](child[key]); } // 否则直接比较属性值 return child[key] === filter[key]; }); }); } // 如果 filter 不是函数也不是对象,则返回空数组 return []; } } (function () { 'use strict'; unsafeWindow.jQuery = $; // unsafeWindow.ModuleRaid = ModuleRaid; unsafeWindow.ReactRaid = ReactRaid; })();