# 視圖 & 模板
<p class="uk-article-lead">控制器處理傳入的請求時,視圖負責渲染響應。為了實現這一點,它使用一個模板引擎。在Pagekit,您可以使用純PHP模板或 twig 模板引擎。</p>
[toc=2]
## 已渲染的視圖響應
渲染視圖最常見的方式是從控制器操作中返回一個數組。使用 `$view` 屬性將參數傳到你的視圖渲染器。
```php
public function indexAction($name = '')
{
return [
'$view' => [
// 在網站的 <title> 中渲染
'title' => 'Hello World',
//已渲染的視圖文件
'name' => 'hello:views/index.php',
],
// 傳遞參數到視圖文件
'name' => $name
];
}
```
已渲染的視圖文件看起來是這樣:
```php
<!-- packages/pagekit/extension-hello/views/index.php -->
<h1>Hello <?= $name ?></h1>
<p>
...
</p>
```
這個視圖默認包裹在主要布局(main layout)中。要避免此行為,你可以在 `$view` 數組中修改 `'layout' => false`。
## 手動渲染視圖
可以手動訪問 `View` 服務來渲染模板文件。這會在動態判斷加載哪些視圖時派上用場。注意,在下面的例子中 `hello:` 指的是使用 [模塊](224131)中的源碼簡寫方法定義的簡寫。。
```php
use Pagekit\Application as App;
class MyController {
public function anotherViewAction()
{
return App::view()->render('hello:views/view.php', ['id' => 1]);
}
}
```
相關視圖文件:
```HTML
<!-- packages/pagekit/extension-hello/views/index.php -->
<h1>You are viewing article number <?= $id ?></h1>
<p>
...
</p>
```
## Templating
這些視圖是使用 PHP 模板引擎渲染的,模板引擎提供了已定義的全局模板變量和一系列視圖助手。
### 引入其他視圖
使用 `$view`助手在視圖中渲染子視圖。 `render` 方法評估并返回模板文件的內容。這與在控制器中手動渲染視圖是相同的。
```php
$view->render('hello:views/view.php', ['id' => 1])
```
### 鏈接到路由
正如前面看到的,每個路徑都有一個名稱,你可以動態生成鏈接到具體路線的鏈接。`Url`暴露了 `UrlProvider`的功能。
```HTML
<a href="<?= $view->url('@hello/default/view') ?>">View all articles</a>
<a href="<?= $view->url('@hello/default/view', ['id' => 23]) ?>">View article 23</a>
```
可以鏈接到資源,比如圖片或其他文件,使用 URL 提供者(`UrlProvider`)的 `getStatic($path)` 方法。
```HTML
<img src="<?= $view->url()->getStatic('hello:extension.svg') ?>" alt="Extension icon" />
```
## 處理資源/Assets
資源是指項目中需要的靜態文件,包括CSS、JS和圖片文件等。
### 生成靜態資源的 URL
要生成靜態資源的路由,使用`UrlProvider` 類的 `getStatic` 方法。
```php
<img src="<?= $view->url()->getStatic('my-assets/image.jpg') ?>">
```
### 在視圖文件中引入 CSS
在視圖文件中引入 CSS,調用 `View` 中的 `style` 助手。
```php
$view->style($id, $path [, $dependencies ])
```
第一個參數是樣式表的唯一標識符。如果為多個樣式表使用同一個標識符,Pagekit只會引入最后一個。第二個參數是樣式表的路徑,可以使用 `theme:` 作為 'theme'包根目錄的參照。可以通過可選的第三個參數定義依賴。
Example:
```
<?php $view->style('theme', 'theme:css/theme.css') ?>
<?php $view->style('theme', 'theme:css/theme.css', 'uikit') ?>
<?php $view->style('theme', 'theme:css/theme.css', ['uikit', 'somethingelse']) ?>
```
**Note** 這并不會直接在 HTML 中輸出,它會將 CSS 文件添加到 Pagekit 的資源管理器(Pagekit Asset Manager)。樣式表會引入到主題的 `<head>` 部分。
### 在視圖文件中引入 JS
要在模板中引入 javascript 文件,調用 `#view` 對象的 `script` 助手,作用和 `style` 助手一樣。
```php
$view->script($id, $path [, $dependencies ])
```
第一個參數是腳本資源的唯一標識符。如果為多個腳本使用了相同的標識符,Pagekit 只會引入最后一個。第二個參數是腳本的路徑,可以使用 `theme:` 作為 'theme'包根目錄的參考。可以通過可選的第三個參數定義依賴。
Example:
```
<?php $view->script('theme', 'theme:js/theme.js') ?>
<?php $view->script('theme', 'theme:js/theme.js', 'jquery') ?>
<?php $view->script('theme', 'theme:js/theme.js', ['jquery', 'uikit']) ?>
```
**Note** 在內部,`style()` 和 `script()` 各自使用它們自己的資源管理器。這些都是彼此分開的,可以無沖突地為 CSS 和 JS 文件指定相同的名稱(前面的例子里都叫做 `theme`)。然而,兩個腳本或樣式表不能使用相同的名稱。例如,在添加兩個樣式表時,一個可以稱為 'theme',另一個稱為 'custom'。
### 異步和延遲腳本執行/Async and deferred script execution
默認地,腳本會在已渲染的 HTML 標簽的 head 部分被引入,是即時獲取并執行的。之后,瀏覽器才會繼續解析頁面。
要改變這樣的行為,可以在加載腳本時使用關鍵字 `async` 和 `defer`。在 PHP 代碼中設置適當的選項,于是在最后的 `<script>` 標簽中將會有這些屬性。
|屬性 | 描述|
|--------- | -----------|
|`async` | 告訴瀏覽器異步執行腳本,這意味著就算是在執行腳本,頁面解析也會繼續。|
|`defer` | 告訴瀏覽器在頁面解析完畢后再執行腳本。瀏覽器對于這個 HTML 特性的支持并不完美,只是會在腳本執行的順序很重要時使用它才會導致一些問題。|
Example: 延遲執行,無依賴。
```
<?php $view->script('theme', 'theme:js/theme.js', [], ['defer']) ?>
```
Example: 有依賴的延遲和異步執行。
```
<?php $view->script('theme', 'theme:js/theme.js', ['jquery', 'uikit'], ['defer', 'async']) ?>
```
## Twig 模板
可以在主題和擴展模板中使用 Twig 來替代原生的 PHP 模板。在 Twig 官方文檔的基礎[Twig 語法和特性](http://twig.sensiolabs.org/doc/templates.html) 上找到相關資料。類似于官方使用默認的PHP模板制作的的 Hello 主題,還有一個 [boilerplate Twig theme](https://github.com/florianletsch/theme-twig) ,可以用它作為起點。
### 使用 Twig 構建主題
默認地,Pagekit 主題用到的主要模板文件,位于 `views/template.php`,并且它是使用默認的 PHP 渲染器進行渲染的。在主題的 `index.php` 中,你可以另外定義一個布局文件用作主要模板。
```
/**
* 使用 Twig 模板修改默認布局
*/
'layout' => 'views:template.twig',
```
文件擴展名 `*.twig` 將導致 Pagekit 使用 Twig 引擎來渲染模板。主題的 `views/template.twig` 的簡單例子,看起來是這樣:
```
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{ view.render('head') | raw }}
{{ view.style('theme', 'theme:css/theme.css') }}
{{ view.script('theme', 'theme:js/theme.js') }}
</head>
<body>
<!-- Render logo or title with site URL -->
<a href="{{ view.url().get() }}">
{% set logo = params.logo %}
{% if logo %}
<img src="{{ logo }}" alt="Logo">
{% else %}
{{ params.title }}
{% endif %}
</a>
<!-- Render menu position -->
{% if view.menu().exists('main') %}
{{ view.menu('main') | raw }}
{% endif %}
<!-- Render widget position -->
{% if view.position().exists('sidebar') %}
{{ view.position('sidebar') | raw }}
{% endif %}
<!-- Render system messages -->
{{ view.render('messages') | raw }}
<!-- Render content -->
{{ view.render('content') | raw }}
<!-- Insert code before the closing body tag -->
{{ view.render('footer') | raw }}
</body>
</html>
```
如你所見,可以為輸出的已轉義內容使用基礎的 Twig 語法(`{{ ... }}`),未轉義內容使用 (`{{ ... | raw }}`)。默認的控制結構,比如 `if` 和 `for` 都是可用的,更多詳情查閱[Twig 語法](http://twig.sensiolabs.org/doc/templates.html) 文檔。
Pagekit 的視圖助手和函數通過可以在模板中用作 `view` 的視圖渲染器暴露。PHP 表達式可以輕松轉換為 Twig 語法,例如為當前頁面添加 CSS 文件:
```html
<!-- PHP version -->
<?php $view->style('theme', 'theme:css/theme.css'); ?>
<!-- Twig version -->
{{ view.style('theme', 'theme:css/theme.css') }}
```