在 Svelte 组件中创建 "ready" 状态(=mounted + 已加载依赖库)
Create a "ready" State (=mounted + dependent lib loaded) in a Svelte Component
作为第一步,我的测试应用程序由 2 个文件组成:
- Comp 谁公开了一个 doSomething() 函数
- App 谁需要在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 加载库以便能够在
Comp.doSomething()
中使用它
为了做到这一点,我将介绍一个 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 尽快和...在这里你可以看到两件事:
我用来检查库是否已加载的反应语句不起作用。 (我不知道如何在 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' 挂载时的事件,以及他所依赖的库也被加载时的事件。此 '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 中,我们做了两件事:
Comp 终于自己触发了 doSomething,而不是将此责任委托给 App。这有点解耦代码。
应用程序显示使用 Comp 槽来显示消息或使用另一个组件,这将取决于 Comp 的准备情况。
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”确实是抽象术语。有多种解决方案可用,尝试看看哪种解决方案最适合您的用例。
作为第一步,我的测试应用程序由 2 个文件组成:
- Comp 谁公开了一个 doSomething() 函数
- App 谁需要在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 加载库以便能够在
Comp.doSomething()
中使用它
为了做到这一点,我将介绍一个 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 尽快和...在这里你可以看到两件事:
我用来检查库是否已加载的反应语句不起作用。 (我不知道如何在 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' 挂载时的事件,以及他所依赖的库也被加载时的事件。此 '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 中,我们做了两件事:
Comp 终于自己触发了 doSomething,而不是将此责任委托给 App。这有点解耦代码。
应用程序显示使用 Comp 槽来显示消息或使用另一个组件,这将取决于 Comp 的准备情况。
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”确实是抽象术语。有多种解决方案可用,尝试看看哪种解决方案最适合您的用例。