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

Vue3+Vite SSR项目集成Vuetify3遇未知.css文件扩展名错误

Vue 3 + Vuetify 3 + Vite SSR 中 Unknown file extension ".css" 错误解决

错误信息

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".css" for /tmp/vite-plugin-vue/playground/ssr-vue/node_modules/vuetify/lib/components/VApp/VApp.css
    at new NodeError (node:internal/errors:387:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:75:11)
    at defaultGetFormat (node:internal/modules/esm/get_format:117:38)
    at defaultLoad (node:internal/modules/esm/load:81:20)
    at nextLoad (node:internal/modules/esm/loader:163:28)
    at ESMLoader.load (node:internal/modules/esm/loader:605:26)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:457:22)
    at new ModuleJob (node:internal/modules/esm/module_job:63:26)
    at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:480:17)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:434:34)

相关代码

应用初始化代码

import { createSSRApp } from 'vue'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import App from './App.vue'
import { createRouter } from './router'

export function createApp() {
  const app = createSSRApp(App)
  const router = createRouter()
  app.use(router)
  const vuetify = createVuetify({
    ssr: true,
    components,
    directives,
  })
  app.use(vuetify)
  return { app, router }
}

Vite配置文件(vite.config.js)

import path from 'node:path'
import { defineConfig } from 'vite'
import vuePlugin from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'


const virtualFile = '@virtual-file'
const virtualId = '\0' + virtualFile
const nestedVirtualFile = '@nested-virtual-file'
const nestedVirtualId = '\0' + nestedVirtualFile

const base = '/test/'

// preserve this to test loading __filename & __dirname in ESM as Vite polyfills them.
// if Vite incorrectly load this file, node.js would error out.
globalThis.__vite_test_filename = __filename
globalThis.__vite_test_dirname = __dirname

export default defineConfig(({ command, ssrBuild }) => ({
  base,
  plugins: [
    vuePlugin({
      template: { transformAssetUrls }
    }),
    vuetify({
      autoImport: true,
      styles: {
        configFile: 'src/styles/settings.scss',
      },
    }),
    vueJsx(),
    {
      name: 'virtual',
      resolveId(id) {
        if (id === '@foo') {
          return id
        }
      },
      load(id, options) {
        const ssrFromOptions = options?.ssr ?? false
        if (id === '@foo') {
          // Force a mismatch error if ssrBuild is different from ssrFromOptions
          return `export default { msg: '${
            command === 'build' && !!ssrBuild !== ssrFromOptions
              ? `defineConfig ssrBuild !== ssr from load options`
              : 'hi'
          }' }`
        }
      },
    },
    {
      name: 'virtual-module',
      resolveId(id) {
        if (id === virtualFile) {
          return virtualId
        } else if (id === nestedVirtualFile) {
          return nestedVirtualId
        }
      },
      load(id) {
        if (id === virtualId) {
          return `export { msg } from "@nested-virtual-file";`
        } else if (id === nestedVirtualId) {
          return `export const msg = "[success] from conventional virtual file"`
        }
      },
    },
    // Example of a plugin that injects a helper from a virtual module that can
    // be used in renderBuiltUrl
    (function () {
      const queryRE = /\?.*$/s
      const hashRE = /#.*$/s
      const cleanUrl = (url) => url.replace(hashRE, '').replace(queryRE, '')
      let config

      const virtualId = '\0virtual:ssr-vue-built-url'
      return {
        name: 'built-url',
        enforce: 'post',
        configResolved(_config) {
          config = _config
        },
        resolveId(id) {
          if (id === virtualId) {
            return id
          }
        },
        load(id) {
          if (id === virtualId) {
            return {
              code: `export const __ssr_vue_processAssetPath = (url) => '${base}' + url`,
              moduleSideEffects: 'no-treeshake',
            }
          }
        },
        transform(code, id) {
          const cleanId = cleanUrl(id)
          if (
            config.build.ssr &&
            (cleanId.endsWith('.js') || cleanId.endsWith('.vue')) &&
            !code.includes('__ssr_vue_processAssetPath')
          ) {
            return {
              code:
                `import { __ssr_vue_processAssetPath } from '${virtualId}';__ssr_vue_processAssetPath;` +
                code,
              sourcemap: null, // no sourcemap support to speed up CI
            }
          }
        },
      }
    })(),
  ],
  experimental: {
    renderBuiltUrl(filename, { hostType, type, ssr }) {
      if (ssr && type === 'asset' && hostType === 'js') {
        return {
          runtime: `__ssr_vue_processAssetPath(${JSON.stringify(filename)})`,
        }
      }
    },
  },
  build: {
    minify: false,
  },
  ssr: {
    noExternal: [
      // this package has uncompiled .vue files
      '@vitejs/test-example-external-component',
    ],
  },
  optimizeDeps: {
    exclude: ['@vitejs/test-example-external-component'],
  },
}))

解决方法

1. 更新SSR noExternal配置

在vite.config.js的ssr.noExternal数组中添加vuetify和vite-plugin-vuetify,让Vite将这些依赖纳入SSR打包流程,避免Node.js直接加载未处理的CSS文件:

ssr: {
  noExternal: [
    '@vitejs/test-example-external-component',
    'vuetify',
    'vite-plugin-vuetify'
  ],
},

2. 优化依赖预构建(可选)

如果遇到依赖预构建问题,可以将Vuetify相关样式和组件添加到optimizeDeps.include中,确保Vite提前处理这些依赖:

optimizeDeps: {
  exclude: ['@vitejs/test-example-external-component'],
  include: ['vuetify/styles', 'vuetify/components', 'vuetify/directives']
},

3. 确认插件加载顺序

确保vuetify插件在vuePlugin之后加载,这是因为Vuetify需要依赖Vue的模板转换配置,当前你的配置已经符合要求,无需调整。

原理说明

Node.js的ES模块加载器默认不支持解析.css文件的导入,而SSR构建时如果直接从node_modules加载Vuetify,Node会尝试处理其中的CSS导入从而报错。通过设置ssr.noExternal,Vite会将这些依赖打包到SSR产物中,在打包过程中自动处理CSS导入,避免Node直接处理这类文件。

内容的提问来源于stack exchange,提问作者Amritpal Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:38:10