Download JS hook file after loading LiveView component

Hi friends, I do not know it is about Phoenix or LiveView. for example you have a component that uses hook in js and this hook has more than 200 lines and you want to separate its file from app.js.

But you do not want to download or import this hook js file before the page that uses this component is loaded.

Because it is admin panel and has more than 200 components like this and some of the users maybe do not see some pages that have these components

Now what is the solution? How can I create package that each component has its js files and when I need they are loaded!

It should be noted, page is under LiveView router and these components are for LiveView. users do not full reload or every page refresh.

I think to add new hook to Hook object I need to add to window? am I wrong?

Why I am asking this question?

I did not find any bundler for LiveView js to handle it automatically in document!

The only idea I have like this:

Create a Hook in my component and dispatch an event to client and inside the client I run dynamic import like this

import('./x.js')
  .then((module) => {
    module.someFunction();
  })
  .catch((err) => {
    console.error('Error loading module:', err);
  });

Thank you in advance

5 Likes

Since the JS part is using the esbuild for building the frontend, you might want to look at how to configure it appropriately: How to lazy load with esbuild

PS: This might not be possible with esbuild CLI that is generated by default in phoenix apps, it is possible that you will need to use the esbuild directly from JS. Sadly I don’t have examples on hand on how to do that, but there might be on this forum somewhere if you search.

Thank you I will see the page and try. I hope the other developers give me their solutions like adding js to head and prevent duplication adding js file and update hook object etc.

Because I see the way I find in the phoenix LiveView if you want to support client side as well as a project in js framework!! is not going to be good especially you have many stateful component

If I do this I need to send web socket request for each component when is mounted, I do not think it is a good way, if not do this I should import all my components at first of my app running

You don’t want to lazy load hooks. Phoenix would just consider the hook missing I guess. Instead make the hook handle the async livecycle of loading any heavy dependency it depends on. As for when it makes sense to split out heavy dependencies (tradeoff of more requests, but less data per request) I suggest reading The Three Cs: 🤝 Concatenate, 🗜️ Compress, 🗳️ Cache – Harry Roberts – Web Performance Consultant tdlr. It might be later than you think.

4 Likes

Before reading the post you linked, we imagine Hook is lazy and is not loaded until user calls, but what about our imports? for example we need to import 200 components index.ts files in app.js at first, it does not download?

If you use static imports, all imported modules will be downloaded on initial load in app.js

A lazy hook should not need to import anything before being mounted.

Hooks.VideoPlayer = {
    mounted() {
        import("./hooks/video-player").then(
            ({ setupVideoPlayer }) => {
                setupVideoPlayer(this, this.el);
            }
        )
    }
}

In the esbuild config:

  default: [
    args: ~w(js/app.js js/hooks/video-player.js
      --chunk-names=chunks/[name]-[hash] --splitting --format=esm --bundle --target=es2017
      --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
3 Likes

Yes as the blog post @D4no0 suggested. but thank you for full esbuild config I will try it :pray:t2::pray:t2::pray:t2:.

I think I should improve this part of app to get file address in a main hook to import dynamically, because it is about 200 js files for example and I can not force programmer to create hook for each component like this. there should be some whitelist paths to be imported, and when my component is executed, sends command to load x component js file.

My goal to create ui package

Couple thoughts on this:

  • one hook per component is a LOT of hooks, relative to most LiveView code I’ve encountered
  • loading code on-demand makes the bundle size smaller on first load, but it also means that parts of the UI aren’t going to work until another request has completed. That either means additional complexity (to indicate to the user that things aren’t ready) or user confusion.
1 Like

Not for all components, just for the components need hook.

We can wait for load the js that is very small size and notice the backend if the data depend to ui at first!!

I think something is missed here!

Only part of the component becomes Hook, which is related to complex state management. And the component itself is created by Phoenix component and live component in a hybrid form, and if there is a problem to initialize the initial data like other frameworks, you can use the skeleton.

And as far as I know, it is only imported once in the browser, and the second time it is imported again, the browser takes it from the cache.