014_捆綁包與顯示模式

来源:http://www.cnblogs.com/KeSaga/archive/2016/06/03/5556098.html
-Advertisement-
Play Games

捆綁包(Bundle): 能夠組織和優化CSS以及JavaScript文件,是由視圖和佈局引發瀏覽器向伺服器請求的文件。 顯示模式(Display Mode): 針對不同的設備採用不同的視圖。 理解預設腳本庫 在創建除Empty以外的任一MVC項目時,Visual Studio都會在Scripts文 ...


捆綁包(Bundle

   能夠組織和優化CSS以及JavaScript文件,是由視圖和佈局引發瀏覽器向伺服器請求的文件。

顯示模式(Display Mode

   針對不同的設備採用不同的視圖。

理解預設腳本庫

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

  • 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專用庫:

  • 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會自動使用它們。

   這裡列出的都是常規的版本,同時出現的還會有壓縮版——一般在發佈的時候使用,可以節省很多空間,並減少網路帶寬,節約網路資源。

準備示例

項目: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就報出瞭如下圖的錯誤:

 

         解決上面的問題,可以有兩種方式:

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

2、  利用_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><

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這幾天一直在搞linux下麵的.net mvc的部署工作,遇到了很多問題,還好有一些朋友的幫助,問題才得到瞭解決! 環境:Linux+Mongo+Jexus 希望的結果:直接運行windows+vistualstudio開發的MVC網站 遇到的問題:可以解析,但出現一些運行時(CLR)的錯誤 截圖 ...
  • 關於Linux的資源我瞭解還是比較少的,因為我最討厭用命令行了(那是我大學時代的陰影啊!)。這個資源收集很久了一直沒有分享出來,因為我對Linux的瞭解真的很少,不知道怎麼去描述,但是今天分享出來我想讓跟我一樣不太懂的和想學習一下Linux的小伙伴一起學習。 直接點擊小就可以下載哦! ├─lesso ...
  • .\usb\USB\usb_pwr.h(54): error: #20: identifier "bool" is undefinedusb\USB\usb_pwr.h(54): error: #20: identifier "bool" is undefinedusb-driver\STM32_U ...
  • 很久沒用筆記本上的ubuntu,用不順手,比在公司調教了半年多的電腦差遠了。一步一步來。先解決最不順手的三件事 1.su認證失敗。 新安裝的ubuntu系統是無法切換到root賬戶的,得做一番修改 sudo passwd 密碼: 輸入新的UNIX密碼: 確認密碼: 這樣就可以了 2.文件夾里打開終端 ...
  • 介紹 comm命令可以對兩個已排序好的文本的內容進行交集和差集的對比,記住必須是已排序過的文件;可以使用sort命令對沒有排序的文件進行排序,comm命令在對比結果中會產生三列分別是:在A中不在B中的內容,在B中不在A中的內容,AB的交集的內容。 事例 [root@localhost test]# ...
  • 作為一個菜鳥,對於SSL證書,我瞭解不多,只知道用了它網站更安全,所以這次使用SSL證書途中遇到各方面的各種問題,到今天為止終於全部解決。 一、證書格式 前兩天在那什麼雲上面買了個SSL證書,是WoSign的,證書簽發後下載下來有兩個文件,.pem和.key,查詢得知IIS需要.pfx格式的證書文件 ...
  • 以上文章由本文作者根據網路上其它的例子學習整合再加上自己的思想凝聚而成,如有侵犯請聯繫本人,速刪 ...
  • 經常,會有一些人搞不清楚.NET和c#和ASP.NET這三者之間的關係,她們都是什麼呢?他們之間有什麼關係呢?今天我簡單給大家總結一下吧。 首先:什麼是.NET? .NET是微軟公司下的一個開發平臺,.NET核心就是.NET Framwork(.NET框架)是.NET程式開發和運行的環境,在這個平臺 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...