Skip to content

Commit bb57ec2

Browse files
authored
refactor: separate window messaging (#282)
1 parent c475e67 commit bb57ec2

File tree

4 files changed

+107
-87
lines changed

4 files changed

+107
-87
lines changed

packages/client/src/main.ts

Lines changed: 13 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import '@unocss/reset/tailwind.css'
22
import 'floating-vue/dist/style.css'
33

44
import type { BridgeInstanceType } from '@vue/devtools-core'
5-
import { BROADCAST_CHANNEL_NAME, isInChromePanel, isInElectron, isInIframe } from '@vue/devtools-shared'
6-
import { Bridge, HandShakeServer, createDevToolsVuePlugin, initViteClientHotContext, setupDevToolsBridge } from '@vue/devtools-core'
5+
import { isInChromePanel, isInElectron, isInIframe } from '@vue/devtools-shared'
6+
import { Bridge, HandShakeServer, createDevToolsVuePlugin, initDevToolsSeparateWindow, initDevToolsSeparateWindowBridge, initViteClientHotContext, setupDevToolsBridge } from '@vue/devtools-core'
77

8-
import type { App as AppType } from 'vue'
98
import { createApp } from 'vue'
109
import { createMemoryHistory, createRouter } from 'vue-router'
1110
import App from './App.vue'
@@ -128,66 +127,26 @@ window.addEventListener('message', (event) => {
128127
}
129128
})
130129

131-
// @TODO: refactor separate window channel
132130
if (!isInIframe && !isInChromePanel && !isInElectron) {
133-
function initSeparateWindowChannel() {
134-
const connectionInfo: {
135-
connected: boolean
136-
timer: NodeJS.Timeout | null
137-
app: AppType<Element> | null
138-
} = {
139-
connected: false,
140-
timer: null,
141-
app: null,
142-
}
143-
144-
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME)
145-
146-
function connect() {
147-
connectionInfo.timer = setInterval(() => {
148-
channel.postMessage({
149-
source: '__VUE_DEVTOOLS_CLIENT__',
150-
data: {
151-
event: 'ready',
152-
},
153-
})
154-
}, 2000)
155-
}
156-
157-
connectionInfo.app = createConnectionApp()
158-
159-
channel.onmessage = (event) => {
160-
if (event.data?.data?.event === '__VUE_DEVTOOLS_CREATE_CLIENT__') {
161-
connectionInfo.app?.unmount()
162-
connectionInfo.connected = true
163-
clearInterval(connectionInfo.timer!)
131+
function initSeparateWindow() {
132+
const connectionApp = createConnectionApp()
164133

134+
initDevToolsSeparateWindow({
135+
onConnected: (channel) => {
136+
connectionApp?.unmount()
165137
initDevTools({
166138
connect: (callback) => {
167-
const bridge = new Bridge({
168-
tracker(fn) {
169-
channel.onmessage = (event) => {
170-
if (event.data.source === '__VUE_DEVTOOLS_USER_APP__')
171-
fn(event.data.data)
172-
}
173-
},
174-
trigger(data) {
175-
channel.postMessage({
176-
source: '__VUE_DEVTOOLS_CLIENT__',
177-
data,
178-
})
179-
},
180-
})
139+
const bridge = initDevToolsSeparateWindowBridge(channel)
181140
bridge.on('disconnect', () => {
182141
channel.close()
183-
initSeparateWindowChannel()
142+
initSeparateWindow()
184143
})
185144
callback(bridge)
186145
},
187146
})
188-
}
189-
}
190-
connect()
147+
},
148+
})
191149
}
192-
initSeparateWindowChannel()
150+
151+
initSeparateWindow()
193152
}

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './bridge'
66
export * from './vite-bridge'
77
export * from './bridge-events/devtools-actions'
88
export * from './bridge-events/devtools-listeners'
9+
export * from './separate-window'
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Bridge } from '../bridge'
2+
import { prepareInjection } from '../injection'
3+
4+
const BROADCAST_CHANNEL_NAME = '__vue-devtools-broadcast-channel__'
5+
6+
const MESSAGING_APP_TARGET = '__VUE_DEVTOOLS_USER_APP__'
7+
const MESSAGING_DEVTOOLS_TARGET = '__VUE_DEVTOOLS_CLIENT__'
8+
const CHECK_CONNECTION_EVENT = '__VUE_DEVTOOLS_CHECK_CONNECTION__'
9+
const CONNECTED_EVENT = '__VUE_DEVTOOLS_CONNECTED__'
10+
11+
// used in user app side
12+
export function initAppSeparateWindow() {
13+
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME)
14+
const bridge = new Bridge({
15+
tracker(fn) {
16+
channel.onmessage = (event) => {
17+
if (event.data.target === MESSAGING_DEVTOOLS_TARGET)
18+
fn(event.data.data)
19+
}
20+
},
21+
trigger(data) {
22+
channel.postMessage({
23+
target: MESSAGING_APP_TARGET,
24+
data,
25+
})
26+
},
27+
})
28+
prepareInjection(bridge)
29+
30+
bridge.on(CHECK_CONNECTION_EVENT, () => {
31+
bridge.emit(CONNECTED_EVENT)
32+
// force sync to make sure that connect the devtools client
33+
setTimeout(() => {
34+
bridge.emit('syn')
35+
}, 200)
36+
37+
window.addEventListener('beforeunload', () => {
38+
bridge.emit('disconnect')
39+
})
40+
})
41+
}
42+
43+
export function initDevToolsSeparateWindowBridge(channel: BroadcastChannel) {
44+
const bridge = new Bridge({
45+
tracker(fn) {
46+
channel.onmessage = (event) => {
47+
if (event.data.target === MESSAGING_APP_TARGET)
48+
fn(event.data.data)
49+
}
50+
},
51+
trigger(data) {
52+
channel.postMessage({
53+
target: MESSAGING_DEVTOOLS_TARGET,
54+
data,
55+
})
56+
},
57+
})
58+
59+
return bridge
60+
}
61+
62+
// used in devtools client side
63+
export function initDevToolsSeparateWindow(
64+
options: {
65+
onConnected?: (channel: BroadcastChannel) => void
66+
} = {},
67+
) {
68+
const { onConnected = () => {} } = options
69+
let connectionTimer: NodeJS.Timeout | null = null
70+
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME)
71+
72+
function connect() {
73+
connectionTimer = setInterval(() => {
74+
channel.postMessage({
75+
target: MESSAGING_DEVTOOLS_TARGET,
76+
data: {
77+
event: CHECK_CONNECTION_EVENT,
78+
},
79+
})
80+
}, 2000)
81+
}
82+
83+
channel.onmessage = (event) => {
84+
if (event.data?.data?.event === CONNECTED_EVENT) {
85+
clearInterval(connectionTimer!)
86+
onConnected(channel)
87+
}
88+
}
89+
90+
connect()
91+
}

