Commit 8e1a8dee authored by Sean McGivern's avatar Sean McGivern

Lazy-load performance bar UI

We need to eagerly load the performance bar wrapper component
(`performance_bar/index.js`) when the page is loaded and the container element
is present, to ensure that we register the interceptor early enough in the
page's lifecycle.

However, we don't need to load the UI at that point. Not doing so means that we
can let Webpack extract the UI (`performance_bar/components/*`) into its own
chunk, so users who don't see the performance bar don't have to load too much
unnecessary JavaScript.
parent f4e60c26
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import PerformanceBarService from '../services/performance_bar_service';
import detailedMetric from './detailed_metric.vue'; import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue'; import requestSelector from './request_selector.vue';
import simpleMetric from './simple_metric.vue'; import simpleMetric from './simple_metric.vue';
import Flash from '../../flash';
export default { export default {
components: { components: {
detailedMetric, detailedMetric,
...@@ -69,37 +66,13 @@ export default { ...@@ -69,37 +66,13 @@ export default {
}, },
}, },
mounted() { mounted() {
this.interceptor = PerformanceBarService.registerInterceptor(
this.peekUrl,
this.loadRequestDetails,
);
this.loadRequestDetails(this.requestId, window.location.href);
this.currentRequest = this.requestId; this.currentRequest = this.requestId;
if (this.lineProfileModal.length) { if (this.lineProfileModal.length) {
this.lineProfileModal.modal('toggle'); this.lineProfileModal.modal('toggle');
} }
}, },
beforeDestroy() {
PerformanceBarService.removeInterceptor(this.interceptor);
},
methods: { methods: {
loadRequestDetails(requestId, requestUrl) {
if (!this.store.canTrackRequest(requestUrl)) {
return;
}
this.store.addRequest(requestId, requestUrl);
PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
.then(res => {
this.store.addRequestDetails(requestId, res.data.data);
})
.catch(() =>
Flash(`Error getting performance bar results for ${requestId}`),
);
},
changeCurrentRequest(newRequestId) { changeCurrentRequest(newRequestId) {
this.currentRequest = newRequestId; this.currentRequest = newRequestId;
}, },
......
import Vue from 'vue'; import Vue from 'vue';
import performanceBarApp from './components/performance_bar_app.vue'; import Flash from '../flash';
import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store'; import PerformanceBarStore from './stores/performance_bar_store';
export default ({ container }) => export default ({ container }) =>
new Vue({ new Vue({
el: container, el: container,
components: { components: {
performanceBarApp, performanceBarApp: () => import('./components/performance_bar_app.vue'),
}, },
data() { data() {
const performanceBarData = document.querySelector(this.$options.el) const performanceBarData = document.querySelector(this.$options.el)
...@@ -21,6 +22,34 @@ export default ({ container }) => ...@@ -21,6 +22,34 @@ export default ({ container }) =>
profileUrl: performanceBarData.profileUrl, profileUrl: performanceBarData.profileUrl,
}; };
}, },
mounted() {
this.interceptor = PerformanceBarService.registerInterceptor(
this.peekUrl,
this.loadRequestDetails,
);
this.loadRequestDetails(this.requestId, window.location.href);
},
beforeDestroy() {
PerformanceBarService.removeInterceptor(this.interceptor);
},
methods: {
loadRequestDetails(requestId, requestUrl) {
if (!this.store.canTrackRequest(requestUrl)) {
return;
}
this.store.addRequest(requestId, requestUrl);
PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
.then(res => {
this.store.addRequestDetails(requestId, res.data.data);
})
.catch(() =>
Flash(`Error getting performance bar results for ${requestId}`),
);
},
},
render(createElement) { render(createElement) {
return createElement('performance-bar-app', { return createElement('performance-bar-app', {
props: { props: {
......
import Vue from 'vue'; import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import performanceBarApp from '~/performance_bar/components/performance_bar_app.vue'; import performanceBarApp from '~/performance_bar/components/performance_bar_app.vue';
import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
import PerformanceBarStore from '~/performance_bar/stores/performance_bar_store'; import PerformanceBarStore from '~/performance_bar/stores/performance_bar_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import MockAdapter from 'axios-mock-adapter';
describe('performance bar', () => { describe('performance bar app', () => {
let mock;
let vm; let vm;
beforeEach(() => { beforeEach(() => {
const store = new PerformanceBarStore(); const store = new PerformanceBarStore();
mock = new MockAdapter(axios);
mock.onGet('/-/peek/results').reply(
200,
{
data: {
gc: {
invokes: 0,
invoke_time: '0.00',
use_size: 0,
total_size: 0,
total_object: 0,
gc_time: '0.00',
},
host: { hostname: 'web-01' },
},
},
{},
);
vm = mountComponent(Vue.extend(performanceBarApp), { vm = mountComponent(Vue.extend(performanceBarApp), {
store, store,
env: 'development', env: 'development',
...@@ -45,44 +21,9 @@ describe('performance bar', () => { ...@@ -45,44 +21,9 @@ describe('performance bar', () => {
afterEach(() => { afterEach(() => {
vm.$destroy(); vm.$destroy();
mock.restore();
}); });
it('sets the class to match the environment', () => { it('sets the class to match the environment', () => {
expect(vm.$el.getAttribute('class')).toContain('development'); expect(vm.$el.getAttribute('class')).toContain('development');
}); });
describe('loadRequestDetails', () => {
beforeEach(() => {
spyOn(vm.store, 'addRequest').and.callThrough();
});
it('does nothing if the request cannot be tracked', () => {
spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
vm.loadRequestDetails('123', 'https://gitlab.com/');
expect(vm.store.addRequest).not.toHaveBeenCalled();
});
it('adds the request immediately', () => {
vm.loadRequestDetails('123', 'https://gitlab.com/');
expect(vm.store.addRequest).toHaveBeenCalledWith(
'123',
'https://gitlab.com/',
);
});
it('makes an HTTP request for the request details', () => {
spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
vm.loadRequestDetails('456', 'https://gitlab.com/');
expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
'/-/peek/results',
'456',
);
});
});
}); });
import axios from '~/lib/utils/axios_utils';
import performanceBar from '~/performance_bar';
import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
import MockAdapter from 'axios-mock-adapter';
describe('performance bar wrapper', () => {
let mock;
let vm;
beforeEach(() => {
const peekWrapper = document.createElement('div');
peekWrapper.setAttribute('id', 'js-peek');
peekWrapper.setAttribute('data-env', 'development');
peekWrapper.setAttribute('data-request-id', '123');
peekWrapper.setAttribute('data-peek-url', '/-/peek/results');
peekWrapper.setAttribute('data-profile-url', '?lineprofiler=true');
document.body.appendChild(peekWrapper);
mock = new MockAdapter(axios);
mock.onGet('/-/peek/results').reply(
200,
{
data: {
gc: {
invokes: 0,
invoke_time: '0.00',
use_size: 0,
total_size: 0,
total_object: 0,
gc_time: '0.00',
},
host: { hostname: 'web-01' },
},
},
{},
);
vm = performanceBar({ container: '#js-peek' });
});
afterEach(() => {
vm.$destroy();
mock.restore();
});
describe('loadRequestDetails', () => {
beforeEach(() => {
spyOn(vm.store, 'addRequest').and.callThrough();
});
it('does nothing if the request cannot be tracked', () => {
spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
vm.loadRequestDetails('123', 'https://gitlab.com/');
expect(vm.store.addRequest).not.toHaveBeenCalled();
});
it('adds the request immediately', () => {
vm.loadRequestDetails('123', 'https://gitlab.com/');
expect(vm.store.addRequest).toHaveBeenCalledWith(
'123',
'https://gitlab.com/',
);
});
it('makes an HTTP request for the request details', () => {
spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
vm.loadRequestDetails('456', 'https://gitlab.com/');
expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
'/-/peek/results',
'456',
);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment