Skip to content

第 5.3 章:在 Laravel 视图中嵌入 JavaScript

学习时间: 50 分钟


1. Web 中 JavaScript 的两种方法

迄今为止,我们一直在使用 服务器端渲染 (SSR) — 服务器 (Laravel) 生成完整的 HTML(通过 Blade)并将其发送到浏览器。这对于 SEO 和快速首次加载非常有利。

现在我们将添加客户端交互——浏览器在加载页面后将执行 JavaScript 代码来完成以下操作:

  • 无需重新加载页面即可向我们的 API 发送请求。
  • 动态更新页面部分(例如,向列表中添加新行星)。
  • 显示通知和模态窗口。

💡 宇宙类比:

想象一下,SSR 就像在任务控制中心 (服务器) 获取打印出来的完整星系图。您可以看到打印时的所有对象。

客户端 JS 就像您的个人平板电脑 (浏览器),它实时与卫星 (API) 通信并更新您地图上对象的位置,而无需向任务控制中心请求新的纸质地图。


2. JS 代码的存储位置和引入方式

如我们所知,所有公共资源(CSS、JS、图像)都应位于 public 文件夹中。

正确结构:

  1. 源文件: 您所有的主要 JS 代码都应位于 public/js/ 中。例如,public/js/planets.js
  2. Blade 中引入: 使用 asset() 辅助函数生成正确的 URL。

layouts/app.blade.php 布局中引入的示例:

<!DOCTYPE html>
<html>
<head>
    {{-- ... --}}
</head>
<body>
    {{-- ... 头部和主体 ... --}}

    <footer>
        <p>&copy; {{ date('Y') }} Space Command.</p>
    </footer>

    {{-- 脚本最好放在 body 标签的末尾,以加快页面渲染速度 --}}
    <script src="{{ asset('js/planets.js') }}"></script>
    @stack('scripts') {{-- 为特定页面的脚本创建一个“槽位” --}}
</body>
</html>

  • @stack('scripts') 是一个强大的 Blade 指令。它允许子视图将自己的 JS 代码“推入”到这个位置。这在某个页面需要独特脚本而另一个页面不需要时非常有用。

3. 示例:“删除”确认按钮

让我们在行星列表页面 (planets/index.blade.php) 上为每个行星添加一个“删除”按钮,该按钮将通过 JavaScript 和 Fetch API 工作。

步骤 1:在 resources/views/planets/index.blade.php 中添加按钮

修改行星卡片,添加带有 data 属性的按钮:

{{-- ... 在 @forelse 循环内部 ... --}}
<div class="planet-card" id="planet-card-{{ $planet->id }}">
    <h3>{{ $planet->name }}</h3>
    <p>太阳系:{{ $planet->solar_system }}</p>
    <p>直径:{{ number_format($planet->size_km, 0, '.', ' ') }} 公里</p>
    <a href="/planets/{{ $planet->id }}">了解更多 &rarr;</a>
    <button class="delete-btn" data-id="{{ $planet->id }}" data-url="/api/planets/{{ $planet->id }}">
        报废
    </button>
</div>
<!-- ... 在 body 结束标签之前 ... -->
<script src="{{ asset('js/planets.js') }}" defer></script>

  • id="planet-card-{{ $planet->id }}" — 整个卡片的唯一 ID,以便我们可以在 DOM 中将其删除。
  • data-iddata-url — 将数据从 PHP (Blade) 传递到 JavaScript 的便捷方式。

步骤 2:编写 JavaScript 逻辑

创建文件 public/js/planets.js 并添加以下代码:

document.addEventListener('DOMContentLoaded', () => {
    // 查找所有“删除”按钮
    const deleteButtons = document.querySelectorAll('.delete-btn');

    deleteButtons.forEach(button => {
        button.addEventListener('click', async (event) => {
            const planetId = event.target.dataset.id;
            const apiUrl = event.target.dataset.url;

            if (!confirm(`您确定要报废 ID 为 ${planetId} 的行星吗?此操作不可逆。`)) {
                return; // 用户点击了“取消”
            }

            try {
                // 向我们的 API 发送 DELETE 请求
                const response = await fetch(apiUrl, {
                    method: 'DELETE',
                    headers: {
                        'Accept': 'application/json',
                        // 稍后我们将在此处添加 CSRF 令牌
                    }
                });

                if (response.status === 204) { // 204 无内容 - 成功删除
                    // 从页面中移除行星卡片
                    const cardToRemove = document.getElementById(`planet-card-${planetId}`);
                    if (cardToRemove) {
                        cardToRemove.remove();
                    }
                    alert('行星已成功报废。');
                } else {
                    // 如果 API 返回错误
                    const errorData = await response.json();
                    alert(`错误:${errorData.message || '未能删除行星。'}`);
                }
            } catch (error) {
                console.error('发送请求时出错:', error);
                alert('发生网络错误。请重试。');
            }
        });
    });
});

现在,如果您刷新 /planets 页面,您将看到“报废”按钮,点击它们将触发我们的 JavaScript 代码!


4. 从 Blade 向 JavaScript 传递数据

有时需要向 JS 传递的不仅仅是字符串,而是整个数组或对象。

错误的方法(易受攻击):

let planets = {{ $planets }}; // 这将导致语法错误且不安全

正确的方法(通过 JSON): 使用 @json 指令。它能安全地将 PHP 数组/对象转换为有效的 JSON 对象。

planets/index.blade.php 中的示例:

@extends('layouts.app')
{{-- ... --}}
@section('content')
    {{-- ... --}}
@endsection

@push('scripts') {{-- 将我们的脚本“推入”布局中的 @stack('scripts') 槽位 --}}
<script>
    // Laravel 安全地将 $planets 集合转换为 JSON 数组
    const planetsData = @json($planets);

    // 现在我们可以在 JS 中使用这个数组了
    console.log('从 Blade 传递的行星数据:', planetsData);
    alert(`已加载 ${planetsData.length} 颗行星!`);
</script>
@endpush

  • @push('scripts') 将内容放入 layouts/app.blade.php 中的 @stack('scripts') 内部。这允许仅在真正需要脚本的页面上添加它们。

巩固知识小测验

1. 在 Laravel 项目中,公共 JS 和 CSS 文件应该存储在哪里?

2. 哪个 Blade 辅助函数用于正确生成资产(JS、CSS)的 URL?

3. `@push('scripts')` / `@stack('scripts')` 这对指令有什么作用?

4. 如何安全地将 PHP 数组 `$data` 从 Blade 传递到 JavaScript?

5. 为什么建议将 JS 脚本放在 body 标签的末尾引入?


🚀 本章总结:

您已学会通过添加客户端逻辑来为静态 Blade 页面注入活力。关键技能包括:

  • 在 Laravel 项目中正确组织和引入 JS 文件。
  • 使用 data-* 属性将数据从 HTML 传递到 JS。
  • 使用 Fetch 动态与 API 交互,无需重新加载页面。
  • 使用 @json 指令安全地将 PHP 变量传递给 JavaScript。
  • 使用 @push@stack 来组织脚本。 您的"控制面板"变得可交互了。然而,目前我们的修改请求(POST、PUT、DELETE)存在漏洞。在下一章中,我们将添加一个至关重要的保护机制——CSRF 令牌。