Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preload Directives Generation not working #2460

Closed
3 tasks done
josepot opened this issue Mar 10, 2021 · 3 comments
Closed
3 tasks done

Preload Directives Generation not working #2460

josepot opened this issue Mar 10, 2021 · 3 comments

Comments

@josepot
Copy link

josepot commented Mar 10, 2021

Describe the bug

I just migrated a TS React project from CRA to Vite and I'm very happy with the results. However, the index.html file doesn't have the <link rel="preload"> entries for the dynamic imports. At first, I thought that maybe the issue had to do with the fact that I was using React.lazy. However, after refactoring my code to look like this:

const mod = import("./LiveRatesCore")
const LiveRatesCore = lazy(() => mod)

the chunks for those dynamic imports are still not present into the index.html file. They all get loaded immediately after the main chunk gets parsed, of course.

I think that this is a bug, because the docs state that:

Vite automatically generates <link rel="modulepreload"> directives for entry chunks and their direct imports in the built HTML.

Reproduction

In order to isolate the issue, I've created this small repo with npm init @vitejs/app test-dynamic-imports-vite-react-ts -- --template react-ts, then I've added a basic component named DynamicComponent.tsx and I have imported that component from the root component like this:

const mod = import("./DynamicComponent");
const DynamicComponent = lazy(() => mod);

then after running npm run build && cat ./dist/index.html we can appreciate that the index.html is missing an entry that should look more or less like this: <link rel="preload" as="script" href="/assets/DynamicComponent.7f9cf23b.js">.

The actual project where I found this issue is this one, but that one is a lot more complex.

System Info

  • vite version: 2.0.5
  • Operating System: linux-x64
  • Node version: v14.15.1
  • Package manager (npm/yarn/pnpm) and version: npm@7.6.0

Logs (Optional if provided reproduction)

> dynamic-imports-react-ts@0.0.0 build
> tsc && vite build "--debug"

  vite:config bundled config file loaded in 101ms +0ms
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'alias',
  vite:config     'react-refresh',
  vite:config     'vite:dynamic-import-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'rollup-plugin-dynamic-import-variables',
  vite:config     'vite:import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:terser',
  vite:config     'vite:reporter'
  vite:config   ],
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge16', 'firefox60', 'chrome61', 'safari11' ],
  vite:config     polyfillDynamicImport: true,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: false,
  vite:config     rollupOptions: {},
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     minify: 'terser',
  vite:config     terserOptions: {},
  vite:config     cleanCssOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: false,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     brotliSize: true,
  vite:config     chunkSizeWarningLimit: 500
  vite:config   },
  vite:config   configFile: '/home/josep/Projects/dynamic-imports-react-ts/vite.config.ts',
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: undefined,
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     build: {}
  vite:config   },
  vite:config   root: '/home/josep/Projects/dynamic-imports-react-ts',
  vite:config   base: '/',
  vite:config   resolve: { dedupe: undefined, alias: [ [Object] ] },
  vite:config   publicDir: '/home/josep/Projects/dynamic-imports-react-ts/public',
  vite:config   command: 'build',
  vite:config   mode: 'production',
  vite:config   isProduction: true,
  vite:config   optimizeCacheDir: '/home/josep/Projects/dynamic-imports-react-ts/node_modules/.vite',
  vite:config   server: {},
  vite:config   env: { BASE_URL: '/', MODE: 'production', DEV: false, PROD: true },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen]
  vite:config   },
  vite:config   createResolver: [Function: createResolver]
  vite:config } +3ms
vite v2.0.5 building for production...
✓ 23 modules transformed.
dist/assets/favicon.17e50649.svg           1.49kb
dist/assets/logo.ecc203fb.svg              2.61kb
dist/index.html                            0.51kb
dist/assets/DynamicComponent.7f9cf23b.js   0.27kb / brotli: 0.15kb
dist/assets/index.76acfdae.css             0.76kb / brotli: 0.40kb
dist/assets/index.0bfbc426.js              2.36kb / brotli: 0.94kb
dist/assets/vendor.6ad4fa42.js             127.23kb / brotli: 36.05kb
@yyx990803
Copy link
Member

This is expected: Vite only generates preload directives for direct imports. If Vite preloads every lazy/dynamic import, then they are not "lazy" anymore. Imagine you have an app with 100 lazy routes, you definitely don't want to preload all of them upfront for mobile users.

You can, however, write a plugin with the transformIndexHtml hook to inject additional preload directives for dynamic imports yourself.

@josepot
Copy link
Author

josepot commented Mar 15, 2021

Hi and thanks for looking into this @yyx990803! However, I think that I didn't explained myself very well.

If Vite preloads every lazy/dynamic import, then they are not "lazy" anymore. Imagine you have an app with 100 lazy routes, you definitely don't want to preload all of them upfront for mobile users.

I'm not suggesting that Vite should preload every lazy dynamic import.

What I'm suggesting is that it preloads only those ones that are eagerly required from the root of the main chunk.

For instance, consider this example:

import { Suspense, useState } from "react"

const mod = import("./LoadAsap")
const LoadAsap = lazy(() => mod)

const LoadWhenActive = lazy(() => import("./LoadWhenActive"))

export default Main: React.FC = () => {
  const [isActive, setIsActive] = useState(false)
  return (
    <div>
      <Suspense fallback={null}>
        <LoadAsap />
      </Suspense>
      <Suspense fallback={null}>
        {isActive ? <LoadWhenActive /> : null}
      </Suspense>
      {!isActive ? (
        <button
          onClick={() => {
            setIsActive(true)
          }}
        >
          Load
        </button>
      ) : null}
    </div>
  )
}

Let's say that this is the root component and that therefore it's part of the main chunk. This component is using 2 different lazy imports: LoadAsap and LoadWhenActive.

IMO what would be ideal is that Vite would preload the chunk for the LoadAsap component, since that one is being eagerly requested from the root (const mod = import("./LoadAsap")). However, the LoadWhenActive component -which is not eagerly requested from the root- shouldn't be preloaded.

You can, however, write a plugin with the transformIndexHtml hook to inject additional preload directives for dynamic imports yourself.

Thanks! this is useful! I already found a way to "hack" this rollup plugin for my own needs, but I may end up writing my own plugin for this. Thanks anyways!

@a-tonchev
Copy link

Hi @josepot can you share you solution, and how did you manage to tellwhich chunks to be shared? I also need something similar for the 5 top chunks used. Thanks in advance

@github-actions github-actions bot locked and limited conversation to collaborators Jul 22, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants