014_捆綁包與顯示模式

捆綁包(Bundlejavascript

   可以組織和優化CSS以及JavaScript文件,是由視圖和佈局引起瀏覽器向服務器請求的文件。css

顯示模式(Display Modehtml

   針對不一樣的設備採用不一樣的視圖。java

理解默認腳本庫

         在建立除Empty之外的任一MVC項目時,Visual Studio都會在Scripts文件夾中添加一組JavaScript庫,最主要並經常使用的有:jquery

  • jquery-1.8.2.js:jQuery庫,可以使得在瀏覽器中操做HTML元素變得簡單而容易,與HTML標準部分的內建API相比,其優點尤爲明顯。
  • jquery-ui-1.8.24.js:jQuery UI庫,經過HTML元素建立富用戶控件,爲Web應用程序建立美觀的UI,該庫創建在jQuery庫之上。
  • jquery.mobile-1.1.0.js:jQuery Mobile庫,爲移動設備建立富用戶控件。jQuery Mobile創建在jQuery之上,且只會添加到使用Mobile模板選項建立的項目中
  • jquery.validate.js:jQuery Validation庫,執行HTML表單元素的輸入驗證。
  • knockout-2.2.0.js:Knockout庫,將「模型-視圖-視圖模型」模式運用於Web程序的客戶端部分,做用是將Web程序中客戶端的數據從顯示給用戶的元素中分離出來。一般被稱爲MVC的瀏覽器。
  • modernizr-2.6.2.js:Modernizr庫,用於檢測瀏覽器對HTML5及CSS3的支持狀況,可以在支持狀況下使用最新功能,而在不支持時能夠優雅降級。

如下是Visual Studio及MVC專用庫:web

  • jquery-1.8.2.intellisense.js:在視圖中編寫jQuery代碼時,爲Visual Studio提供智能感應的功能。
  • jquery.unobtrusive-ajax.js:提供MVC框架漸近式Ajax特性,依賴於jQuery。
  • jquery.validate-vsdoc.js:在編寫使用jQuery驗證庫的代碼時,爲Visual Studio提供智能感應的功能。
  • jquery.validate.unobtrusive.js:提供MVC框架漸近式驗證特性,依賴於jQuery。

   對於Visual Studio及MVC專用的庫,不須要咱們作任何事情,Visual Studio會自動使用它們。ajax

   這裏列出的都是常規的版本,同時出現的還會有壓縮版——通常在發佈的時候使用,能夠節省不少空間,並減小網絡帶寬,節約網絡資源。數據庫

準備示例

項目:ClientFeatures瀏覽器

項目模板:Basic(基本)緩存

模型類:Appointment.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace ClientFeatures.Models
{
    public class Appointment
    {
        [Required]
        public string ClientName { get; set; }

        [DataType(DataType.Date)]
        public DateTime Date { get; set; }

        public bool TermsAccepted { get; set; }

    }
}

控制器:Home

using ClientFeatures.Models;
using System;
using System.Web.Mvc;

namespace ClientFeatures.Controllers
{
    public class HomeController : Controller
    {

        public ViewResult MakeBooking()
        {
            return View(new Appointment
            {
                ClientName = "Adam",
                Date = DateTime.Now.AddDays(2),
                TermsAccepted = true
            });
        }

        public JsonResult MakeBooking(Appointment appt)
        {
            // 在實際項目中,這裏是存儲新 Appointment 的語句
            return Json(appt, JsonRequestBehavior.AllowGet);
        }

    }
}

視圖:MakeBooking.cshtml

@model ClientFeatures.Models.Appointment

@{
    ViewBag.Title = "Make A Booking";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        OnSuccess = "processResponse"
    };
}

<h2>Book an Appointment</h2>

<link rel="stylesheet" href="~/Content/CustomStyles.css" />
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>

<script type="text/javascript">
    function processResponse(appt) {
        $('#successClientName').text(app.ClientName);
        $('#successDate').text(processDate(app.Date));
        switchViews();
    }

    function processDate(dateString) {
        return new Date(parseInt(dateString.substr(6, dateString.length - 8))).toDateString();
    }

    function switchViews() {
        var hidden = $('.hidden');
        var visible = $('.visible');
        hidden.removeClass('.hidden').addClass('.visible');
        visible.removeClass('.visible').addClass('.hidden');
    }

    $(document).ready(function () {
        $('#backButton').click(function (e) {
            switchViews();
        })
    });

</script>

<div id="formDiv" class="visible">
    @using (Ajax.BeginForm(ajaxOpts))
    {
        @Html.ValidationSummary(true)
        <p>@Html.ValidationMessageFor(m => m.ClientName)</p>
        <p>Your name: @Html.EditorFor(m => m.ClientName)</p>
        <p>@Html.ValidationMessageFor(m => m.Date)</p>
        <p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
        <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
        <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms $ conditions</p>
        <input type="submit" value="Make booking" />
    }
</div>

<div id="successDiv" class="hidden">
    <h4>Your appointment is confirmed</h4>
    <p>Your name is: <b id="successClientName"></b></p>
    <p>The date of your appointment is: <b id="successDate"></b></p>
    <button id="backButton">Back</button>
</div>

         上面視圖中,兩個div元素,一個會在視圖第一次渲染時顯示給用戶,它含有一個已啓用Ajax的表單。當表單被遞交併在接受到服務器對Ajax請求做出的相應時,應採起的應對方式是隱藏第一個div中的表單,同時顯示另外一個div元素,顯示預定所肯定的細節。

         視圖中link元素中引用的/Content文件夾中的CustomStyles .css文件爲自定義添加的CSS文件,內容以下:

div.hidden {
    display: none;
}

div.hidden {
    display: block;
}

         此處但願建立一個用於複雜視圖的典型場景,但又不須要建立一個複雜的應用程序,這就是爲何添加的CSS文件只有兩盒樣式,也是爲何對一個十分簡單的視圖使用一連串的jQuery庫的緣由。其關鍵思想是有許多文件要進行管理。當在編寫一個實際程序時,所受到的考驗偏偏是須要在視圖中處理許多腳本和樣式文件。

         如今啓動程序看就能夠看到效果了:

                         

 

管理腳本與樣式表

         上面示例中視圖代碼中混用了Scripts文件夾中的庫、Content文件夾中的CSS樣式表、本地的Script元素,還有HTML和Razor標記。可是,按照示例那樣寫仍是存在着一些隱含的問題,咱們能夠將腳本和樣式表進行管理以改善。

腳本及樣式表加載的資料分析

         在對一個項目進行優化以前,最好是先作一些測量。對於本例使用的是IE11的「F12工具」進行測量。

         啓動程序,導航至/Home/MakeBooking,而後按F12鍵。以後點擊「網絡」選項卡( ),點擊「網絡流量捕獲」按鈕( )。重載瀏覽器內容(刷新頁面)將會獲得以下圖這樣的結果:

 

         下面列出的是從上圖看到的一些關鍵數據:

  • 瀏覽器對Home/MakeBooking造成了9個請求
  • 2個請求用於CSS文件
  • 6個請求用於JavaScript文件
  • 瀏覽器發送給服務的有22322字節
  • 服務器發送給瀏覽器的有642670字節

這些都是最壞狀況下的數據,由於在加載以前已經清理了緩存。若是在現實中,瀏覽器的緩存未清理,則瀏覽器會經過以前的請求緩衝,對此會有所改善。

         若是不清理緩存,再次加載,則會是下面這種效果:

 

  • 瀏覽器對Home/MakeBooking造成了9個請求
  • 2個請求用於CSS文件
  • 6個請求用於JavaScript文件
  • 瀏覽器發送給服務的有4610字節
  • 服務器發送給瀏覽器的有158315字節

   若是注意觀察,將會發現視圖下載的JavaScript文件列表中已經從新建立了兩個常見的問題。第一個是混用了最小化的和常規的JavaScript文件。這個問題不大,但對開發期間的調試會形成必定的影響,因此,最好不用混用。

   第二個是同時下載了jQuery庫的最小化和常規版本。發送這種狀況是由於佈局也會加載一些JavaScript和CSS文件,而用戶缺少相應的手段強制瀏覽器不用下載它已經擁有的代碼。

         後面的內容,將介紹一下如何有控制地獲取腳本好CSS文件。更普遍意義上,也會展現如何減小瀏覽器須要發送給服務器的請求數,以及須要下載的數據量。

使用腳本和樣式捆綁包

         將JavaScript和CSS文件組織成捆綁包(Bundle),使其可以做爲一個單一的單元進行處理。捆綁包是在/App_Start/BundleConfig.cs文件中定義的。下面是由Visual Studio默認建立的:

using System.Web;
using System.Web.Optimization;

namespace ClientFeatures
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                        "~/Scripts/jquery-ui-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*"));

            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                        "~/Scripts/modernizr-*"));

            bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

            bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
                        "~/Content/themes/base/jquery.ui.core.css",
                        "~/Content/themes/base/jquery.ui.resizable.css",
                        "~/Content/themes/base/jquery.ui.selectable.css",
                        "~/Content/themes/base/jquery.ui.accordion.css",
                        "~/Content/themes/base/jquery.ui.autocomplete.css",
                        "~/Content/themes/base/jquery.ui.button.css",
                        "~/Content/themes/base/jquery.ui.dialog.css",
                        "~/Content/themes/base/jquery.ui.slider.css",
                        "~/Content/themes/base/jquery.ui.tabs.css",
                        "~/Content/themes/base/jquery.ui.datepicker.css",
                        "~/Content/themes/base/jquery.ui.progressbar.css",
                        "~/Content/themes/base/jquery.ui.theme.css"));
        }
    }
}

         其中靜態方法RegisterBundles會在MVC程序第一次啓動時,經過Global.asax中的Application_Start方法調用。RegisterBundles方法以一個BundleCollection對象爲參數,經過使用它的Add方法註冊新的文件捆綁包。

         也能夠分別建立用於腳本和樣式表的捆綁包,重要的是將這些文件類型分開,由於MVC框架對這些文件的優化是不一樣的。樣式是由StyleBundle類表示,而腳本是由ScriptBundle類表示。

         建立一個新的捆綁包,實際上就是在建立StyleBundle或ScriptBundle類的一個實例。它們都有一個構造函數參數,即引用捆綁包的路徑。其做用是做爲瀏覽器請求捆綁包內容的一個URL,所以,重要的是要爲這些路徑使用一個與應用程序所支持的路徑無衝突的URL方案。最安全的作法是以~/bundles或~/Content做爲起始路徑。

         一旦建立了上述的StyleBundle或ScriptBundle對象,就可使用Include方法添加捆綁包所包含的樣式或腳本文件的細節。有一些較好的作法,能夠參考下面對BundleConfig類進行的修改:

