視圖
視圖,你所看見的部分。
- <?php
- echo 'hello, world';
從簡單開始理解
這就是個視圖文件中的代碼,沒錯就這么簡單。視圖,實際上是在 MVC 這種架構上提出的。MVC 中,視圖負責呈現數據。因此可以說只要是輸出了數據的,都叫做視圖。
在沒有使用框架的時候,業務邏輯、數據的讀寫、組織和展示都是在一堆代碼里,難以剝離,隨着項目增大變得越來越難以維護。MVC 有效的分離了三者,各司其職。視圖作為呈現數據的,只負責組織、展示,不再負責讀寫和業務邏輯。
既然視圖只負責呈現數據,那么單獨成一個文件,這個文件內的代碼絕對不要讀取數據庫、做業務判斷,那么就算你將視圖獨立出來了。這時候大多數人會想到,視圖中的數據從哪來呢?
我們以一個簡單的例子實現一個業務邏輯和視圖分離的結構。
文件 controller.php
,代碼如下
- <?php
- $time = time();
- $string = ($time % 2) == 0 ? '偶數' : '奇數';
- // 加載視圖
- include 'view.php';
文件 view.php
,代碼如下
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>視圖實例</title>
- </head>
- <body>
- <p>時間戳 <?=$time?> 是 <?=$string?></p>
- </body>
- </html>
代碼寫好,運行后結果是預期的,對嗎?
這兩個文件中,毋庸置疑 view.php
就是視圖文件,而 controller.php
你可以當做一個控制器,因為它實現了主要的業務邏輯(雖然極其簡單)。盡管簡潔,但在結構上我們已經完全分離。
事實上,目前所有的框架的視圖的實現都大致如此,沒什么特別的、神秘的地方,只是說框架它做了更多的事,比如驗證視圖文件的合法性、通過更少的參數去加載視圖等等。
如何傳遞一個變量到視圖文件?
我相信很多人在這個上面有着很多的疑惑。比如我們常看到這樣向視圖傳遞變量:
- // laravel 通過視圖類的 with 方法傳遞
- View::make('view')->with('value', '實際的數據');
- // Smarty 模板引擎通過 assign 方法向最終的編譯好的視圖傳遞變量
- $smarty->assign('value', '實際的數據');
- // ...
實際上,在上面的邏輯與視圖分離的例子中已經說了。當 include 或 require 一個文件時,該文件會繼承引入他的那段代碼的作用域。php 官方文檔是這么說的:
當一個文件被包含時,其中所包含的代碼繼承了 include 所在行的變量范圍。從該處開始,調用文件在該行處可用的任何變量在被調用的文件中也都可用。不過所有在包含文件中定義的函數和類都具有全局作用域。
如果 include 出現於調用文件中的一個函數里,則被調用的文件中所包含的所有代碼將表現得如同它們是在該函數內部定義的一樣。所以它將遵循該函數的變量范圍。此規則的一個例外是魔術常量,它們是在發生包含之前就已被解析器處理的。
實戰 —— 實現一個視圖類
假設我們有一個視圖類,定義在文件 View.php
:
- <?php
- class View
- {
- protected $data = [];
- public function display($file)
- {
- extract($this->data);
- include $file;
- }
- public function assign($key, $value)
- {
- $this->data[$key] = $value;
- }
- }
我們現在有模板文件,定義在文件 Template.php:
- <!DOCTYPE html>
- <html>
- <head>
- <title><?=$title?></title>
- <meta charset="utf-8">
- </head>
- <body>
- <h1><?=$title?></h1>
- <p>
- <?=$content?>
- </p>
- </body>
- </html>
然后我們的主要業務邏輯代碼如下:
- <?php
- // 包含視圖類文件
- include 'View.php';
- $view = new View();
- $view->assign('title', '視圖測試');
- $view->assign('content', '這是一個視圖類的示例');
- $view->display('Template.php');
輸出的內容大家都已經看得出來了,我就不截圖了。
至此,大家應該都明白如何實現一個視圖了吧?其實原理就這么簡單。
模板引擎
模板引擎的誕生實際上是將視圖拆分出來以后的事兒。大家看到了,上面例子中,所有模板中的變量,都是通過原生的 php 語法進行輸出。假如單純是輸出內容,其實不用模板引擎也可以,但是一旦涉及到需要復用模板或者對視圖文件進行模塊化的拆分,實現諸如繼承、布局之類的功能時,原生的 php 語法似乎需要寫更為復雜的代碼才能實現。
很顯然,我們不應該在視圖文件內寫超出職責或過於繁雜的額外代碼,因此,模板引擎就顯得十分必要。
模板引擎的用處並不是所謂的方便前端的美工和網頁設計師,因為原生的語法比很多模板引擎語法更為簡潔。實際上,模板引擎最主要的任務是簡化在視圖上的額外代碼,比如處理布局、模板繼承等等。
說了那么多,如何實現模板引擎?
其實模板引擎就是將一個指定的模板標記通過正則匹配,替換成相應的合法 php 語句而已。