第 5.3 章:在 Laravel 视图中嵌入 JavaScript
学习时间: 50 分钟
1. Web 中 JavaScript 的两种方法
迄今为止,我们一直在使用 服务器端渲染 (SSR) — 服务器 (Laravel) 生成完整的 HTML(通过 Blade)并将其发送到浏览器。这对于 SEO 和快速首次加载非常有利。
现在我们将添加客户端交互——浏览器在加载页面后将执行 JavaScript 代码来完成以下操作:
- 无需重新加载页面即可向我们的 API 发送请求。
- 动态更新页面部分(例如,向列表中添加新行星)。
- 显示通知和模态窗口。
💡 宇宙类比:
想象一下,SSR 就像在任务控制中心 (服务器) 获取打印出来的完整星系图。您可以看到打印时的所有对象。
客户端 JS 就像您的个人平板电脑 (浏览器),它实时与卫星 (API) 通信并更新您地图上对象的位置,而无需向任务控制中心请求新的纸质地图。
2. JS 代码的存储位置和引入方式
如我们所知,所有公共资源(CSS、JS、图像)都应位于 public
文件夹中。
正确结构:
- 源文件: 您所有的主要 JS 代码都应位于
public/js/
中。例如,public/js/planets.js
。 - Blade 中引入: 使用
asset()
辅助函数生成正确的 URL。
在 layouts/app.blade.php
布局中引入的示例:
<!DOCTYPE html>
<html>
<head>
{{-- ... --}}
</head>
<body>
{{-- ... 头部和主体 ... --}}
<footer>
<p>© {{ 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 }}">了解更多 →</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-id
和data-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 传递的不仅仅是字符串,而是整个数组或对象。
错误的方法(易受攻击):
正确的方法(通过 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')
内部。这允许仅在真正需要脚本的页面上添加它们。
巩固知识小测验
🚀 本章总结:
您已学会通过添加客户端逻辑来为静态 Blade 页面注入活力。关键技能包括:
- 在 Laravel 项目中正确组织和引入 JS 文件。
- 使用
data-*
属性将数据从 HTML 传递到 JS。 - 使用 Fetch 动态与 API 交互,无需重新加载页面。
- 使用
@json
指令安全地将 PHP 变量传递给 JavaScript。 - 使用
@push
和@stack
来组织脚本。 您的"控制面板"变得可交互了。然而,目前我们的修改请求(POST、PUT、DELETE)存在漏洞。在下一章中,我们将添加一个至关重要的保护机制——CSRF 令牌。