You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用InertiaJS(SSR)、Vite和SCSS时FOUC问题的排查与解决

Laravel + Inertia SSR + Vue3 中 FOUC(无样式内容闪烁)问题的原因与解决方案

原因分析

  • SSR渲染阶段缺失组件样式:组件内联引入的SCSS是在客户端Vue实例挂载后才被Vite动态加载的,但SSR生成的初始HTML中并没有包含这些样式。刷新页面时,浏览器先渲染SSR返回的无样式DOM,直到客户端JS加载并注入样式后才会显示正确样式,这就造成了FOUC。
  • Vite异步加载组件样式:组件内通过import './style.scss'引入的样式,Vite默认会打包成异步chunk,客户端需要额外请求才能加载,无法在SSR渲染时同步注入到HTML头部。
  • 现有方案的局限性:
    • 将样式放入app.blade.php:虽然能解决刷新时的FOUC,但会一次性加载所有页面样式,违背了组件样式隔离的设计,且Inertia页面切换时不会卸载旧样式,可能导致样式冲突。
    • Inertia <Head>预加载:这是客户端层面的操作,SSR渲染时并不会执行这段逻辑,所以刷新初始阶段依然无法获取样式。

解决方案

1. 配置Vite将组件样式同步注入SSR输出

在vite.config.js中调整SSR相关配置,让组件内的样式在SSR打包时被提取并同步处理:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    laravel({
      input: 'resources/js/app.js',
      ssr: 'resources/js/ssr.js',
      refresh: true,
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false,
        },
      },
    }),
  ],
  ssr: {
    // 强制组件样式打包为同步资源,避免异步加载
    noExternal: ['vue'],
    optimizeDeps: {
      include: ['vue'],
    },
  },
  css: {
    modules: {
      generateScopedName: '[name]__[local]___[hash:base64:5]',
    },
  },
});

2. 改造SSR入口文件,收集并注入样式

在resources/js/ssr.js中,利用Vue SSR API收集组件样式,插入到HTML头部:

import { createSSRApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { renderToString } from '@vue/server-renderer';
import { createServer } from '@inertiajs/vue3/server';
import resolvePage from './pages/resolve';

export default createServer((page) =>
  createInertiaApp({
    page,
    render: renderToString,
    resolve: resolvePage,
    setup({ App, props, plugin }) {
      const styles = [];
      const app = createSSRApp({ render: () => h(App, props) })
        .use(plugin)
        .config.globalProperties.$ssrCollectStyle = (style) => styles.push(style);

      return { app, styles };
    },
    // 将收集的样式注入HTML头部
    head: (head) => ({
      ...head,
      style: [...(head.style || []), ...styles.map(s => ({ children: s }))],
    }),
  })
);

3. 调整组件样式引入逻辑

在组件中区分SSR/客户端环境,将样式传递给SSR收集器或客户端动态注入:

<template>
  <div class="page-container">...</div>
</template>

<script setup>
import { getCurrentInstance } from 'vue';
import styleText from './style.scss?raw';

const instance = getCurrentInstance();
if (import.meta.env.SSR) {
  // SSR环境下收集样式
  instance.appContext.app.$ssrCollectStyle(styleText);
} else {
  // 客户端环境动态注入样式
  const style = document.createElement('style');
  style.textContent = styleText;
  document.head.appendChild(style);
}
</script>

4. 备选方案:使用CSS Modules自动处理

如果上述配置过于繁琐,可以改用CSS Modules,Vite和Vue3对其支持更完善,SSR时会自动注入样式:

  1. 将组件样式文件命名为style.module.scss
  2. 在组件中引入使用:
<template>
  <div :class="$style.container">...</div>
</template>

<script setup>
import $style from './style.module.scss';
</script>

内容的提问来源于stack exchange,提问作者Khaled Nadam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:52:47