Yii-view

View组件

前言

视图层是以 View组件 为主体,通过小部件、主题、国际化、模板渲染 进行扩展,由于通过服务定位器的方式获取到的 view对象 ,所以整个周期中所用的 view 是同一个对象, 当然你也可以单独创建。

数据传递

  1. 通过控制器的 render() 方法传递给视图层
  2. 在视图层通过 $this->context 来获取所对应的控制器对象,可以访问对象中的属性值
  3. 控制器 controller 中 使用 $this->view->params['menu'] ;在布局(其他视图)中可以使用 $this->params['menu'] 访问;

例如:

1
2
3
4
5
6
7
8
index.php 视图文件
<?php
$this->params['ding']='ran'
?>
layout 布局文件
<?php
echo $this->params['ding'];
?>

渲染视图相关

在讲控制器的时候我们已经讲到控制器中调用的渲染视图的方法,最终都是调用view中的方法,具体的用法已经说过了,可以回去看一下。
没什么难点,就是根据规则找到视图路径,然后进行加载。

注册前端资源&&注册标签

使用注册资源的前提

注册资源就是将前端资源(css、js)注册到视图中相应的位。比如通过 registerCssFile() 方法来注册一个css文件,如何实现的呢?我们要从布局文件中发现他的秘密。
看一下自带视图文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $content string 字符串 */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
# 注册csrf
<?= Html::csrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<header>My Company</header>
<?= $content ?>
<footer>&copy; 2014 by My Company</footer>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>

注意各个方法所在的位置

beginPage() 方法

这个就是开启一个ob,并触发一个事件

1
2
3
4
5
6
7
8
public function beginPage()
{
// 开启ob
ob_start();
ob_implicit_flush(false);
// 触发事件
$this->trigger(self::EVENT_BEGIN_PAGE);
}

head() 方法

只是输出了一个占位符

1
2
3
4
public function head()
{
echo self::PH_HEAD;
}

beginBody() 方法

输出占位符,并触发一个事件

1
2
3
4
5
public function beginBody()
{
echo self::PH_BODY_BEGIN;
$this->trigger(self::EVENT_BEGIN_BODY);
}

endBody() 方法

触发一个事件,并输出占位符,同时注册 AssetBundle 资源,这个并不打算多说,也不是重点

1
2
3
4
5
6
7
8
9
public function endBody()
{
$this->trigger(self::EVENT_END_BODY);
echo self::PH_BODY_END;
// 注册资源
foreach (array_keys($this->assetBundles) as $bundle) {
$this->registerAssetFiles($bundle);
}
}

endPage() 方法

看下面的方法,是通过替换占位符来将不同的资源注册到相应的位置的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function endPage($ajaxMode = false)
{
$this->trigger(self::EVENT_END_PAGE);
# 获取页面内容
$content = ob_get_clean();
// 替换预留位置 加载注册的js/css 和标签
echo strtr($content, [
// 注册需要注册在头部的html
self::PH_HEAD => $this->renderHeadHtml(),
self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),
self::PH_BODY_END => $this->renderBodyEndHtml($ajaxMode),
]);
$this->clear();
}

看了原理之后,注册资源的方法就自己看吧。我不太想用,也就不细讲了

视图事件

View 视图组件会在视图渲染过程中触发几个事件, 可以在内容发送给终端用户前,响应这些事件来添加内容到视图中或调整渲染结果。

例如,如下代码将当前日期添加到页面结尾处:

1
2
3
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});

下面是debug模块在启动的时候注册的前端显示debug导航条的代码

1
2
3
4
5
6
7
8
9
10
11
12
public function bootstrap($app)
{
...
// 绑定声明周期事件
// delay attaching event handler to the view component after it is fully configured
$app->on(Application::EVENT_BEFORE_REQUEST, function () use ($app) {
// 前端页面注册debug小工具图标
$app->getView()->on(View::EVENT_END_BODY, [$this, 'renderToolbar']);
$app->getResponse()->on(Response::EVENT_AFTER_PREPARE, [$this, 'setDebugHeaders']);
});
...
}

安全

先看一下官网的介绍,详细的请看安全篇的文章

当创建生成HTML页面的视图时,在显示之前将用户输入数据进行转码和过滤非常重要, 否则,你的应用可能会被 跨站脚本(用户输入的前端代码) 攻击。

要显示纯文本,先调用 yii\helpers\Html::encode() 进行转码, 例如如下代码将用户名在显示前先转码:

1
2
3
4
5
6
7
<?php
use yii\helpers\Html;
?>
<div class="username">
<?= Html::encode($user->name) ?>
</div>

要显示HTML内容,先调用 yii\helpers\HtmlPurifier 过滤内容, 例如如下代码将提交内容在显示前先过滤:

1
2
3
4
5
6
7
<?php
use yii\helpers\HtmlPurifier;
?>
<div class="post">
<?= HtmlPurifier::process($post->text) ?>
</div>

提示: HTMLPurifier在保证输出数据安全上做的不错,但性能不佳,如果你的应用需要高性能可考虑 缓存 过滤后的结果。

小部件方法

其实下面的一些方法,都是将小部件封装到了 view 的方法中

嵌套布局

一个布局嵌套在另一个布局内,可以实现多层嵌套布局
用的是 ContentDecorator 小部件

1
2
3
4
5
6
7
8
9
10
11
12
content.php 子布局文件
//嵌套方法
//@app/views/layouts/main.php 表示要嵌套在main.php布局内,填充父布局的 $content 变量
<?php $this->beginContent('@app/views/layouts/main.php'); ?>
<hr>
<?=$content ;?>
<hr>
<?php $this->endContent(); ?>
//main.php yii默认布局,不用动
控制器修改布局为子布局
$this->loayout = 'content';

使用数据块

视图文件中定义数据块,布局文件根据逻辑判断使用数据块,用来做网页布局部分不相同的视图部分
用的是 Block 小部件,同样这个小部件用 view 组件的 blocks 属性来存放创建的数据块

1
2
3
4
5
6
7
8
9
10
11
12
//视图文件定义数据块
<?php $this->beginBlock('block1'); ?>
...content of block1...
<?php $this->endBlock(); ?>
//布局文件使用数据块
<?php if (isset($this->blocks['block1'])): ?>
<?= $this->blocks['block1'] ?>
<?php else: ?>
... default content for block1 ...
<?php endif; ?>

echo-ding wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!