Skip to content

Commit 55e9d64

Browse files
committed
✨ add UNSTABLE_useVueComponentTracker composable
1 parent 6df2366 commit 55e9d64

4 files changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { RumPublicApi } from '@datadog/browser-rum-core'
2+
import { onRumInit } from '../vuePlugin'
3+
4+
export const addDurationVital: RumPublicApi['addDurationVital'] = (name, options) => {
5+
onRumInit((_, rumPublicApi) => {
6+
rumPublicApi.addDurationVital(name, options)
7+
})
8+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { defineComponent, h } from 'vue'
2+
import { mount } from '@vue/test-utils'
3+
import { initializeVuePlugin } from '../../../test/initializeVuePlugin'
4+
import { mockClock } from '../../../../core/test'
5+
// eslint-disable-next-line camelcase
6+
import { UNSTABLE_useVueComponentTracker } from './useVueComponentTracker'
7+
8+
const MOUNT_DURATION = 50
9+
10+
describe('UNSTABLE_useVueComponentTracker', () => {
11+
it('reports a vueComponentRender vital on mount', async () => {
12+
const addDurationVitalSpy = jasmine.createSpy()
13+
const clock = mockClock()
14+
initializeVuePlugin({ publicApi: { addDurationVital: addDurationVitalSpy } })
15+
16+
const TrackedComponent = defineComponent({
17+
setup() {
18+
UNSTABLE_useVueComponentTracker('MyComponent')
19+
},
20+
render() {
21+
clock.tick(MOUNT_DURATION)
22+
return h('div')
23+
},
24+
})
25+
26+
// eslint-disable-next-line @typescript-eslint/await-thenable
27+
await mount(TrackedComponent)
28+
29+
expect(addDurationVitalSpy).toHaveBeenCalledTimes(1)
30+
const [name, options] = addDurationVitalSpy.calls.mostRecent().args
31+
expect(name).toBe('vueComponentRender')
32+
expect(options).toEqual({
33+
description: 'MyComponent',
34+
startTime: clock.timeStamp(0),
35+
duration: MOUNT_DURATION,
36+
context: {
37+
is_first_render: true,
38+
framework: 'vue',
39+
},
40+
})
41+
})
42+
43+
it('reports is_first_render: false on update', async () => {
44+
const addDurationVitalSpy = jasmine.createSpy()
45+
initializeVuePlugin({ publicApi: { addDurationVital: addDurationVitalSpy } })
46+
47+
const TrackedComponent = defineComponent({
48+
props: ['count'],
49+
setup() {
50+
UNSTABLE_useVueComponentTracker('MyComponent')
51+
},
52+
render() {
53+
return h('div', this.count)
54+
},
55+
})
56+
57+
// eslint-disable-next-line @typescript-eslint/await-thenable
58+
const wrapper = await mount(TrackedComponent, { props: { count: 0 } })
59+
await wrapper.setProps({ count: 1 })
60+
61+
expect(addDurationVitalSpy).toHaveBeenCalledTimes(2)
62+
const options = addDurationVitalSpy.calls.mostRecent().args[1]
63+
expect(options.context.is_first_render).toBe(false)
64+
})
65+
})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated } from 'vue'
2+
import { clocksNow } from '@datadog/browser-core'
3+
import { addDurationVital } from './addDurationVital'
4+
5+
/**
6+
* Track the performance of a Vue component.
7+
*
8+
* @category Performance
9+
* @experimental
10+
* @example
11+
* ```ts
12+
* import { UNSTABLE_useVueComponentTracker } from '@datadog/browser-rum-vue'
13+
*
14+
* // Inside a component's setup():
15+
* UNSTABLE_useVueComponentTracker('MyComponent')
16+
* ```
17+
*/
18+
// eslint-disable-next-line camelcase
19+
export function UNSTABLE_useVueComponentTracker(name: string): void {
20+
let mountStartClocks: ReturnType<typeof clocksNow> | undefined
21+
let updateStartClocks: ReturnType<typeof clocksNow> | undefined
22+
23+
onBeforeMount(() => {
24+
mountStartClocks = clocksNow()
25+
})
26+
27+
onMounted(() => {
28+
if (!mountStartClocks) {
29+
return
30+
}
31+
const duration = clocksNow().relative - mountStartClocks.relative
32+
addDurationVital('vueComponentRender', {
33+
description: name,
34+
startTime: mountStartClocks.timeStamp,
35+
duration,
36+
context: {
37+
is_first_render: true,
38+
framework: 'vue',
39+
},
40+
})
41+
})
42+
43+
onBeforeUpdate(() => {
44+
updateStartClocks = clocksNow()
45+
})
46+
47+
onUpdated(() => {
48+
if (!updateStartClocks) {
49+
return
50+
}
51+
const duration = clocksNow().relative - updateStartClocks.relative
52+
addDurationVital('vueComponentRender', {
53+
description: name,
54+
startTime: updateStartClocks.timeStamp,
55+
duration,
56+
context: {
57+
is_first_render: false,
58+
framework: 'vue',
59+
},
60+
})
61+
})
62+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export type { VuePluginConfiguration, VuePlugin } from '../domain/vuePlugin'
22
export { vuePlugin } from '../domain/vuePlugin'
33
export { addVueError } from '../domain/error/addVueError'
4+
// eslint-disable-next-line camelcase
5+
export { UNSTABLE_useVueComponentTracker } from '../domain/performance/useVueComponentTracker'

0 commit comments

Comments
 (0)