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

Create a "ready" State (=mounted + dependent lib loaded) in a Svelte Component

作为第一步,我的测试应用程序由 2 个文件组成:

据我所知,这可以像下面那样完成(欢迎在这里提出批评:模式、耦合、有效性等)

第 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. Comp挂载
  2. Comp 加载库以便能够在 Comp.doSomething()
  3. 中使用它

为了做到这一点,我将介绍一个 LibraryLoader Class,它将负责加载库(无需加载它 N 次,管理超时等)用于应用程序的所有潜在其他组件,这些组件需要加载该库才能运行。请随意评论以下代码,因为老实说,我认为这是 javascript 中的第一个或第一个 classes :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 加载库 class 尽快和...在这里你可以看到两件事:

  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' 挂载时的事件,以及他所依赖的库也被加载时的事件。此 'ready' 事件将触发其他组件,这些组件需要调用 Comp 函数 Comp 准备好了。

我分享一个REPL代码,方便大家:

https://svelte.dev/repl/cca38d8a3ac04cf4a356cc4ed5e336d1?version=3.46.2

再次感谢您的帮助:)

编辑 24/01/2022

对于那些感兴趣的人,我引入了一个 Dispatcher class 来解决调度未附加到 DOM 元素的事件的问题。因此,在下面的 REPL 中,您会看到@Bob Fanger 强调的问题,即尝试在组件之间同步状态的复杂性和时间。

https://svelte.dev/repl/d3d48eb4106c4b24b984c4b70fd32271?version=3.46.2

使用插槽可能很有趣,可以根据特定组件的可用性有条件地使用另一个组件。 在下面的 REPL 中,我们做了两件事:

https://svelte.dev/repl/ef7876d4fa8847d1ad1e13eb616b95fa?version=3.46.2

libLoader 未重新分配,因此 Svelte 不会 re-evaluate libLoader.isLoaded()

export let loaded = false;
loadLibrary.then(() => {
  loaded = true;
)

此处 loaded 被重新分配,并且 svelte 能够检测到更改。 export 允许 <Comp bind:loaded.

除了loaded = true你还可以调度一个事件:

loadLibrary.then(() => {
  dispatch('loaded')
)

使用 two-way 绑定状态或事件是解决方案,但不是保持 App 和 Comp 之间的状态同步,一种方法是使用 <slot>s

在Comp.svelte中:

{#if loaded}
  <slot />
{/if}

在App.svelte

<Comp>
  <SomeComponent />
</Comp>

使用插槽方法可以简化代码,因为在创建 SomeComponent 时保证加载库。

(而不是使用 loaded 变量 <#await libLoader.getLibrary()> 也可能)

“App”、“Library”和“Comp”确实是抽象术语。有多种解决方案可用,尝试看看哪种解决方案最适合您的用例。