PAYATHON 2026

Laravel 项目中使用 layouts、views 和 partials 时应如何组织 CSS?

技术老齐

CSS 可以按使用范围分为公共样式、布局样式、组件样式和页面样式。由 layout 统一加载基础文件,再为具体页面预留额外的样式入口。CSS 文件不必与 Blade 文件一一对应。

Blade 最终输出的是 HTML,浏览器根据页面加载的样式表和 CSS 选择器应用样式。@include、@extends、@yield 都不会自动加载 CSS,也不会隔离样式的作用范围。

按使用范围组织 CSS

登录、注册等页面可以采用下面的目录结构。这个示例直接使用 public 下的静态 CSS 文件:

resources/views/
├── layouts/
│   └── auth.blade.php
├── partials/
│   └── header.blade.php
└── auth/
    └── login.blade.php

public/css/
├── app.css
├── components/
│   └── header.css
├── layouts/
│   └── auth.css
└── pages/
    └── login.css

各文件分别存放以下样式:

  • app.css:全站通用的基础样式,例如字体、页面默认间距。
  • components/header.css:header 的样式,供包含该 partial 的页面复用。
  • layouts/auth.css:登录、注册等使用 auth layout 的页面共享的样式。
  • pages/login.css:仅登录页需要的样式。

文件数量可以根据项目规模调整。如果登录和注册页面的样式基本一致,放在 auth.css 中即可,不必为了对应 Blade 文件再拆成多个 CSS 文件。

在 layout 中加载共享样式

将原来空的 <link> 填上实际的资源地址,并用 @stack('styles') 接收页面额外声明的样式:

{{-- resources/views/layouts/auth.blade.php --}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>@yield('title', '账户')</title>

    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    <link rel="stylesheet" href="{{ asset('css/components/header.css') }}">
    <link rel="stylesheet" href="{{ asset('css/layouts/auth.css') }}">

    @stack('styles')
</head>
<body class="auth-layout">
    @include('partials.header')

    <main class="auth-container">
        @yield('form')
    </main>
</body>
</html>

asset('css/app.css') 用于生成资源 URL,对应的文件放在 public/css/app.css,路径中不用再加 public/。

auth layout 加载了 header 的 CSS,因此使用这个 layout 的页面都会加载 header 样式。其他 layout 如果也包含 header,就需要加载相同的样式,或将其纳入全站公共 CSS 入口。

在具体 view 中补充页面样式

登录页继承 layout,通过 @push('styles') 添加自己的 CSS:

{{-- resources/views/auth/login.blade.php --}}
@extends('layouts.auth')

@section('title', '登录')

@push('styles')
    <link rel="stylesheet" href="{{ asset('css/pages/login.css') }}">
@endpush

@section('form')
    <section class="auth-card login-card">
        <h1>登录</h1>
        <p class="login-hint">请输入你的账户信息。</p>

        {{-- 在这里放置已有的登录表单 --}}
    </section>
@endsection

渲染页面时,@push('styles') 中的内容会输出到 layout 的 @stack('styles') 位置。其他页面如果没有通过 @push 添加 login.css,就不会通过这个入口加载它。

@yield('form') 输出表单区域的 HTML,@stack('styles') 则汇集额外的样式标签。

partial 使用自己的类名,避免样式互相影响

header partial 可以只保留 HTML:

{{-- resources/views/partials/header.blade.php --}}
<header class="site-header">
    <a class="site-header__brand" href="{{ url('/') }}">
        网站名称
    </a>
</header>

将以下样式分别放入注释标明的文件:

/* public/css/app.css */
* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: sans-serif;
}

/* public/css/components/header.css */
.site-header {
    padding: 16px 24px;
    border-bottom: 1px solid #ddd;
}

.site-header__brand {
    color: #222;
    text-decoration: none;
}

/* public/css/layouts/auth.css */
.auth-layout {
    background: #f5f5f5;
}

.auth-container {
    width: 100%;
    max-width: 480px;
    margin: 48px auto;
    padding: 0 16px;
}

.auth-card {
    padding: 24px;
    background: #fff;
    border-radius: 8px;
}

/* public/css/pages/login.css */
.login-card .login-hint {
    color: #666;
}

.site-header__brand、.auth-container 这样的类名可以减少与其他区域重名的机会。CSS 即使拆成多个文件,加载后仍然参与当前文档的样式计算。单独放在 header.css 中,并不会自动将其中的规则限制在 header 内。

组件样式应尽量使用组件自己的类名。只有在打算影响全站时,才使用 a { ... }、input { ... } 这样的选择器。

项目使用 Vite 时,沿用已有的资源入口

如果项目已经配置了 Laravel 的 Vite 集成,可以把 CSS 源文件放在 resources/css 下,通过已有入口导入组件和布局样式。例如,CSS 入口 resources/css/app.css 可以包含:

@import './components/header.css';
@import './layouts/auth.css';

/* 其他公共样式 */

如果该文件已在 Vite 配置中登记为入口,就可以在 layout 中这样加载:

@vite('resources/css/app.css')

项目如果已经通过 JavaScript 入口导入 CSS,沿用那个入口即可。页面专属 CSS 可以合并到现有入口中;确实需要按页面单独加载时,再配置为独立入口,并通过页面的 @push 引入。

resources/css 中的源文件需要经过相应的资源处理流程,不能直接把上面的静态文件路径改成 asset('resources/css/app.css')。

使用时注意这几点

  • 同一份 CSS 只保留一种加载方式,避免既通过 <link> 引入,又被构建入口重复导入。
  • 尽量由 layout 或资源入口统一管理共享样式。在反复使用的 partial 中直接放 <link>,容易产生重复标签。
  • 示例将页面样式放在共享样式之后,便于补充页面规则。能否覆盖已有声明,还取决于选择器优先级、!important 等层叠规则。
  • CSS 没生效时,先在浏览器开发者工具中检查资源请求是否成功,再检查选择器是否匹配、声明是否被覆盖。

备注:内容仅供参考。