Public API for React DevTools Plus plugins
npm install @react-devtools-plus/apiReact DevTools Plus 插件开发 API。
本包提供创建 DevTools 插件所需的所有 API,包括插件定义、宿主脚本、视图通信等。
``bash`
npm install @react-devtools-plus/apior
pnpm add @react-devtools-plus/api
创建 DevTools 插件的主要入口。
`typescript
import { defineDevToolsPlugin } from '@react-devtools-plus/api'
import type { DevToolsPluginProps } from '@react-devtools-plus/api'
// 插件面板组件
function MyPanel({ tree, selectedNodeId, theme }: DevToolsPluginProps) {
return (
Selected: {selectedNodeId ?? 'None'}
// 定义插件
export const MyPlugin = defineDevToolsPlugin({
meta: {
name: 'my-plugin',
title: 'My Plugin',
icon: 'lucide:puzzle',
// npm 包信息(用于生产环境加载)
packageName: '@my-org/devtools-plugin',
viewExportName: 'MyPanel',
bundlePath: 'dist/index.mjs',
},
view: {
src: MyPanel,
},
defaultOptions: {
enabled: true,
},
})
`
使用方式:
`typescript
// vite.config.ts
import { reactDevToolsPlus } from 'react-devtools-plus/vite'
import { MyPlugin } from '@my-org/devtools-plugin'
export default {
plugins: [
reactDevToolsPlus({
plugins: [
MyPlugin(), // 默认配置
MyPlugin({ enabled: false }), // 自定义配置
],
}),
],
}
`
宿主脚本运行在宿主应用的主线程中,可以拦截网络请求、操作 DOM、与 DevTools UI 通信。
`typescript
// src/host.ts
import { defineHostPlugin } from '@react-devtools-plus/api'
const requests: Map
export default defineHostPlugin({
name: 'network-inspector',
// RPC 方法 - View 层可调用
rpc: {
getRequests() {
return Array.from(requests.values())
},
clearRequests() {
requests.clear()
},
},
// 初始化逻辑
setup(ctx) {
// 获取用户配置
const options = ctx.getOptions<{ maxRequests: number }>()
// 拦截 fetch 请求
ctx.network.onFetch({
onRequest(request) {
ctx.emit('request:start', { url: request.url })
},
onResponse(response, request) {
ctx.emit('request:complete', {
url: request.url,
status: response.status,
})
},
})
// 返回清理函数
return () => {
requests.clear()
}
},
})
`
在插件面板组件中使用这些 Hooks 与宿主脚本通信。
`typescript
import { usePluginRpc } from '@react-devtools-plus/api'
function MyPanel() {
const rpc = usePluginRpc()
const handleFetch = async () => {
const data = await rpc.call('getRequests')
console.log(data)
}
return
}
`
`typescript
import { usePluginEvent } from '@react-devtools-plus/api'
import { useState } from 'react'
function MyPanel() {
const [requests, setRequests] = useState([])
// 监听宿主脚本发送的事件
usePluginEvent('request:add', (request) => {
setRequests(prev => [...prev, request])
})
return
$3
`typescript
import { usePluginOptions } from '@react-devtools-plus/api'interface MyPluginOptions {
maxItems: number
showDebug: boolean
}
function MyPanel() {
const options = usePluginOptions()
return
最大条目: {options.maxItems}
}
`非 Hook API
在 React 组件外部使用。
$3
`typescript
import { createRpcClient } from '@react-devtools-plus/api'const rpc = createRpcClient('my-plugin')
const data = await rpc.call('getData')
`$3
`typescript
import { getPluginOptions } from '@react-devtools-plus/api'const options = getPluginOptions('my-plugin')
`完整插件示例
$3
`typescript
import { defineDevToolsPlugin } from '@react-devtools-plus/api'
import MyPanel from './Panel'export interface MyPluginOptions {
maxRequests?: number
debug?: boolean
}
export const MyPlugin = defineDevToolsPlugin({
meta: {
name: 'my-plugin',
title: 'My Plugin',
icon: 'lucide:activity',
packageName: '@my-org/my-plugin',
viewExportName: 'MyPanel',
bundlePath: 'dist/index.mjs',
},
view: {
src: MyPanel,
},
// 宿主脚本
host: {
src: './src/host.ts',
inject: 'head', // 或使用函数精确控制
},
// 注入额外的 HTML 内容
htmlInject: [
{
tag: 'link',
attrs: { rel: 'stylesheet', href: '/my-plugin.css' },
inject: 'head',
},
],
defaultOptions: {
maxRequests: 100,
debug: false,
},
})
export { default as MyPanel } from './Panel'
`$3
`typescript
import { defineHostPlugin } from '@react-devtools-plus/api'
import type { MyPluginOptions } from './index'const logs: string[] = []
export default defineHostPlugin({
name: 'my-plugin',
rpc: {
getLogs: () => logs,
clearLogs: () => { logs.length = 0 },
},
setup(ctx) {
const options = ctx.getOptions()
if (options.debug) {
console.log('[MyPlugin] Debug mode enabled')
}
// 监听点击事件
const handleClick = (e: MouseEvent) => {
const target = e.target as HTMLElement
logs.push(
Click: ${target.tagName})
ctx.emit('log:add', { message: Click: ${target.tagName} })
} document.addEventListener('click', handleClick)
// 返回清理函数
return () => {
document.removeEventListener('click', handleClick)
}
},
})
`$3
`typescript
import { usePluginRpc, usePluginEvent, usePluginOptions } from '@react-devtools-plus/api'
import type { DevToolsPluginProps } from '@react-devtools-plus/api'
import type { MyPluginOptions } from './index'
import { useState, useEffect } from 'react'export default function MyPanel({ theme }: DevToolsPluginProps) {
const rpc = usePluginRpc()
const options = usePluginOptions()
const [logs, setLogs] = useState([])
// 初始加载
useEffect(() => {
rpc.call('getLogs').then(setLogs)
}, [])
// 监听新日志
usePluginEvent('log:add', ({ message }) => {
setLogs(prev => [...prev.slice(-options.maxRequests! + 1), message])
})
const handleClear = async () => {
await rpc.call('clearLogs')
setLogs([])
}
const isDark = theme.mode === 'dark'
return (
padding: 16,
background: isDark ? '#1a1a1a' : '#fff',
color: isDark ? '#fff' : '#000',
}}>
My Plugin
{logs.map((log, i) => (
- {log}
))}
HTML 内容注入
除了宿主脚本,还可以注入任意 HTML 内容:
`typescript
export const MyPlugin = defineDevToolsPlugin({
// ...
htmlInject: [
// 注入 importmap
{
tag: 'script',
attrs: { type: 'importmap' },
children: JSON.stringify({
imports: { 'lodash': 'https://cdn.jsdelivr.net/npm/lodash-es/+esm' }
}),
inject: 'head-prepend',
}, // 注入样式表
{
tag: 'link',
attrs: { rel: 'stylesheet', href: '/plugin.css' },
inject: 'head',
},
// 使用函数精确定位
{
tag: 'meta',
attrs: { name: 'plugin-version', content: '1.0.0' },
inject: (html, content) => {
return html.replace(/
/, \n${content})
},
},
],
})
`注入位置
inject 选项控制脚本或 HTML 内容的注入位置:$3
| 值 | 说明 |
| ---------------- | ----------------------------------- |
|
'head' | 在 末尾注入 |
| 'head-prepend' | 在 开头注入(最早执行) |
| 'body' | 在 末尾注入 |
| 'body-prepend' | 在 开头注入 |
| 'idle' | 使用 requestIdleCallback 延迟注入 |$3
`typescript
inject: (html, content) => {
// 在 React 脚本之后注入
return html.replace(
/(