返回

Inertia.js + Vue + Laravel 全局CSS引用404问题解决

vue.js

Inertia.js + Vue + Laravel:全局 CSS 文件引用问题及解决方案

遇到了个挺常见的问题,在用 Inertia.js、Vue 和 Laravel 搭建项目的时候,想通过全局 CSS 文件 (app.css) 来移除 Bootstrap 按钮获得焦点时的边框,但发现 Laravel 找不到这个 CSS 文件,报 404 错误。 像下面这样:

resources/css/app.css

.btn:focus {
    box-shadow: none;
}

resources/views/app.blade.php

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="../css/app.css"> // 404 Not Found
    @vite('resources/js/app.js')
    @inertiaHead
  </head>
  <body>
    @inertia
  </body>
</html>

控制台报错: Failed to load resource: the server responded with a status of 404 (Not Found)

问题原因分析

问题出在对 Vite 和 Laravel 资源路径的理解上。 简单来说,就是咱直接用 <link> 标签引入相对路径的 CSS 文件,这条路在基于 Vite 构建的项目里走不通。

具体原因有以下几点:

  1. Vite 的工作方式: Vite 在开发环境下,并不是直接把 resources 目录下的文件原封不动地放到服务器根目录。它会对资源进行处理、打包,并把处理后的文件放在一个特定的输出目录(通常是 public/build)。
  2. 相对路径的解析: <link rel="stylesheet" href="../css/app.css"> 这个路径是相对于 app.blade.php 所在的位置(resources/views)来解析的。 浏览器会尝试访问 /resources/css/app.css,而这个路径并不是 Vite 处理后文件的实际存放位置,因此找不到。
  3. Laravel 静态资源 : 传统上,Laravel 会把静态资源(如 CSS、JavaScript、图片)放在 public 目录下。

解决方案

解决这问题,其实有多种方式,关键在于让Vite正确处理CSS文件,并使app.blade.php 能正确引入处理后的文件。

方案一: 使用 Vite 处理 CSS (推荐)

这是最推荐的方式,完全利用 Vite 的能力来处理 CSS,一步到位。

  1. 修改 app.js (或你的入口 JS 文件):

    在你的 JavaScript 入口文件中(通常是 resources/js/app.js)导入你的 CSS 文件:

    import { createApp, h } from 'vue';
    import { createInertiaApp } from '@inertiajs/vue3'; // 导入正确的模块
    
    // 导入全局 CSS 文件
    import '../css/app.css';
    
    createInertiaApp({
      resolve: name => {
        const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
        return pages[`./Pages/${name}.vue`]
      },
      setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
          .use(plugin)
          .mount(el)
      },
    });
    
  2. 移除 app.blade.php 中手动添加的 <link> 标签:

    由于 Vite 会自动处理并注入 CSS,所以不需要在 app.blade.php 中手动添加 <link> 标签引入 app.css

  3. 构建:
    开发环境中,通常无需手动构建,vite会自动处理。如果需要部署,使用以下指令:

    npm run build
    

    Vite 会自动把 app.css 以及其他 JavaScript 文件一起打包,并生成到 public/build 目录下。

原理:

Vite 会自动处理导入的 CSS 文件。通过import '../css/app.css', Vite 会找到 app.css,进行处理(比如编译、压缩、添加浏览器前缀等),然后将处理后的 CSS 代码注入到最终生成的 JavaScript 文件中。这样,当浏览器加载 JavaScript 文件时,CSS 样式也会同时生效。

方案二: 使用 @vite 指令引入 CSS

这种方式可以在 Blade 模板中使用 @vite 指令来引入 CSS 文件。

  1. app.blade.php 中使用 @vite 指令:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
        @vite(['resources/js/app.js', 'resources/css/app.css'])
        @inertiaHead
      </head>
      <body>
        @inertia
      </body>
    </html>
    

    使用 @vite指令,把resources/css/app.css 加到需要加载的文件数组内。

  2. 构建:
    和上面方案一样。

    npm run build
    

原理:

@vite 指令是 Laravel 提供的一个方便的工具,用来引入 Vite 处理后的资源。 它会根据开发环境或生产环境,自动生成正确的 <link><script> 标签,指向 Vite 处理后的文件。

方案三: 将 CSS 文件移动到 public 目录 (不推荐)

这种方式虽然可以解决问题,但并不推荐。因为它绕过了 Vite 的处理流程,无法享受 Vite 提供的各种优化功能。

  1. 移动 app.css:resources/css/app.css 文件移动到 public/css/app.css

  2. 修改 app.blade.php 中的路径:

    <link rel="stylesheet" href="/css/app.css">
    

原理:

public 目录下的文件可以直接通过 URL 访问,所以这样修改后,浏览器可以找到 app.css 文件。但, app.css 不会经过 vite 的任何处理。

不推荐理由: 这样做绕过了 Vite,无法使用 Vite 的诸多优点,例如:代码分割、热模块替换(HMR)、自动添加浏览器前缀等。

安全建议 (适用于所有方案)

  • 内容安全策略 (CSP): 如果你使用了 CSP, 确保你的 CSP 策略允许内联样式或者允许加载 Vite 生成的 CSS 文件。
  • 代码审查: 定期审查 CSS 代码,避免潜在的样式冲突或安全漏洞。

进阶技巧 (针对方案一)

1. 使用 CSS 预处理器 (Sass, Less, Stylus):

Vite 原生支持 CSS 预处理器。如果想使用 Sass,只需安装相应的依赖:

npm install -D sass

然后,把你的 .css 文件重命名为 .scss (或 .sass),就可以直接在文件中使用 Sass 语法了。 Vite 会自动处理编译。

2. CSS Modules:

如果你希望避免全局 CSS 样式污染,可以使用 CSS Modules。 在 Vue 组件中,可以这样使用:

<template>
  <button :class="$style.myButton">Click me</button>
</template>

<style module>
.myButton {
  background-color: blue;
  color: white;
}

.myButton:focus{
    box-shadow: none;
}
</style>

Vite 会自动处理 CSS Modules,确保类名唯一,避免冲突。

3. PostCSS 配置:

如果你需要更精细地控制 CSS 的处理(比如添加自定义的 PostCSS 插件),可以创建一个 postcss.config.js 文件,在里面配置 PostCSS 插件。例如, 使用autoprefixer:

```bash
 npm install -D autoprefixer
```

然后在 postcss.config.js:

```js
// postcss.config.js
 module.exports = {
  plugins: {
   autoprefixer: {},
   },
  }
 ```
 这样Vite在构建过程中,就会使用autoprefixer了。

通过上面的几种方式,就能完美解决全局 CSS 文件引用的问题。推荐使用方案一,能够充分享受 Vite 带来的各种便利和优化,写起来也更方便。