使用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时会自动注入样式:
- 将组件样式文件命名为
style.module.scss - 在组件中引入使用:
<template> <div :class="$style.container">...</div> </template> <script setup> import $style from './style.module.scss'; </script>
内容的提问来源于stack exchange,提问作者Khaled Nadam
相关产品推荐
相关产品推荐

