之前的文章我们根据源码的分析,弄清了 Yii 如何处理一次请求 ,以及 根据解析的路由如何调用控制器中的 action ,那接下来好奇的可能就是,我在控制器 action 中执行了
return $this - >render('index')
,那 render 这个方法是如何完成渲染视图文件的工作的?我们继续从源码入手.
1,找到视图文件
先看我们在 controller/action 中视图渲染的调用:
public function actionIndex()
{
//代码省略
return $this->render('index',[
'model'=>$model
]);
}
1.1,找到 render 方法
因为所有的控制器类都继承了 yii\web\Controller,最终找到 render 位于 yii\web\Controller 的父类
yii\base\Controller
, 看定义:
//yii\base\Controller
public
function render($view, $params = []) {
$content = $this - >getView() - >render($view, $params, $this);
return $this - >renderContent($content);
}
可以看到真正的渲染操作并非在 controller 中处理,而是在 view 对象中完成的.这里只负责调用:
把视图文件给我渲染好
把渲染好的视图文件放进我定义的布局文件
返回
1.2,获取视图对象
从 render 方法的设计可以看到 Yii2.0 中,控制器 (C) 和视图 (V) 是完全分离的,各司其职.看 $this->getView():
//yii\base\Controller
public
function getView() {
if ($this - >_view === null) {
$this - >_view = Yii: :$app - >getView();
}
return $this - >_view;
}
Yii::$app,就是我们 new Application 的时候保存的一个全局 application 变量,在构造方法中已经保存好了 Yii::$app = $this;,可以查看
yii\base\Application __construct
,不过多描述了.
这样我们知道了
Yii: :$app - >getView()
,getView 应该位于
yii\web\Application
,或者其父类中 `:
//yii\base\Application
public
function getView() {
return $this - >get('view');
}
get 方法位于 Application 的父类
yii\di\ServiceLocator
, 根据源码可以看到,是对当前对象中的两个成员变量 $_components 与 $_definitions 做了判断,看是否有 view 这个组件的对象 (实例),或者定义 (
['class' = >'yii\web\view']
),有实例则直接返回,否则根据定义创建实例 (createObject) 返回.
[注] 关于 $_definitions 变量的初始化,其实在 new Application 的时候,在
yii\base\Application
的__construct 构造方法中,调用了 preInit, 而 preInit 方法中对核心组件 (coreComponents) 进行了初始化,这里就包括了
'view' = >['class' = >'yii\web\View']
,组件的初始化就是这些配置信息保存的过程,主要通过 yii\base\Object 中的构造方法,调用了
Yii: :configure($config)
,configure 的过程就是对
$_definitions $_components
赋值的过程.
最终经过 getView(),我们得到的就是一个 yii\web\View object
2,渲染视图文件
进入到 yii\web\View 中的 render 方法,位于其父类.
//yii\base\View
public
function render($view, $params = [], $context = null) {
$viewFile = $this - >findViewFile($view, $context);
return $this - >renderFile($viewFile, $params, $context);
}
通过代码可以看到,渲染视图文件的第一步就是找到这个视图文件在磁盘中的文件存储位置(真实路径).从 findViewFile 方法寻找路径的过程,也可看出我们的 $view 参数支持的几种格式.
2.1,$view 参数格式
源码不放了,大家对照 yii\base\View 中的 function findViewFile,或 看这里
按照 alias 别名的方式
检查 $view 中是否包含 "@",如果有说明是使用了别名的方法,直接通过
Yii: :getAlias($view)
获取, 因为形式是 "@backend/views/site/index" 这种格式 ,而在 config 文件夹下的 bootstrap.php 中已经对 backend 进行了 setAlias,所以通过 getAias 简单处理就可返回真实路径了
以 "//" 开头的 $view, 如 "//site/index"
匹配到这种形式执行
Yii: :$app - >getViewPath()
,getViewPath 方法事实上是:获取我们在配置文件中定义的 basePath,再根据 Yii2.0 中约定的目录结构,加上 "/views" 返回
以 "/" 开头
相当于绝对路径的方式,在当前控制器所在的 module 中进行寻找,这个 controller 对象的 module 成员保存的是当前控制器所在的 module 对象,这个保存是在 createControllerByID 中调用 Yii::createObject() 的时候进行赋值的.
yii\base\Controller
中的构造方法有一句:
$this - >module = $module;
controller 对象调用 View 进行视图渲染时,将对象自身传递给了 view,也就是 $context,如果这个控制器实现了 ViewContextInterface 接口(接口中就定义了一个 getViewPath 方法),那么直接调用控制器的 getViewPath
最后一种情况就是根据当前视图文件的文件路径来返回视图路径(这种应该是在视图文件中渲染其他视图的情况)
[注] 2 与 3 其实都是调用 yii\base\Module 中的 getViewPath,getViewPath 中调用了 getBasePath,区别在于 2 中用的是 Yii::$app 这个对象,Application 对象初始化的时候在 preInit 中已经对将配置文件中的 basePath 初始化并保存在了 $_basePath 中;3 中 getViewPath 调用的发起者是 controller 实例所属的 module 对象,module 对象创建的时候并未初始化 $_basePath,所以可以看到 getBasePaht 中是使用反射的方法获取当前 module 对象对应文件的路径,进而返回 basePath 的
2.2,renderFile
renderFile 的操作就比较简单了,根据视图文件的后缀来判断是否启用了相应的模板引擎,如 Smarty,Twig 等,如果都没有那就默认的 php 文件的方式 (renderPhpFile) 进行渲染,renderPhpFile 的代码非常简单:
public
function renderPhpFile($_file_, $_params_ = []) {
ob_start();
ob_implicit_flush(false);
//将我们render的参数数组extract为本地变量
extract($_params_, EXTR_OVERWRITE);
//require 我们的视图文件
require($_file_);
//清空并返回当前缓冲区的内容
return ob_get_clean();
}
经过以上的分析,一个视图文件的渲染就完成了,而我们的 Yii 框架中大多使用了布局文件.所以回到
yii\base\Controller
的 render 方法中,视图文件渲染之后返回的是一个字符串, 保存在了 $content 中,然后执行了
return $this - >renderContent($content)
,这其实就是把视图文件渲染后的结果放到布局中,这也是为什么我们的布局文件中会有这么一行代码:.renderContent 其实就是获取布局文件的路径然后在调用 View 中的 renderFile 方法,过程跟视图渲染的过程一样.
至此,Yii2.0 视图渲染过程分析完毕.
[附] render 渲染代码调用流程
来源: https://www.cnblogs.com/skyfynn/p/8320574.html