作为第一步,我的测试应用程序由 2 个文件组成:
- 公开 doSomething() 函数的Comp
- 需要尽快在Comp中触发 doSomething() 的应用程序(例如,在应用程序开始时,无需用户执行任何操作)。
据我所知,这可以像下面这样完成(这里欢迎批评:模式、耦合、有效性等)
第 1 部分(一些上下文)
App.svelte
<script>
import Comp from './Comp.svelte';
let comp;
function handleChildCompMounted(){
comp.doSomething();
}
</script>
<Comp bind:this=comp on:mounted={handleCompMounted}/>
Comp.svelte
<script>
import {onMount, createEventDispatcher} from 'svelte';
const dispatch = createEventDispatcher();
onMount(()=>dispatch('mounted'));
export function doSomething(){ ... }
</script>
...
第 2 部分(真正的问题)
现在的问题是Comp使用了一个外部库,在能够执行任何操作之前需要加载该库。 换句话说,对于App调用Comp.doSomething()
,他必须确保Comp完全正常运行,也就是说:
- 已安装
- 加载库是为了Comp能够在
Comp.doSomething()
中使用它
为了做到这一点,我将引入一个LibraryLoader类,该类将负责加载库(无需加载 N 次,管理超时等),用于需要加载此库才能运行的应用程序的所有潜在其他组件。随意评论下面的代码,因为老实说,我认为这是我在 javascript 中的第一堂或第一堂课之一:D
const FAKE_LOAD_TIME = 3000;
const TIMEOUT = 5000;
export class LibraryLoader{
#library_state = 'NOT_LOADED'; // NOT_LOADED || LOADING || LOADED
#totalWaitTime = 0;
isLoaded(){
return this.#library_state === 'LOADED';
}
async getLibrary(){
console.log('** getLibrary() called **');
if(this.#library_state === 'LOADING'){
while (this.#library_state !== 'LOADED') {
console.log('** waiting 1sec ... **');
await this.#wait(1000);
this.#totalWaitTime += 1000;
if (this.#totalWaitTime > TIMEOUT) {
console.log('Timeout!!! Library failed to load');
library_state = 'FAILED';
break;
}
}
}
if (this.#library_state === 'NOT_LOADED') {
this.#library_state = 'LOADING';
console.log('** Loading Library **');
await LibraryLoader.#loadLibrary();
this.#library_state = 'LOADED';
console.log('** Library is Loaded** ');
dispatchEvent(new Event('loaded'));
}
}
static async #loadLibrary(){
window.myLib = null;
await LibraryLoader.#wait(FAKE_LOAD_TIME);
window.myLib = {name:"MY_LIBRARY"};
}
static #wait(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
}
现在Comp尝试使用LibraryLoader类尽快加载库,然后...在这里你可以看到两件事:
我用于检查库是否已加载的反应语句不起作用。(我不知道如何在这个LibraryLoader的上下文中创建和调度事件,就像我们对 DOM 事件所做的那样)
我没有找到在代码中的相关位置编写调度("ready")的方法。
<script>
import {onMount} from 'svelte';
import {LibraryLoader} from './libraryLoader.js';
console.log('Comp is asking for the Library');
const libLoader = new LibraryLoader();
libLoader.getLibrary();
$: { // REACTIVITY NOT WORKING HERE
libLoader.isLoaded()
? console.log('library available for Comp')
: console.log('library NOT available for Comp');
}
onMount(() => {
console.log('Comp is mounted');
});
</script>
我现在需要做的最后一件事是在挂载Comp调度时以及加载他所依赖的库时使它成为"就绪">事件。这个"ready"事件将是其他组件的触发器,这些组件需要调用Comp已准备好的Comp函数。
我正在分享一个 REPL 代码,这对你们来说会更方便:
https://svelte.dev/repl/cca38d8a3ac04cf4a356cc4ed5e336d1?version=3.46.2
再次感谢您的帮助:)
编辑 24/01/2022
对于那些感兴趣的人,我引入了一个调度程序类来搁置调度未附加到 DOM elem 的事件的问题。因此,在下面的 REPL 中,您将看到 @Bob Fanger 强调的问题,即尝试同步组件之间状态的复杂性和时间。
https://svelte.dev/repl/d3d48eb4106c4b24b984c4b70fd32271?version=3.46.2
使用插槽可能会很有趣,有条件地使用另一个组件以达到特定组件的可用性。 在下面的 REPL 中,我们做了两件事:
Comp 最终自己触发了 doSomething,而不是将这个责任委托给 App。这稍微解耦了代码。
应用程序显示使用 Comp 槽来显示消息或使用另一个组件,这取决于 Comp 的就绪情况。
https://svelte.dev/repl/ef7876d4fa8847d1ad1e13eb616b95fa?version=3.46.2
libLoader 没有重新分配,因此 Svelte 不会重新评估libLoader.isLoaded()
export let loaded = false;
loadLibrary.then(() => {
loaded = true;
)
在这里,loaded
被重新分配,svelte能够检测到变化。export
允许<Comp bind:loaded
.
除了loaded = true
,您还可以调度事件:
loadLibrary.then(() => {
dispatch('loaded')
)
使用双向绑定状态或事件是解决方案,但不是在应用和 Comp 之间保持状态同步,而是使用<slot>
在 Comp.svelte 中:
{#if loaded}
<slot />
{/if}
in App.svelte
<Comp>
<SomeComponent />
</Comp>
使用插槽方法可以简化代码,因为当创建某个组件时,可以保证加载库。
(而不是使用loaded
变量<#await libLoader.getLibrary()>
也可能)
应用程序","库"和"比较"实际上是抽象的术语。有多种解决方案可用,请尝试看看哪种解决方案最适合您的用例。