using System.Web;
using System.Web.Optimization;

namespace ClientFeatures
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/*.css"));

            bundles.Add(new ScriptBundle("~/bundles/clientfeaturesscripts").Include(
                        "~/Scripts/jquery-{version}.js",
                        "~/Scripts/jquery.validate.js",
                        "~/Scripts/jquery.validate.unobtrusive.js",
                        "~/Scripts/jquery.unobtrusive-ajax.js"));

        }
    }
}

         上述修改中,首先使用~/Content/css路徑對StyleBundle進行了修改,並將Include方法的參數改成~/Content/*.css,以可以使該捆綁包包含程序中全部的CSS文件。文件後綴.css前面的星號(*)是一個通配符,表示Content文件夾中的全部CSS文件,但這裏忽略了文件的順序,固然這在此處並不重要。——實際上,在瀏覽器中,CSS文件的加載順序是不重要的,所以使用通配符的方式是很好的選擇。可是,若是要使用CSS的樣式優先規則,則須要分別列出這些文件,以保證順序的正確。

         ScriptBundle中的路徑設置爲~/bundles/clientfeaturesscripts,這個捆綁包中使用Include方法逐一指定了須要的腳本文件,並以逗號分隔,緣由是此處只須要部分腳本文件,並關注腳本的加載和執行的順序。

         注意jQuery庫文件的指定方式:~/Scripts/jquery-{version}.js,文件名中的{version}部分相對靈活,由於這樣作,會匹配指定文件的任一版本,它會使用程序的配置,選擇該文件的常規或最小化版本。

         提示:使用常規版或最小版是由Web.config文件中的compilation元素決定的。當其debug屬性被設置爲true時,使用常規版;而當debug爲false時,則使用最小化版。

         這麼寫的好處是能夠將所使用的庫的更新爲新版本,而沒必要從新定義捆綁包。缺點是{version}標誌沒法區分同一個文件夾中同一個庫的不一樣版本。所以,必須確保Scripts文件夾中只有一個版本的庫。

運用捆綁包

         使用捆綁包以前,首先要作的是建立視圖。固然也能夠沒有這一步,但這可讓MVC框架可以爲應用程序執行最大限度的優化。

         建立一個新的文件夾,路徑及名稱爲/Scripts/Home,在該文件夾中添加一個新的腳本MakeBooking.js。這是須要遵照的約定,以便按控制器來組織各個頁面的JavaScript文件(即按控制器名(不含「Controller」部分)及動做方法名組織JavaScript腳本的約定):   

    function processResponse(appt) {
        $('#successClientName').text(appt.ClientName);
        $('#successDate').text(processDate(appt.Date));
        switchViews();
    }

    function processDate(dateString) {
        return new Date(parseInt(dateString.substr(6, dateString.length - 8))).toDateString();
    }

    function switchViews() {
        var hidden = $('.hidden');
        var visible = $('.visible');
        hidden.removeClass('hidden').addClass('visible');
        visible.removeClass('visible').addClass('hidden');
    }

    $(document).ready(function () {
        $('#backButton').click(function (e) {
            switchViews();
        })
    });

         上面代碼只是將原來視圖中的那部分腳本轉移到了獨立的js文件中。接着就是要修改視圖文件了。修改原則是但願瀏覽器只請求其所須要的文件,適當地保留須要負責副本的請求,因此能夠刪除已經建立捆綁包的那些link和script元素,保留惟一的一個指向新建的那個專門的js文件MakeBooking.js的那個script元素,具體以下:

@model ClientFeatures.Models.Appointment

@{
    ViewBag.Title = "Make A Booking";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        OnSuccess = "processResponse"
    };
}

<h4>Book an Appointment</h4>

<script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script>

<div id="formDiv" class="visible">
    @using (Ajax.BeginForm(ajaxOpts))
    {
        @Html.ValidationSummary(true)
        <p>@Html.ValidationMessageFor(m => m.ClientName)</p>
        <p>Your name: @Html.EditorFor(m => m.ClientName)</p>
        <p>@Html.ValidationMessageFor(m => m.Date)</p>
        <p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
        <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
        <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms $ conditions</p>
        <input type="submit" value="Make booking" />
    }
</div>

<div id="successDiv" class="hidden">
    <h4>Your appointment is confirmed</h4>
    <p>Your name is: <b id="successClientName"></b></p>
    <p>The date of your appointment is: <b id="successDate"></b></p>
    <button id="backButton">Back</button>
</div>

         能夠在視圖文件中引用捆綁包,但最好是建立可以多個視圖共享的捆綁包——即在佈局中運用捆綁包:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/jquery")     @RenderSection("scripts", required: false)
</body>
</html>

         捆綁包是分別使用@Styles.Render和@Scripts.Render輔助器方法添加的。爲了達到預期目標,咱們須要編輯_Layout.cshtml文件,以使用新定義的捆綁包,刪除不想使用的引用:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") </head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/clientfeaturesscriptes") 
    @RenderSection("scripts", required: false)
</body>
</html>

         好了,如今能夠看看最終生成的HTML,下面是由用於~/Content/css捆綁包的Styles.Render方法生成的結果:

<link href="/Content/CustomStyles.css" rel="stylesheet"></link>

<link href="/Content/Site.css" rel="stylesheet"></link>

         而這些是Scripts.Render方法生成的:

<script src="/Scripts/jquery-1.8.2.js"></script>

<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>

<script src="/Scripts/jquery.validate.js"></script>

<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

 

使用Scripts小節

         到如今爲止,該示例還不能很好的工做,緣由是包含在專業的js文件MakeBooking.js中的腳本代碼須要依靠jQuery爲按鈕創建事件處理程序。也就是,jQuery文件要在MakeBooking.js以前加載,但在佈局中可以看出實際的加載順序正好相反,致使視圖中的script元素出如今佈局中的script元素以前,致使按鈕不能正常工做,甚至有的瀏覽器還會報出JavaScript錯誤,好比我用的IE11就報出了以下圖的錯誤:

 

         解決上面的問題,能夠有兩種方式:

一、  將Scripts.Render調用移入視圖的head元素;

二、  利用_Layout.cshtml文件中定義的「可選腳本小節(Optional Scripts Section)」——使用這種方式其實就是定義了一個佔位符,當視圖中有可選腳本小節時,便將視圖中的實際代碼放在這兒。

下面使用的是第二種方式作的修改,請參考:

@model ClientFeatures.Models.Appointment

@{
    ViewBag.Title = "Make A Booking";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        OnSuccess = "processResponse"
    };
}

<h4>Book an Appointment</h4> @section scripts{ <script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script> } <div id="formDiv" class="visible">
    @using (Ajax.BeginForm(ajaxOpts))
    {
        @Html.ValidationSummary(true)
        <p>@Html.ValidationMessageFor(m => m.ClientName)</p>
        <p>Your name: @Html.EditorFor(m => m.ClientName)</p>
        <p>@Html.ValidationMessageFor(m => m.Date)</p>
        <p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
        <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
        <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
        <input type="submit" value="Make booking" />
    }
</div>

<div id="successDiv" class="hidden">
    <h4>Your appointment is confirmed</h4>
    <p>Your name is: <b id="successClientName"></b></p>
    <p>The date of your appointment is: <b id="successDate"></b></p>
    <button id="backButton">Back</button>
</div>

         這樣一來,script小節會出如今佈局中對Scripts.Render的調用以後,視圖專用的腳本也就會在jQuery以後才加載,按鈕元素也就能正常工做,且不會再報相似以前那種錯誤了。

         這裏仍是要提醒一下,在使用捆綁包時,這是常常會出現的錯誤,有必要對其加以重視,這也是爲何專門把這一問題介紹一下的緣由。

注:關於可選腳本小節,可參考「視圖」一章內關於「對Razor視圖添加動態內容」中的「使用分段」小節的介紹,理解分段的概念及其工做原理。

修改後的資料分析

         如今清理一下緩存,並導航到/Home/MakeBooking,看看這一調整的效果:

 

         下面是主要信息的摘要:

  • 瀏覽器對/Home/MakeBooking造成了8個請求(主要請求項)
  • 2個用於CSS文件的請求
  • 5個用於JavaScript文件的請求
  • 從瀏覽器發送給服務的內容有23237字節
  • 從服務器發給瀏覽器的有496183字節

對比以前的642670字節,大約減小了1/3。

若是將程序從調試模式切換到部署配置時,將會更明顯,具體作法是將Web.config文件中compilation元素上的debug屬性設置爲false便可:

  <system.web><compilation debug="false" targetFramework="4.5" /></system.web>

         此時再次重複上述步驟,將獲得以下結果:

 

         如下是摘要:

  • 造成的請求爲4個
  • 1個請求用於CSS
  • 2個請求用於JavaScript
  • 瀏覽器發送給服務的內容有1288字節
  • 服務器發送給瀏覽器的內容有126607字節

   此次對CSS和JavaScript文件的請求變少了,緣由是MVC框架在部署模式中,將一系列樣式表和JavaScript文件聯繫在一塊兒,並作了最小化,以使一個捆綁包中的內容可以經過一個單一的請求進行加載。若是查看程序渲染的HTML,就能明白是怎麼實現的了:

   Styles.Render方法生成的結果:

<link href="/Content/css?v=6jdfBoUlZKSHjUZCe_rkkh4S8jotNCGFD09DYm7kBWE1" rel="stylesheet"/>

         Scripts方法生成的結果:

<script src="/bundles/clientfeaturesscripts?v=KyclumLmAXQGM1-wDTwVUS31lpYigmXXR8HfERBGk_I1"></script>

         這些長長的URL以一個單一的數據庫的形式,請求了一個捆綁包的內容。MVC框架對CSS數據所採起的最小化與JavaScript文件是不一樣的,這是爲何將樣式表和腳本分別放在不一樣捆綁包中的緣由。

         雖然還能夠繼續優化,但對於咱們這樣的一個簡單的程序,這已經足夠了,好比若是將MakeBooking.js文件也添加到捆綁包中,還能夠再消除一個請求,但這已經意義不大了。一開始那種視圖專用的內容與佈局的代碼混合在一塊兒的作法是提倡的,若是在更復雜的系統中,也會會建立第二個捆綁包,讓它包含更多的自定義代碼,但這裏只是一個簡單的示例程序,優化的準則是要清楚到什麼程度爲止。——目前,對應這樣的一個示例程序已經達到目標了。

面向移動設備

         移動設備和桌面設備有着很大的區別,最容易引發問題的是觸摸交互和受限制的屏幕尺寸。

         更多的移動開發方面的知識不是咱們這裏關注的,在此,只須要作些瞭解便可。

觀察應用程序

         開發一個移動設備的程序最好是從零開始創建一個新項目。但這裏採起的辦法是在原示例項目(ClientFeatures)基礎中添加支持。

         首先,須要一個可以模擬移動端展現的程序的工具,書中推薦的是Opera Mobile Emulator(Opera移動設備模擬器),該模擬器是免費的,能夠從www.opera.com/developer/tools/mobile上下載。固然也可使用Windows Phone、Android以及Blackberry等,但這些每每都很慢,用起來也很痛苦,由於它們都不是僅模擬瀏覽器,而是模擬了整個移動操做系統。

         我在學習的時候下載的是「Opera Mobile Classic Emulator 12.1 for Windows」版本。安裝完成後啓動,並將模擬器的設置使用HTC Hera的配置。導航到/Home/MakeBooking將會看到結果以下:

 

使用移動專用的佈局和視圖

         因爲程序如此之簡單,以致於沒有什麼醜陋的地方須要修改,只是它並無在觸摸屏上以易於操做的方式顯示。後面,簡單介紹如何建立移動專用版本的視圖和佈局,以方便對其內容的調整來適應目標設備。其實,這個過程很簡單,須要作的只是建立其名稱帶有.Mobile的視圖和佈局便可:

         視圖:MakeBooking.Mobile.cshtml(注意,在擴展名前面加了.Mobile)

@model ClientFeatures.Models.Appointment

@{
    ViewBag.Title = "Make A Booking";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        OnSuccess = "processResponse"
    };
}

<h4>This is an MOBILE View</h4>

@section scripts{
    <script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script>
}

<div id="formDiv" class="visible">
    @using (Ajax.BeginForm(ajaxOpts))
    {
        @Html.ValidationSummary(true)
        <p>@Html.ValidationMessageFor(m => m.ClientName)</p>
        <p>Name: </p><p>@Html.EditorFor(m => m.ClientName)</p>
        <p>@Html.ValidationMessageFor(m => m.Date)</p>
        <p>Date: </p><p>@Html.EditorFor(m => m.Date)</p>
        <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
        <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
        <input type="submit" value="Make booking" />
    }
</div>

<div id="successDiv" class="hidden">
    <h4>Your appointment is confirmed</h4>
    <p>Your name is: <b id="successClientName"></b></p>
    <p>The date of your appointment is: <b id="successDate"></b></p>
    <button id="backButton">Back</button>
</div>

         注意,爲了可以使input元素的標籤可以單獨顯示,上面對其作了一點改動。另外也對h4標題元素的內容作了調整,以使其更加醒目。

         MVC框架可以智能識別並運用移動視圖。好比若是使用桌面瀏覽器,將使用/Views/Home/MakeBooking.cshtml視圖;若是是移動瀏覽器模擬器,則將看到由/Views/Home/MakeBooking.Mobile.cshtml視圖渲染的HTML,如圖:

 

         MVC檢測瀏覽器的方式是依賴於.NET框架包含的一組文件,這組文件是在相似C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Browsers目錄的位置的,對於不一樣的系統會有些不一樣。該目錄中有一系列文件,其中的每個都與一個移動瀏覽器相關聯。當MVC框架接收到移動模擬器的請求時,會使用「用戶代理」字符串,這種用戶代理字符串是全部瀏覽器都會發生的,因而便能肯定該請求發自移動設備,也就是爲何會自動使用MakeBooking.Mobile.cshtml視圖了。

         上述目錄中的文件大致參考下圖:

 

建立自定義顯示模式

         默認狀況下,MVC框架只檢測移動設備,並將其餘方面的各項工做做爲一個單一的類別加以處理。若是需對不一樣種類的設備採起具體的響應,則須要建立本身的顯示模式。

         這裏選擇使用Amazon的Kindle Fire配置資料,它是Opera Mobile Emulator中自帶的一種配置。因爲它是平板設備上的一款瀏覽器,所以,它發生的用戶代理字符串與.NET Framwork指望的從Opera Mobile不一樣,所以默認將會使用標準的MakeBooking.cshtml視圖。

         下面經過對Global.asax文件進行修改,以使其可以爲Opera Tablet(指平板電腦的Opera瀏覽器)建立一個新的顯示模式:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Web.WebPages; namespace ClientFeatures
{

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("OperaTablet") { ContextCondition = (context => context.Request.UserAgent.IndexOf("Opera Tablet", StringComparison.OrdinalIgnoreCase) >= 0) }); 
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

         經過上面的修改,MVC框架將查找諸如/Views/Home/MakeBooking.OperaTablet.cshtml的視圖。

提示:MVC框架會依次檢查DisplayModeProvider.Instance.Modes屬性所返回的集合中的每一種顯示模式,並在發現其ContextCondition表達式返回true時,中止查找。有一個默認的備用顯示模式可以匹配任何請求,並使用默認視圖。須要確保顯示模式可以在該備用模式以前獲得查詢,因此,上述代碼中使用了Insert方法將對象放到了零索引的位置。

         爲了利用這一顯示模式,此處添加了一個新的視圖文件:MakeBooking.OperaTablet.cshtml:

@model ClientFeatures.Models.Appointment

@{
    ViewBag.Title = "Make A Booking";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        OnSuccess = "processResponse"
    };
}

<h4>This is the OPERA TABLET View</h4>

@section scripts{
    <script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script>
}

<div id="formDiv" class="visible">
    @using (Ajax.BeginForm(ajaxOpts))
    {
        @Html.ValidationSummary(true)
        <p>@Html.ValidationMessageFor(m => m.ClientName)</p>
        <p>Name: </p><p>@Html.EditorFor(m => m.ClientName)</p>
        <p>@Html.ValidationMessageFor(m => m.Date)</p>
        <p>Date: </p><p>@Html.EditorFor(m => m.Date)</p>
        <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
        <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
        <input type="submit" value="Make booking" />
    }
</div>

<div id="successDiv" class="hidden">
    <h4>Your appointment is confirmed</h4>
    <p>Your name is: <b id="successClientName"></b></p>
    <p>The date of your appointment is: <b id="successDate"></b></p>
    <button id="backButton">Back</button>
</div>

         下面是修改後的效果:

 

         到這裏,已經簡單地介紹瞭如何建立自定義顯示模式,雖然上圖中的顯示仍是不很近人意,但也能明確地說明了其原理。

注意:在建立自定義顯示模式時要當心。它很容易捕獲不正確的客戶端請求,或漏掉在某個特定類型客戶端上運行不一樣版本瀏覽器之間的一些微妙的差別,建議用一系列設備進行完全的測試。

相關文章
相關標籤/搜索