Svelte: Route navigation deactivates Svelte apps async loading

Created on 14 May 2019  Â·  12Comments  Â·  Source: sveltejs/svelte

Hi all,

I have created a very basic Sapper project that is supposed to load Svelte apps asynchronously at runtime (svelte v3.0.0 and sapper v0.26).

All works great when the Svelte apps (very basic Svelte template projects) are loaded async for the first time (they show up correctly in the Sapper host page). Unfortunately, subsequent navigation back and forth from and to the routes used to load the two sample apps will not dynamically load them anymore (no async requests are being made at all).

Apparently, this is not a Sapper issue because I can reproduce the same behaviour by using a different Svelte routing library like svelte-routing.

I couldn't set up a demo by using REPL, but I have created a very simple Sapper project on Github: sapper-dynamic-import.

Here is the code that loads the Svelte apps asynchronously:

<script context="module">
    export async function preload({ params, query }) {
        return { appName: params.app };
    }
</script>

<script>
    import { onMount } from 'svelte';

    let App;
    export let appName;

    onMount(() => {
        console.log('the component has mounted');
        loadApp();
    });

    async function loadApp() {  
        const app = `./Apps/${appName}/bundle.js`;
        ({ default: App } = await import(app));
    }    
</script>

<div id="AppHost">
    <svelte:component this={App}/>
</div>

Most helpful comment

@rzvdaniel I don't know why it didn't work for you when you exported the Svelte component itself — it should!

Here's a working demo for you, featuring apps being loaded both via preload (so it works both client-side and during server-side rendering) and a version that works purely at runtime. Note that apps have been built separately with rollup.apps.config.js.

Github isn't really the place for support questions like this, so I'm going to close this issue. Feel free to reach out at Discord!

All 12 comments

Instead of loading them onMount, you want to do it anytime appName changes. Depending on the routing solution you use (including Sapper), they won't recreate a route to pass in new parameters, instead just updating the properties.

So instead of onMount, try something like $: loadApp(appName).

Very interesting. Have you tried loading a .svelte file dynamically instead of a compiled JavaScript file? That seems to work.

@mrkishi
I have just tried what you have suggested without success :(

@EmilTholin
That was working for me too. When loading a Svelte app (.svelte or compiled) into a page that doesn't change (using an event as you did in your example), it works. But as soon as you are using my scenario of loading an app after navigating to page (route), it only works first time.

More, even my scenario works when the "target" of incoming apps is "body". If the "target" is hosted on the destination page (route), it is not working anymore. It may be the "target" on the fault?

P.S.
I am not using .svelte files because I want the flexibility of adding the apps (independent compiled javascript logic) at runtime! 🗡 :D

Best!

What kind of errors do you get?

@mrkishi

There is no error related to the app loading (you can check by yourself when running the example in the repository I have created).

However, there is an error in console that happens all the time (even when the apps are loading successfully): GET http://localhost:35729/livereload.js?snipver=1 net::ERR_CONNECTION_REFUSED

Additionally, if I navigate manually to the route (i.e. from the browser's address bar) the apps are correctly loaded.

Best!

Oh, I just realized your bundle includes a main file initializing the component, it's not the plain App.svelte output. That wouldn't work, since once you import it it just appends itself to the target (it's not really using <svelte:component>).

You want to compile your apps' bundles with a single export default from './App.svelte' instead.

@mrkishi

Thank you, Mauricio.

You are right about the target: if I remove <svelte:component> from the App Host page ([app].svelte), it still displays the loaded component the first time I navigate to the route.

I have tried to replace the entire main.js content of one of the apps with export default './App.svelte', but still facing the same behaviour. Maybe you have an example that works? :P

I thought I can create an independent Svelte application which I can develop and test in isolation (in this case just a default Svelte Template), and load it later asynchronously in a Sapper project (or any other Svelte project) with the help of <svelte:component> (even 'target' :P). Is this possible?

Best!

Thank you again Mauricio for revealing that in this scenario <svelte:component> is not used at all and the compiled Svelte apps are injected directly to the target.

I have found that if I redirect to the route, the Svelte apps are correctly injected to the all the time. :)

<ul>
    <li><a href="/app/hellosvelte" on:click={loadApp}>Hello Svelte</a></li>
    <li><a href="/app/helloworld" on:click={loadApp}>Hello World</a></li>
</ul>

<script>
  async function loadApp(event) {
    window.location = event.target.href;
  }
</script>

But anyone has a clue why using the normal links to navigate to the route doesn't work?

<ul>
    <li><a href="/app/hellosvelte">Hello Svelte</a></li>
    <li><a href="/app/helloworld">Hello World</a></li>
</ul>

Best!

Are /app/hellosvelte and /app/helloworld links that Sapper is intercepting the click for and attempting to do its own client-side navigation to, and is that causing a problem? If you need to prevent Sapper from attempting to handle a link, you can put target="_self" on it.

Hi Conduitry!

Thank you, that's it.

The target="_self" works exactly as the code using the on:click and javascript redirection above and the compiled Svelte apps are loading all the time, as I wish. 😃

I have noticed two more things and I'm not aware if this is normal or not.

  1. Even if the apps are loading just fine on the client side, now I can see the following error on the server side.
    It is now a server route being triggered along with the client side one?
(node:8900) UnhandledPromiseRejectionWarning: Error: Cannot find module './Apps/hellosvelte/bundle.js'
Require stack:
C:\Work\Github\sapper-dynamic-import\__sapper__\dev\server\server.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:610:15)
    at Function.Module._load (internal/modules/cjs/loader.js:526:27)
    at Module.require (internal/modules/cjs/loader.js:666:19)
    at require (internal/modules/cjs/helpers.js:16:16)
    at loadApp (C:\Work\Github\sapper-dynamic-import\__sapper__\dev\server\server.js:388:45)
  1. In the browser's Network tab, I can now see that everything is being loaded when navigation happens.
    This may be normal? :P
client.397208a1.js
client.397208a1.js
global.css
main.1279100824.css
global.css
main.1279100824.css
chunk.fdfc8cf8.js
chunk.fdfc8cf8.js
sapper-dev-client.66640646.js
sapper-dev-client.66640646.js
[app].55809b0f.js
[app].55809b0f.js
__sapper__
bundle.js
bundle.js
livereload.js?snipver=1
manifest.json
manifest.json
logo-192.png
logo-192.png
service-worker.js

Thanks!

@rzvdaniel I don't know why it didn't work for you when you exported the Svelte component itself — it should!

Here's a working demo for you, featuring apps being loaded both via preload (so it works both client-side and during server-side rendering) and a version that works purely at runtime. Note that apps have been built separately with rollup.apps.config.js.

Github isn't really the place for support questions like this, so I'm going to close this issue. Feel free to reach out at Discord!

Thank you, Mauricio.

This is what I was looking for. See you on Discord! :)

Best!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  Â·  3Comments

robnagler picture robnagler  Â·  3Comments

angelozehr picture angelozehr  Â·  3Comments

matt3224 picture matt3224  Â·  3Comments

sskyy picture sskyy  Â·  3Comments