Ich habe eine Svelte-App geschrieben, die sich auf jeder Seite selbst installieren muss. Ich habe herausgefunden, wie ich meine JS-Datei in den Head-Bereich einfügen und sie beim ersten Laden der Seite laden kann. Bei meinen Experimenten stellte ich jedoch fest, dass Discourse neue Inhalte über XHR abruft und bestimmte Abschnitte ersetzt, sodass meine App bei jedem Laden einer neuen Seite nicht neu initialisiert wurde.
Ich habe verschiedene Versuche unternommen, um benachrichtigt zu werden, wenn sich die Seite ändert, aber es scheint, dass Ember keine Hooks bereitstellt, und ich konnte keine benutzerdefinierten Ereignisse finden, auf die ich hören konnte.
Es scheint, dass eine Möglichkeit darin besteht, einen Mutationsbeobachter zum DOM hinzuzufügen und auf Änderungen zu achten. Ich habe festgestellt, dass das „#topic div“ dasjenige zu sein scheint, das neu geladen wird (und die Attribute sich ändern, sodass man nur auf Attributänderungen achten kann). Ich habe dort einen Mutationsbeobachter eingerichtet (Mutationsbeobachter sind der neuere, performante Weg, um DOM-Änderungen zu beobachten). Die Funktionsweise besteht darin, auf Änderungen zu achten und bei deren Auftreten einen Callback auszuführen, um meine Svelte-App für diese Seite neu zu laden.
Dieser Ansatz gefällt mir und ich hätte gerne Feedback dazu.
Eine Frage: Sollte ich stattdessen auf Änderungen an der URL achten? Ist das eine bessere Idee, einen Listener für popstate zu registrieren?
Um es zu verwenden, machen Sie etwas Ähnliches in Ihrem Theme/Head:
<script src="https://files.extrastatic.dev/community/on-discourse.js"></script>
<script src="https://files.extrastatic.dev/community/index.js"></script>
<link rel="stylesheet" type="text/css" href="https://files.extrastatic.dev/community/index.css">
Dann können Sie innerhalb Ihrer Bibliothek on-discourse wie folgt aufrufen:
function log(...msg) {
console.log('svelte', ...msg);
}
// Dies ist der Svelte-Installationscode
function setup() {
try {
const ID = 'my-special-target-id';
log('Inside setup()');
const el = document.getElementById(ID);
if (el) {
log('Removed existing element', ID);
el.remove();
}
const target = document.createElement("div");
target.setAttribute("id", ID);
log('Created target');
document.body.appendChild(target);
log('Appended child to body');
const app = new App({
// eslint-disable-next-line no-undef
target
});
log('Created app and installed');
} catch(err) {
console.error('Unable to complete setup()', err.toString() );
}
}
(function start() {
log('Starting custom Svelte app');
// Re-install on changes
window.onDiscourse && window.onDiscourse( setup );
// Load the app the first page load
window.addEventListener('load', () => {
setup();
});
log('Finished custom Svelte app);
})();
Im Grunde rufen Sie einfach window.onDiscourse(callback) mit Ihrem Callback auf (und Sie können ihn mehrmals ausführen, um mehrere Callbacks zu installieren), und wenn dann eine Mutation auftritt, wird dieser Callback ausgeführt, um Ihre App zu initialisieren.
Hier ist der vollständige Code für on-discourse.js. (Bearbeitet: Ich habe dies aktualisiert, um #topic zu verwenden, was eine gute Sache zu beobachten zu sein scheint, da sich die Attribute beim Laden der Seite ändern und die Mutation dann nur auf Attributänderungen achten muss, anstatt den gesamten DOM-Baum nach #main-outlet zu durchsuchen)
let mutationObservers = [];
function log(...msg) {
console.log("on-discourse", ...msg);
}
function observeMainOutlet() {
log('Observing main outlet');
// Select the node that will be observed for mutations
const targetNode = document.getElementById("topic");
if (targetNode) {
// Options for the observer (which mutations to observe)
const config = { attributes: true };
// create an observer instance to reset when childList changes
const observer = new MutationObserver(function(mutations) {
let reset = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes') {
log('Found main-outlet mutation, running callbacks');
mutationObservers.forEach( (s,i) => {
try {
log(`Running div callback ${i+1}`);
s();
log(`Finished div callback ${i+1}`);
} catch( err ) {
log(`Div callback error (${i+1})`, err );
}
});
}
});
});
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
// observer.disconnect();
log('Done with outlet observer');
} else {
console.error('on-discourse FATAL ERROR: Unable to find main-outlet');
}
}
window.addDiscourseDivMutationObserver = (cb) => {
log('Adding on-discourse div mutation callback');
mutationObservers.push(cb);
log('Added on-discourse div mutation callback');
}
window.addEventListener("load", () => {
log('Setting up topic observer');
if (mutationObservers.length > 0) {
observeMainOutlet();
}
log('Created topic observer');
});
log('Completed setup of on-discourse.js');