페이지마다 설치되어야 하는 Svelte 앱을 개발하고 있습니다. JS 파일을 head 섹션에 추가하고 첫 번째 페이지 로드 시 로드되도록 하는 방법을 알아냈습니다. 하지만 실험을 해보면서 discourse가 XHR를 통해 새로운 콘텐츠를 가져와 특정 섹션을 교체한다는 사실을 깨달았고, 이로 인해 새 페이지가 로드될 때 제 앱이 다시 초기화되지 않는 문제가 발생했습니다.
페이지가 변경될 때 알림을 받기 위해 여러 가지 시도를 해보았지만, Ember가 이러한 훅을 제공하지 않는 것으로 보이며, 경청할 수 있는 커스텀 이벤트도 찾을 수 없었습니다.
한 가지 방법은 DOM에 mutation observer를 추가하여 변경 사항을 관찰하는 것입니다. "#topic div가 다시 로드되는(속성이 변경되므로 속성 변경만 관찰하면 됩니다) 요소인 것을 발견했습니다. 저는 그곳에 mutation observer를 설정했습니다(mutation observer는 DOM 변경 사항을 관찰하는 비교적 새로운 방식으로, 성능이 좋습니다). 작동 원리는 변경 사항을 관찰하고, 변경이 발생하면 해당 페이지의 Svelte 앱을 다시 로드하기 위해 콜백을 실행하는 것입니다.
이 접근 방식을 마음에 들어 하고, 피드백을 받고 싶습니다.
한 가지 질문: 대신 URL 변경 사항을 관찰해야 하나요? popstate에 대한 리스너를 등록하는 것이 더 나은 아이디어일까요?
사용하려면 테마의 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" ></link>
그런 다음 라이브러리 내부에서 on-discourse를 다음과 같이 호출할 수 있습니다:
function log(...msg) {
console.log('svelte', ...msg);
}
// This is the Svelte install code
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);
})();
기본적으로 콜백(여러 번 실행하여 여러 콜백을 설치할 수 있음)과 함께 window.onDiscourse(callback)를 호출하기만 하면 되고, 변경 사항이 발생하면 해당 콜백이 실행되어 앱을 초기화합니다.
다음은 on-discourse.js의 전체 코드입니다. (수정: #topic를 사용하도록 업데이트했는데, 페이지 로드 시 속성이 변경되므로 관찰하기에 좋은 대상인 것 같습니다. 그러면 mutation은 #main-outlet의 DOM 트리 전체를 살펴보는 대신 속성 변경 사항만 관찰하면 됩니다.)
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');