DEV: Improve built-in browser performance marks/measurements (#26758)
- Rename `discourse-booted` to 'discourse-init' (because 'booted' makes it sound like boot was finished. When in fact, it was just starting) - Introduce `discourse-paint`, which is fired after the Ember application has been painted to the screen by the browser. This happens slightly after DOMContentLoaded - Add a `performance.measure` call to link those two marks, so they're easily visible in performance traces Also removes an ember boot-order workaround which is no longer required.
This commit is contained in:
parent
d937f5b098
commit
6bfc81978c
|
@ -1,6 +1,8 @@
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
import runAfterFramePaint from "discourse/lib/after-frame-paint";
|
||||||
|
import { isTesting } from "discourse-common/config/environment";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
@ -123,4 +125,24 @@ export default Controller.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
trackDiscoursePainted() {
|
||||||
|
if (isTesting()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runAfterFramePaint(() => {
|
||||||
|
performance.mark("discourse-paint");
|
||||||
|
try {
|
||||||
|
performance.measure(
|
||||||
|
"discourse-init-to-paint",
|
||||||
|
"discourse-init",
|
||||||
|
"discourse-paint"
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn("Failed to measure init-to-paint", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Runs `callback` shortly after the next browser Frame is produced.
|
||||||
|
* ref: https://webperf.tips/tip/measuring-paint-time
|
||||||
|
*/
|
||||||
|
export default function runAfterFramePaint(callback) {
|
||||||
|
// Queue a "before Render Steps" callback via requestAnimationFrame.
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
// MessageChannel is one of the highest priority task queues
|
||||||
|
// which will be executed after the frame has painted.
|
||||||
|
const messageChannel = new MessageChannel();
|
||||||
|
|
||||||
|
// Setup the callback to run in a Task
|
||||||
|
messageChannel.port1.onmessage = callback;
|
||||||
|
|
||||||
|
// Queue the Task on the Task Queue
|
||||||
|
messageChannel.port2.postMessage(undefined);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<DStyles />
|
<DStyles />
|
||||||
<DVirtualHeight />
|
<DVirtualHeight />
|
||||||
|
|
||||||
<DiscourseRoot>
|
<DiscourseRoot {{did-insert this.trackDiscoursePainted}}>
|
||||||
<a href="#main-container" id="skip-link">{{i18n "skip_to_main_content"}}</a>
|
<a href="#main-container" id="skip-link">{{i18n "skip_to_main_content"}}</a>
|
||||||
<DDocument />
|
<DDocument />
|
||||||
<PageLoadingSlider />
|
<PageLoadingSlider />
|
||||||
|
|
|
@ -3,19 +3,12 @@
|
||||||
throw "Unsupported browser detected";
|
throw "Unsupported browser detected";
|
||||||
}
|
}
|
||||||
|
|
||||||
// In Ember 3.28, the `ember` package is responsible for configuring `Helper.helper`,
|
|
||||||
// so we need to require('ember') before setting up any helpers.
|
|
||||||
// https://github.com/emberjs/ember.js/blob/744e536d37/packages/ember/index.js#L493-L493
|
|
||||||
// In modern Ember, the Helper.helper definition has moved to the helper module itself
|
|
||||||
// https://github.com/emberjs/ember.js/blob/0c5518ea7b/packages/%40ember/-internals/glimmer/lib/helper.ts#L134-L138
|
|
||||||
require("ember");
|
|
||||||
|
|
||||||
let element = document.querySelector(
|
let element = document.querySelector(
|
||||||
`meta[name="discourse/config/environment"]`
|
`meta[name="discourse/config/environment"]`
|
||||||
);
|
);
|
||||||
const config = JSON.parse(
|
const config = JSON.parse(
|
||||||
decodeURIComponent(element.getAttribute("content"))
|
decodeURIComponent(element.getAttribute("content"))
|
||||||
);
|
);
|
||||||
const event = new CustomEvent("discourse-booted", { detail: config });
|
const event = new CustomEvent("discourse-init", { detail: config });
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
document.addEventListener("discourse-booted", (e) => {
|
document.addEventListener("discourse-init", (e) => {
|
||||||
performance.mark("discourse-booted");
|
performance.mark("discourse-init");
|
||||||
const config = e.detail;
|
const config = e.detail;
|
||||||
const app = require(`${config.modulePrefix}/app`)["default"].create(config);
|
const app = require(`${config.modulePrefix}/app`)["default"].create(config);
|
||||||
app.start();
|
app.start();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { setup } from "qunit-dom";
|
||||||
import setupTests from "discourse/tests/setup-tests";
|
import setupTests from "discourse/tests/setup-tests";
|
||||||
import config from "../config/environment";
|
import config from "../config/environment";
|
||||||
|
|
||||||
document.addEventListener("discourse-booted", () => {
|
document.addEventListener("discourse-init", () => {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
if (!EmberENV.TESTS_FILE_LOADED) {
|
if (!EmberENV.TESTS_FILE_LOADED) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
Loading…
Reference in New Issue