packages/vite/src/overlay.js

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import vueDevToolsOptions from 'virtual:vue-devtools-options'
2-
import { Bridge, prepareInjection, setDevToolsClientUrl } from '@vue/devtools-core'
3-
import { BROADCAST_CHANNEL_NAME } from '@vue/devtools-shared'
2+
import { initAppSeparateWindow, setDevToolsClientUrl } from '@vue/devtools-core'
43
import { addCustomTab, devtools, setDevToolsEnv, toggleComponentInspectorEnabled } from '@vue/devtools-kit'
54

65
const overlayDir = `${vueDevToolsOptions.clientHost || ''}${vueDevToolsOptions.base || '/'}@id/virtual:vue-devtools-path:overlay`
@@ -49,34 +48,4 @@ body.appendChild(script)
4948
// Used in the browser extension
5049
window.__VUE_DEVTOOLS_VITE_PLUGIN_CLIENT_URL__ = `${window.location.origin}${devtoolsClientUrl}`
5150

52-
// @TODO: refactor separate window channel
53-
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME)
54-
55-
const bridge = new Bridge({
56-
tracker(fn) {
57-
channel.onmessage = (event) => {
58-
if (event.data.source === '__VUE_DEVTOOLS_CLIENT__')
59-
fn(event.data.data)
60-
}
61-
},
62-
trigger(data) {
63-
channel.postMessage({
64-
source: '__VUE_DEVTOOLS_USER_APP__',
65-
data,
66-
})
67-
},
68-
})
69-
70-
prepareInjection(bridge)
71-
72-
bridge.on('ready', () => {
73-
bridge.emit('__VUE_DEVTOOLS_CREATE_CLIENT__')
74-
// force sync to make sure that connect the devtools client
75-
setTimeout(() => {
76-
bridge.emit('syn')
77-
}, 200)
78-
79-
window.addEventListener('beforeunload', (event) => {
80-
bridge.emit('disconnect')
81-
})
82-
})
51+
initAppSeparateWindow()

0 commit comments

Comments
 (0)