﻿# ASP.NET MVC 開發心得分享 (26)：關於 Razor 的運作原理與改進特性

ASP.NET MVC 3 開始提供的 Razor V1 改變了以往使用 WebFormView 的寫作習慣，讓我們在 **檢視頁面** (View Page) 中的 HTML 與伺服器端的語法 (Razor) 混和得更漂亮，不再有醜陋的 <% … %> 符號了。然而從 ASP.NET MVC 4 開始，在 Razor 推出了第二版，有許多增強的特性，本篇文章將介紹這些增強的特性。而到了 ASP.NET MVC 5 推出了 Razor V3 版，這版只有修正一些程式錯誤，並沒有功能增強。

由於使用 Razor 語法的檢視頁面，背後採用的是 ASP.NET Web Pages 技術，整個頁面都會交由 ASP.NET Web Pages 進行控管。而事實上，Razor 頁面則是一種「動態編譯」的技術，當 ASP.NET MVC 網站在執行的時候，會自動監視所有 \*.cshtml 檔案的變更，只要有偵測到檔案異動，就會自動重新產生新的、相對應的 \*.cs 程式碼，預設這些動態產生的程式碼會被寫入到 Temporary ASP.NET Files 資料夾下，可能的路徑有：

-   執行在 x64 架構下：  
    C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\Temporary ASP.NET Files
-   執行在 x86 架構下：  
    C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\Temporary ASP.NET Files

這個暫存路徑其實也可以透過 web.config 進行設定，設定的範例如下：

```
<system.web>   <compilation tempDirectory="G:\TempASP.NETFiles\">      ...   </compilation></system.web>
```

若你想看看這些 Razor 頁面產生的 \*.cs 檔案在哪，你可以隨便找一張 View 並在 Razor 頁面的最上面第一行加上 **@model <>** 故意寫出一個錯誤的 Razor 語法，當顯示例外訊息時，就會看到該檔案產生的 \*.cs 檔案路徑了，如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/5d3b2f7996bad8103301-image_2752.png)

執行該頁面就會得到以下錯誤，錯誤訊息下方的「原始程式檔」就是這張 Razor 所建立的 \*.cs 檔案，檔名當然是一串看不懂的亂碼。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/6c07d85bd5acfda8fbaf-image_2753.png)

接著你將該程式碼打開，就可以看到完整的 \*.cs 檔案：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a190815f555d48b53136-image_2754.png)

從這個檔案你就可以大致上猜到 Razor 使怎樣的運作原理：

1.  網站執行時，先找出所有 \*.cshtml 頁面，並將所有頁面轉換成 \*.cs 格式的程式碼。
    
2.  轉換成 \*.cs 格式的程式碼的過程中，會解析 Razor 專屬的語法，並轉換成標準的 C# 程式碼。
    
    -   例如 @model, @section, @Html.Action, .. 諸如此類的語法。
        
3.  轉換成功後，會交由 C# 編譯器進行程式碼編譯，然後轉成 .NET 組件 (\*.dll) 並自動載入到 AppDomain 裡
    
4.  因此 ASP.NET MVC 就可以順利執行這些 Razor 頁面。
    

所以，ASP.NET MVC 的 Razor 頁面，會有一個轉換成 C# 的過程，也就是說他並不是一個單純的 HTML，而是可以設計出一些好用的功能與特性。例如 ASP.NET MVC 6 開始將會有個 [TagHelper](https://learn.microsoft.com/en-us/archive/blogs/cdndevs/a-complete-guide-to-the-mvc-6-tag-helpers) 功能，這個功能可以伺服器端的輸出更加「標籤化」，這部分我之後會有專文介紹。

當瞭解了 Razor 的潛力無窮後，我就開始來介紹這些新增的特性：

### 更強大的 URL 解析能力

早期的 ASP.NET MVC 要在 View 裡面載入一個 JavaScript 檔案，必須用以下語法：

-   **<script src='@Url.Content("~/Scripts/jquery-1.10.2.js")'></script>**
    

現在，你可以很輕鬆地改寫成以下語法：

-   **<script src="~/Scripts/jquery-1.10.2.js"></script>**
    

不僅僅是 JavaScript 而已，只要 HTML 標籤內的任意屬性 (Attributes)，都可以使用 ~/ 語法來代表網站根目錄。例如我們用 Angular 的 ng-src 屬性，他也是可以認得的：

-   **<img ng-src="~/Content/test.jpg"/>**
    

不過要特別注意的地方是，如果你用 HTML5 的 **data-\*** 屬性 Razor 是不會幫你轉換 **~/** 的！

以下範例用到的 data-uri 由於屬於 HTML5 的自訂擴充屬性，所以 Razor 預設不會幫你解析 ~/ 這個語法：

-   **<button data-uri="~/Content/test.txt">My Button</button>**
    

### 條件式屬性輸出 (Conditional Attribute)

我們以一個 HTML 表單欄位為例，假設我們要透過一個 ViewBag.IsAutoFocus 屬性來控制 View 頁面上的某個 input 欄位是否要設定為自動聚焦，我們可以在 input 標籤中加上 [autofocus](http://www.w3schools.com/tags/att_input_autofocus.asp) 屬性 (這是 HTML5 的新增屬性)，例如：

-   <input type="text" value="" autofocus />
    
-   <input type="text" value="" autofocus\="autofocus" />
    

※ 上述兩行都是有效的 HTML5 語法。

然後要透過 ViewBag.IsAutoFocus 來控制是否輸出 autofocus 屬性，以前就非常麻煩，你可能會這樣寫：

**<input type="text" value=""  
       @if(ViewBag.IsAutoFocus) { <text>autofocus</text> } />**

而在 Razor V2 之後，你可以改成以下語法：

```
@{    ViewBag.IsAutoFocus = true;}<input type="text" value="" autofocus="@ViewBag.IsAutoFocus" />
```

這段輸出的 HTML 將會是：

```
<input type="text" value="" autofocus="autofocus" />
```

如果是以下程式，也就是說如果將 ViewBag.IsAutoFocus 改為 false：

```
@{    ViewBag.IsAutoFocus = false;}<input type="text" value="" autofocus="@ViewBag.IsAutoFocus" />
```

這段輸出的 HTML 將會是：

```
<input type="text" value="" />
```

  

是不是語法漂亮許多呢！ **;-)**

我還另外在 [.NET Fiddle](https://dotnetfiddle.net/) 建立了一個[範例](https://dotnetfiddle.net/168F8n)，你也可以看看！

基本上這個例子是展示，當你想輸出一個 CSS 的 class 到 HTML 標籤中時，如果該 class 屬性的內容為 null 時，那麼 Razor 將會連同 class 一起隱藏起來，這也算是非常貼心的功能！

<iframe height="655" src="https://dotnetfiddle.net/Widget/168F8n" frameborder="0" width="100%"></iframe>

**相關連結**

-   [The Morning Brew - Chris Alcock ? Relocating Temporary ASP.NET Files](http://blog.cwa.me.uk/2007/10/15/relocating-temporary-aspnet-files/)
    
-   [Improvements in Razor in ASP.NET MVC 4 | Shravan Weblog](https://shravan.dev/improvements-in-razor-in-asp-net-mvc-4/)
    
-   [asp.net - What's new in Razor 3? - Stack Overflow](http://stackoverflow.com/questions/19612278/whats-new-in-razor-3)
