视图

简介

直接从路由和控制器里返回整段 HTML 字符串显然不现实。视图就解决了这个问题——把所有 HTML 放在单独的文件里,干干净净。

视图把控制器/应用逻辑和展示逻辑分开,放在 resources/views 目录下。Laravel 的视图模板通常用 Blade 模板引擎来写。一个简单的视图长这样:

<!-- 视图存放在 resources/views/greeting.blade.php -->

<html>
    <body>
        <h1>Hello, {{ $name }}</h1>
    </body>
</html>

因为这个视图存放在 resources/views/greeting.blade.php,你可以用全局 view() 辅助函数返回它:

Route::get('/', function () {
    return view('greeting', ['name' => 'James']);
});

信息

想了解更多 Blade 模板的写法?看看完整的 Blade 文档入门。

用 React / Svelte / Vue 写视图

很多开发者不再用 PHP 通过 Blade 写前端模板,而是更喜欢用 React、Svelte 或 Vue。Laravel 通过 Inertia 让这一切变得很轻松——Inertia 是一个库,能把你的 React / Svelte / Vue 前端和 Laravel 后端绑在一起,不用经历构建 SPA 的那些复杂操作。

我们的 React、Svelte 和 Vue 应用起步套件给你提供了一个很好的起点,基于 Inertia 搭建你的下一个 Laravel 应用。

创建和渲染视图

在 resources/views 目录下放一个 .blade.php 扩展名的文件,或者用 make:view Artisan 命令,就能创建一个视图:

php artisan make:view greeting

.blade.php 扩展名告诉框架这个文件包含 Blade 模板。Blade 模板里既有 HTML,也有 Blade 指令——可以轻松输出值、写 if 语句、遍历数据等等。

创建好视图之后,你可以用全局 view() 辅助函数从路由或控制器返回它:

Route::get('/', function () {
    return view('greeting', ['name' => 'James']);
});

也可以用 View 门面返回:

use Illuminate\Support\Facades\View;

return View::make('greeting', ['name' => 'James']);

如你所见,view() 辅助函数的第一个参数对应 resources/views 目录下的视图文件名。第二个参数是要传给视图的数据数组。这里我们传了 name 变量,在视图里用 Blade 语法显示。

嵌套视图目录

视图也可以放在 resources/views 目录的子目录里。引用嵌套视图时用点号(.)分隔。比如你的视图存放在 resources/views/admin/profile.blade.php,你可以这样返回它:

return view('admin.profile', $data);

注意

视图目录名不要包含 . 字符。

创建第一个存在的视图

用 View 门面的 first() 方法,你可以在一组视图里返回第一个存在的那个。这在你的应用或包允许视图被自定义或覆盖时很有用:

use Illuminate\Support\Facades\View;

return View::first(['custom.admin', 'admin'], $data);

判断视图是否存在

用 View 门面的 exists() 方法可以判断视图是否存在,返回 true 表示存在:

use Illuminate\Support\Facades\View;

if (View::exists('admin.profile')) {
    // ...
}

往视图传数据

前面例子已经展示了,你可以传一个数据数组给视图,让数据在视图里可用:

return view('greetings', ['name' => 'Victoria']);

用这种方式传数据时,数据应该是一个键值对数组。提供数据后,你可以在视图里用对应的键来访问值,比如 <?php echo $name; ?>。

除了传整个数组给 view() 辅助函数,你也可以用 with() 方法逐个添加数据。with() 方法返回视图对象实例,所以你可以继续链式调用:

return view('greeting')
    ->with('name', 'Victoria')
    ->with('occupation', 'Astronaut');

给所有视图共享数据

有时候你可能需要给应用渲染的所有视图共享同一份数据。用 View 门面的 share() 方法就行。通常你应该在服务提供者的 boot() 方法里调用 share()。你可以放在 App\Providers\AppServiceProvider 里,也可以单独生成一个服务提供者来放:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册应用服务。
     */
    public function register(): void
    {
        // ...
    }

    /**
     * 启动应用服务。
     */
    public function boot(): void
    {
        View::share('key', 'value');
    }
}

视图 Composer

视图 Composer 是视图渲染时执行的回调或类方法。如果你有一份数据需要在每次渲染某个视图时都绑定上去,视图 Composer 可以帮你把这些逻辑集中到一个地方。当同一个视图被多个路由或控制器返回、而且总是需要某份数据时,视图 Composer 特别有用。

通常视图 Composer 会在应用的某个服务提供者里注册。下面的例子假设放在 App\Providers\AppServiceProvider 里。

我们用 View 门面的 composer() 方法注册视图 Composer。Laravel 没有为基于类的视图 Composer 提供默认目录,你可以按自己喜欢的方式组织。比如可以建一个 app/View/Composers 目录来放所有的视图 Composer:

<?php

namespace App\Providers;

use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\View;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册应用服务。
     */
    public function register(): void
    {
        // ...
    }

    /**
     * 启动应用服务。
     */
    public function boot(): void
    {
        // 基于类的 Composer...
        Facades\View::composer('profile', ProfileComposer::class);

        // 基于闭包的 Composer...
        Facades\View::composer('welcome', function (View $view) {
            // ...
        });

        Facades\View::composer('dashboard', function (View $view) {
            // ...
        });
    }
}

注册好 Composer 之后,每次渲染 profile 视图时,App\View\Composers\ProfileComposer 类的 compose() 方法就会被执行。来看看这个 Composer 类的例子:

<?php

namespace App\View\Composers;

use App\Repositories\UserRepository;
use Illuminate\View\View;

class ProfileComposer
{
    /**
     * 创建一个新的 Profile Composer。
     */
    public function __construct(
        protected UserRepository $users,
    ) {}

    /**
     * 绑定数据到视图。
     */
    public function compose(View $view): void
    {
        $view->with('count', $this->users->count());
    }
}

可以看到,所有视图 Composer 都通过服务容器解析,所以你可以在 Composer 的构造函数里类型提示需要的依赖。

给多个视图附加 Composer

你可以一次给多个视图附加同一个 Composer,把视图名数组作为第一个参数传给 composer() 方法:

use App\Views\Composers\MultiComposer;
use Illuminate\Support\Facades\View;

View::composer(
    ['profile', 'dashboard'],
    MultiComposer::class
);

composer() 方法还接受 * 通配符,可以把 Composer 附加到所有视图:

use Illuminate\Support\Facades;
use Illuminate\View\View;

Facades\View::composer('*', function (View $view) {
    // ...
});

视图 Creator

视图 "Creator" 和视图 Composer 很像,区别在于执行时机:Creator 在视图实例化之后立即执行,不用等到视图即将渲染。注册视图 Creator 用 creator() 方法:

use App\View\Creators\ProfileCreator;
use Illuminate\Support\Facades\View;

View::creator('profile', ProfileCreator::class);

优化视图

默认情况下,Blade 模板视图是按需编译的。当执行渲染视图的请求时,Laravel 会先判断编译后的视图是否存在。如果存在,再判断未编译的视图是否比编译后的更新。如果编译后的视图不存在,或者未编译的视图被修改过,Laravel 就重新编译。

在请求过程中编译视图可能对性能有一点负面影响,所以 Laravel 提供了 view:cache Artisan 命令来预编译应用用到的所有视图。为了提升性能,你可以在部署流程中加上这一步:

php artisan view:cache

用 view:clear 命令可以清除视图缓存:

php artisan view:clear