Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Loading Component

I am looking for a Svelte "Delegating Lazy Loading" component to another "real" component. This component should be transparent to the user who shouldn't know there is this "proxy" :

  • lazy-load the delegate (using a callback which dynamically import a js module)
  • Supports slots (slots should be forwarded to the delegate when it's ready)
  • Supports events (by forwarding subscriptions to the delegate)

I don't think it's possible now because no api is exposed for slots forwarding or events forwarding. Maybe a hack by implementing in js the same internal interface has a svelte component ?

Edit

I am looking for this kind of magic method:

I have an Heavy component, which I want to load asynchronously

Heavy.svelte:

<div on:click>
  <slot {secret}/>
  <slot name="footer"/>
</div>

<script>
  let secret = 'huhu';
</script>

I want to be able to export this component like this :

module.js

import { lazy } from './lazy.js'; // magic method

export let Heavy = lazy(async () => (await import('./Heavy.svelte')).default)

a consumer can then use Heavy without knowing it has been wrapped in this "high order" lazy component. this consumer doesn't have to handle/knowing anything about the asynchronous behavior of this wrapper :

Consumer.svelte

<Heavy on:click={() => console.log("clicked")} let:secret>
  <div>{secret}</div>
  <div slot="footer">Footer</div> 
</Heavy>

<script>
  import { Heavy } from './module.js';
</script>

I have a "working" solution, which doesn't support "let", doesn't support named slots, and doesn't support events..

like image 428
Jérémie B Avatar asked Nov 06 '22 06:11

Jérémie B


1 Answers

The best I can do. bind:* doesn't work, slots and events seem to work.

Wrapper.svelte

<svelte:component this={cpn} {...slotsProps} {...$$restProps} bind:this={instance}/>

<script>
    import { get_current_component } from 'svelte/internal';
    
    export let provider;
    
    const slotsProps = {"$$slots":$$props.$$slots, "$$scope":$$props.$$scope};
    const self = get_current_component();
    
    let cpn;
    let instance;
        
    provider().then(result => cpn = result);
    
    $: if (instance) {
        for (let [type, listeners] of Object.entries(self.$$.callbacks)) {
            instance.$on(type, (e) => {
                listeners.forEach(l => l(e));
            });
        }
    }

</script>

lazy.js

import Wrapper from './Wrapper.svelte';

export function lazy(provider) {
    return function(opts) {
        opts.props = {...opts.props, provider };
        return new Wrapper(opts)
    }
}

module.js (usecase example)

import { lazy } from './lazy.js';

export let Heavy = lazy(async () => (await import("./Heavy.svelte")).Heavy);
like image 55
Jérémie B Avatar answered Dec 07 '22 16:12

Jérémie B