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

Component assets missing from SSR render #64

Open
lwansbrough opened this issue Nov 6, 2022 · 4 comments
Open

Component assets missing from SSR render #64

lwansbrough opened this issue Nov 6, 2022 · 4 comments

Comments

@lwansbrough
Copy link

lwansbrough commented Nov 6, 2022

We're trying to setup SSR for Vue 2.7 as we move towards a future Vue 3 migration of a large Vue 2 codebase. We've moved from Webpack to Vite in the process, but we're seeing an issue with a FOUC on all pages in both the dev server and production builds.

During the template render, the SSR server does not appear to be picking up all the components that were walked during the render. I would expect these components to be collected into context._registeredComponents which appears to be what the template renderer is using to inject the CSS and scripts.

This functionality does not appear to be functioning properly. And because there isn't a createSSRApp equivalent for Vue 2.7, we're not able to collect the modules for doing our own template rendering either (as shown here.)

What's the recommended process for eliminating this FOUC for Vue 2.7 applications?

@lwansbrough
Copy link
Author

Tangentially related to #56

@lwansbrough
Copy link
Author

lwansbrough commented Nov 6, 2022

Actually I think I can see that this is todo.. https://github.com/vitejs/vite-plugin-vue2/blob/main/src/main.ts#L135 -- would really appreciate work towards this, or guidance on how we can contribute or develop our own solution.

@lwansbrough
Copy link
Author

So I patched the plugin by adding some code that works for our use case to the above TODO'd section:

Obviously this may cause problems if you have components that use the beforeCreate lifecycle hook -- we don't -- but the idea is pretty simple. After this, the example Vue SSR code works fine.

  if (ssr) {
    const normalizedPath = slash(path.normalize(path.relative(options.root, filename)));
    let moduleId = JSON.stringify(normalizedPath);
    output.push(`
    __component__.options.beforeCreate = function() {
      const ctx = this.$ssrContext;
      ctx.modules = ctx.modules || [];
      if (!ctx.modules.includes(${moduleId})) {
        ctx.modules.push(${moduleId});
      }
    };
    `)
  }

@phonezawphyo
Copy link

phonezawphyo commented Oct 12, 2023

For those having trouble, my solution is similar to Iwansbrough's.
I created a burner plugin.

vite.vue-moduleid-extractor.js

/**
 * MIT License
 * 
 * Added this plugin so that you can use ctx.modules to know the rendered vue modules in vue SSR.
 * @vitejs/plugin-vue2 should handle this, but it has been TODO for more than a year.
 * https://github.com/vitejs/vite-plugin-vue2/blob/v2.2.0/src/main.ts#L135
 * 
 * phonezawphyo
 * 2023-10-12
 */
import path from "node:path"
import { normalizePath } from 'vite'

const parseVueRequest = (id) => {
  const [filename, rawQuery] = id.split(`?`, 2)
  const query = Object.fromEntries(new URLSearchParams(rawQuery))
  if (query.vue != null) {
    query.vue = true
  }
  if (query.index != null) {
    query.index = Number(query.index)
  }
  if (query.raw != null) {
    query.raw = true
  }
  if (query.url != null) {
    query.url = true
  }
  if (query.scoped != null) {
    query.scoped = true
  }
  return {
    filename,
    query,
  }
}

export default ({root}) => {
  return {
    name: 'vue-moduleid-extractor',
    transform(code, id, opt) {
      const ssr = opt?.ssr === true
      const { filename, query } = parseVueRequest(id)

      if (ssr && filename.endsWith('.vue') && !query.vue) {
        const normalizedFilename = normalizePath(
          path.relative(root, filename),
        )
        let moduleId = JSON.stringify(normalizedFilename)
        code = code.replace(/export default __component__\.exports$/, "")
        code += `
        __component__.options.beforeCreate = (__component__.options.beforeCreate||[]).concat([
          function() {
            const ctx = this.$ssrContext;
            ctx.modules = ctx.modules || [];
            if (!ctx.modules.includes(${moduleId})) {
              ctx.modules.push(${moduleId});
            }
          }
        ]);
        export default __component__.exports;
        `
        return { code, map: null }
      }
    }
  }
}

Usage in vite.config.js

import vueModuleIdExtractor from "./vite.vue-moduleid-extractor"
...
  plugins: [
    vue(),
    vueModuleIdExtractor({root:process.cwd()})
  ],
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants