1
1
import { type ComponentInternalInstance } from 'vue'
2
2
3
+ let ctx : CanvasRenderingContext2D | null = null
4
+ const updateCounters = new WeakMap < HTMLElement , number > ( )
5
+ const elements = new Set < HTMLElement > ( )
6
+
3
7
export function VueChangeMarker ( ) {
4
- const ctx = initCanvas ( )
8
+ ctx = initCanvas ( )
9
+
5
10
function emit ( event : string , ...payload : any [ ] ) {
6
11
if ( event === 'component:updated' ) {
7
12
const updatedInstance = payload [ 3 ] as ComponentInternalInstance
@@ -15,24 +20,11 @@ export function VueChangeMarker() {
15
20
if ( ! el ) return
16
21
// 注释节点之类的 transition 这类组件
17
22
if ( ! el . getBoundingClientRect ) return
18
- const rect = el . getBoundingClientRect ( )
19
- const width = ctx . canvas . width
20
- const height = ctx . canvas . height
21
-
22
- ctx . font = '14px Arial'
23
- ctx . fillStyle = 'red'
24
-
25
- ctx . fillText ( 'updated component: ' + componentName , rect . x , rect . y - 5 )
26
-
27
- ctx . fillStyle = 'blue'
28
- ctx . strokeStyle = 'red'
29
- ctx . lineWidth = 1
30
- ctx . strokeRect ( rect . x , rect . y , rect . width , rect . height )
31
- setTimeout ( ( ) => {
32
- ctx . clearRect ( 0 , 0 , width , height )
33
- } , 100 )
23
+
24
+ scheduler . addTask ( ( ) => highlight ( ctx ! , el , componentName ) )
34
25
}
35
26
}
27
+
36
28
if ( window . __VUE_DEVTOOLS_GLOBAL_HOOK__ ?. id !== 'vue-change-marker' ) {
37
29
const oldEmit = window . __VUE_DEVTOOLS_GLOBAL_HOOK__ . emit
38
30
window . __VUE_DEVTOOLS_GLOBAL_HOOK__ . emit = function ( ...args : any [ ] ) {
@@ -71,3 +63,84 @@ function initCanvas() {
71
63
72
64
return ctx
73
65
}
66
+
67
+ function clearCanvas ( ctx : CanvasRenderingContext2D ) {
68
+ const width = ctx . canvas . width
69
+ const height = ctx . canvas . height
70
+ ctx . clearRect ( 0 , 0 , width , height )
71
+ }
72
+ function resetAllUpdateCounters ( ) {
73
+ elements . forEach ( ( el ) => {
74
+ updateCounters . set ( el , 0 ) // 将每个元素的更新计数器重置为 0
75
+ } )
76
+ }
77
+
78
+ type HighlightTask = {
79
+ fn : ( ) => void
80
+ }
81
+
82
+ class HighlightScheduler {
83
+ taskQueue : HighlightTask [ ] = [ ]
84
+ addTask ( fn : ( ) => void ) {
85
+ this . taskQueue . push ( { fn } )
86
+ }
87
+
88
+ // 执行队列中的所有任务
89
+ async executeTasks ( ) {
90
+ if ( this . taskQueue . length === 0 ) return
91
+ while ( this . taskQueue . length ) {
92
+ const task = this . taskQueue . shift ( ) !
93
+ task . fn ( )
94
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 30 ) )
95
+ }
96
+ if ( this . taskQueue . length === 0 ) {
97
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
98
+ clearCanvas ( ctx ! )
99
+ resetAllUpdateCounters ( )
100
+ }
101
+ }
102
+ }
103
+
104
+ // 测试
105
+ const scheduler = new HighlightScheduler ( )
106
+ executeTasks ( )
107
+
108
+ async function executeTasks ( ) {
109
+ if ( scheduler . taskQueue . length ) {
110
+ await scheduler . executeTasks ( )
111
+ }
112
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
113
+ await executeTasks ( )
114
+ }
115
+
116
+ function highlight (
117
+ ctx : CanvasRenderingContext2D ,
118
+ el : HTMLElement ,
119
+ componentName : string ,
120
+ ) {
121
+ clearCanvas ( ctx ! )
122
+
123
+ let updateCounter = updateCounters . get ( el ) || 0
124
+ updateCounter ++
125
+
126
+ updateCounters . set ( el , updateCounter )
127
+ elements . add ( el )
128
+
129
+ const opacity = Math . min ( 0.7 , updateCounter * 0.2 )
130
+ const rect = el . getBoundingClientRect ( )
131
+
132
+ ctx . fillStyle = `rgba(255, 0, 0, ${ opacity } )`
133
+ ctx . fillRect ( rect . x , rect . y - 20 , rect . width , 20 )
134
+
135
+ ctx . fillStyle = `white`
136
+ ctx . font = '14px Arial'
137
+ ctx . fillText (
138
+ 'updated component: ' + componentName + ' x:' + updateCounter ,
139
+ rect . x ,
140
+ rect . y - 5 ,
141
+ )
142
+
143
+ ctx . strokeStyle = `rgba(255, 0, 0, ${ opacity } )`
144
+ ctx . lineWidth = 1
145
+ ctx . strokeRect ( rect . x , rect . y , rect . width , rect . height )
146
+ }
0 commit comments