在 Svelte 组件中创建"ready"状态(=mount + 依赖库加载)



作为第一步,我的测试应用程序由 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完全正常运行,也就是说:

  1. 安装
  2. 加载库是为了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类尽快加载库,然后...在这里你可以看到两件事:

  1. 我用于检查库是否已加载的反应语句不起作用。(我不知道如何在这个LibraryLoader的上下文中创建和调度事件,就像我们对 DOM 事件所做的那样)

  2. 我没有找到在代码中的相关位置编写调度("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()>也可能)

">

应用程序","库"和"比较"实际上是抽象的术语。有多种解决方案可用,请尝试看看哪种解决方案最适合您的用例。

最新更新