Svelteアプリを各ページにインストールする必要があり、それを記述していました。JSファイルをヘッドセクションに追加し、最初のページロード時にロードする方法を見つけました。しかし、実験したところ、DiscourseはXHR経由で新しいコンテンツを取得し、特定のセクションを置き換えていることに気づきました。そのため、新しいページがロードされたときにアプリが再初期化されていませんでした。
ページが変更されたときに通知を受けるためのさまざまな試みをしましたが、Emberにはフックがないようで、リッスンできるカスタムイベントも見つかりませんでした。
1つの方法は、DOMにミューテーションオブザーバーを追加し、変更を監視することのようです。「#topic div」が再読み込みされる(そして属性が変更されるため、属性変更を監視するだけでよい)ことがわかりました。そこでミューテーションオブザーバーを設定しました(ミューテーションオブザーバーはDOMの変更を監視するための新しくパフォーマンスの高い方法です)。その仕組みは、変更を監視し、変更が発生したときにコールバックを実行して、そのページのSvelteアプリをリロードすることです。
このアプローチは気に入っており、フィードバックを求めています。
1つの質問:代わりにURLの変更を監視すべきでしょうか?popstateのリスナーを登録するのは良い考えでしょうか?
使用するには、テーマ/ヘッドで次のようなことを行います。
<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" >
次に、ライブラリ内で次のようにon-discourseを呼び出すことができます。
function log(...msg) {
console.log('svelte', ...msg);
}
// これはSvelteのインストールコードです
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');
// 変更時に再インストール
window.onDiscourse && window.onDiscourse( setup );
// 初回ページロード時にアプリをロード
window.addEventListener('load', () => {
setup();
});
log('Finished custom Svelte app);
})();
基本的に、コールバック(複数インストールするために複数回実行できます)とともにwindow.onDiscourse(callback)を呼び出すだけで、ミューテーションが発生すると、そのコールバックが実行されてアプリが初期化されます。
on-discourse.jsの完全なコードは次のとおりです。(編集:これを#topicを使用するように更新しました。これは、ページがロードされるときに属性が変更されるため、監視するのに良いことのようです。そして、ミューテーションはDOMツリー全体を検索するのではなく、属性の変更のみを監視する必要があります。)
let mutationObservers = [];
function log(...msg) {
console.log("on-discourse", ...msg);
}
function observeMainOutlet() {
log('Observing main outlet');
// ミューテーションを監視するノードを選択します
const targetNode = document.getElementById("topic");
if (targetNode) {
// オブザーバーのオプション(どのミューテーションを監視するか)
const config = { attributes: true };
// childListが変更されたときにリセットするオブザーバーインスタンスを作成します
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 );
}
});
}
});
});
// 設定されたミューテーションを監視するためにターゲットノードの監視を開始します
observer.observe(targetNode, config);
// 後で監視を停止できます
// 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');