Swoft2 框架精华教程:Swoft 的视图组件

概述

用模板对页面进行渲染,这是比较经典的一种设计方式了。主要目的是在服务器端进行页面渲染,以使客户端浏览器可以直接拿到页面 html 的代码,这样对搜索引擎对网站的收录比较友好。如果是前后端分离的形式,由于前后端交互是用 js 进行,搜索引擎无法对页面渲染后进行 js 代码的执行,导致无法获取最终的 html 页面结构。所以这种方式对网站的 seo 很不友好,对主要依赖自然搜索流量的站点来说,不要采取前后端分离方式。

Swoft 的这个 View 组件,就是针对服务器端渲染的一个组件。可以自定义网页公共模板,布局文件。对页面拆分提供帮助。

详细说明

@View 注解

  • template:用来标注访问路由和view 模板的绑定关系

    标注了绑定关系,在 Controller/action 执行结束后,才会找此控制器对应的模板,如果 RequestMapping 绑定的 class/method 和 View 组件绑定的模板路径是同一个,则会自动解析此模板。

  • layout:指定的模板路径,如果不设置,则会使用 view 组件的默认模板

控制器配置模板

php 复制
    /**
     * @RequestMapping(route="[/|index.html]", method={"GET"})
     * @View("home/index")
     *
     * @param Request $request
     * @param Response $response
     * @return Response
     */

配置了 @view 注解后,无需在 Controller 的方法中在手动进行 render,方法执行完后,会自动进行页面渲染。

这个机制是通过自定义的 Middleware 实现的。具体可以参见 vendor/swoft/view/src/Middleware 中的 ViewMiddleware.php 查看源代码。只要在项目中引入了这个中间件,在处理客户端请求时候,就会实现 view 的自动加载。

配置 ViewMiddleware

配置 ViewMiddleware 主要有以下几种方式:

全局配置

可以在 bean.php 定义文件中,配置 httpServer 的 httpDispatcher 全局 Middleware。

所谓全局配置,也就是说,请求没有异常,所有的请求都会执行完所有的全局配置中间件。

以下这种配置方式,可以看到,会在用户自定义 UserMiddleware::class 执行后执行 ViewMiddleware,具体,可以参见之前的教程:Middlewares 优先级,里面有关于中间件配置优先级的详细说明,所有的规则都是来自于对源代码的了解。

php 复制
    'httpDispatcher'     => [
        'middlewares'      => [
            \Swoft\Http\Session\SessionMiddleware::class,
            \Swoft\Http\Server\Middleware\ValidatorMiddleware::class,
        ],
        'afterMiddlewares' => [
            \Swoft\View\Middleware\ViewMiddleware::class,
        ]
    ],
通过 @Middleware 注解进行配置 ViewMiddleware 中间件

首先我们知道 @Middleware 注解的可配置对象为:"CLASS", "METHOD", "ANNOTATION",如下源码:

php 复制
/**
 * Class Middleware
 *
 * @since 2.0
 *
 * @Annotation
 * @Target({"CLASS", "METHOD", "ANNOTATION"})
 * @Attributes({
 *     @Attribute("name", type="string"),
 * })
 */
final class Middleware
配置类注解

也就是说,这个注解可以配置针对一个类的所有方法,比如配置到 TestController 类注解上,表示 TestController 中的任何一个方法执行时候,都会在 UserMiddleware::class 级别自动将指定的中间件动态添加(详见 Middlewares 优先级说明)。这样,只有访问到这个控制器的请求才会执行到对应的中间件。

配置方法注解

如题,将 @Middleware 注解配置到方法上,只有请求指定控制器的绑定了 @Middleware 注解的方法,才会执行对应的中间件逻辑。

配置注解的注解
php 复制
/**
 * Class AuthRoleController
 *
 * @since 2.0
 * @Controller("auth/role")
 * @Middlewares({
 *     @Middleware(ViewMiddleware::class),
 *     @Middleware(AuthMiddleware::class)
 * })
 */

@Middleware 注解可以配置到 @Middlewares 注解之中。@Middlewares 注解,如其名字,就是可以绑定多个中间件,执行顺序按照绑定的顺序从上往下执行。

模板代码使用

当指定的方法或者类已经绑定了 ViewMiddleware::class 中间件,一旦控制器中的逻辑执行完毕,后续就会自动执行当前方法绑定的模板页面。

以下示例中这个方法就是访问网站首页时候,会在 resource/views(默认模板存放路径)下寻找 home/index.php,如果有这个文件,就会执行渲染。

php 复制
/**
 * @RequestMapping(route="[/|index.html]", method={"GET"})
 * @View("home/index")
 *
 * @param Request $request
 * @param Response $response
 * @return Response
 */

代码渲染类 Renderder::class

php 复制
// 此段代码摘自 ViewMiddleware 中间件 process 方法中
$actionId = $route->getHandler();
if (!$info = ViewRegister::findBindView($actionId)) {
    return $response;
}

// Get layout and template
[$template, $layout] = $info;

// Accept list
$allowedAccepts = $request->getHeader('accept');
$currentAccept  = current($allowedAccepts);
$contentType    = ContentType::HTML;

if ($template && false !== strpos($currentAccept, $contentType)) {
    $data = $response->getData();

    if (is_object($data) && $data instanceof Arrayable) {
        $data = $data->toArray();
    }

    /* @var Renderer $view */
    $renderer = bean('view'); // 此处为获取模板渲染的对象
    $content  = $renderer->render($template, $data, $layout);

    return $response->withContent($content)->withContentType($contentType);
}

由以上代码可见,最终调用了 Renderer::class->render渲染的模板。模板中使用的为控制器传递过来的 data 参数数组。

php 复制
// 在控制器执行完毕时候,将要渲染的变量传递给模板,这里的 data 就是渲染到模板中的参数集合。
$data = [
    'test' => 'this is a test.'
];
return $response->withData($data);

在模板中使用 $this 变量是可以的,这个指向了 Renderer 对象实例。使用参数时候可以直接调用 $data['test'] 中的参数。也可以直接使用 $data变量中的一级键名作为变量名(也就是直接用 $test)。因为在模板渲染时候,render 方法中使用了以下代码:

php 复制
protected function protectedIncludeScope($file, array $data): void
{
    extract($data, EXTR_OVERWRITE);
    include $file; // 特别注意:千万不要在 data 中放入 file 键,会将此处模板名给覆盖掉。
}

剩下的就是在模板中进行 php html 混编了,这个都是基础,不在赘述。