在asp.net mvc中, razor引擎的作用是渲染出靜態頁面。(我這里指的靜態頁面,是指對瀏覽器而言,第一次從服務器下載以后頁面。)
而ajax的作用是瀏覽器端的頁面通過js向服務端再次發出請求從而從服務端獲取數據展示在頁面。
我們一般的編程模式是ajax只輸出json風格的數據,再由客戶端通過某種方式動態展示到頁面上,比如通過jquery的模板插件,或第三方的控件。
這種方式無疑是高效的。對服務器壓力也較小。
但是它的缺點就是我們無法利用現有的強大的razor引擎,用它豐富的Html幫助類,精確地控制數據展示的方式,維護各種鏈接。因為它是工作在服務器端的,在初始頁面第一次從服務器端下載完以后,它的使命就完成了。
那么有沒有辦法讓ajax和razor這兩種截然不同的技術有機地結合在一起呢?
最近本人在做一個項目時發現,只需要簡單的寫一個jquery的擴展,就可以實現上述目的。
先看下效果:
1. 當我們在vs2013下,新建一個asp.net mvc應用程序, 會出現一個初始站點,我們就在這個初始站點上做文章,

2. 把其中的“關於”和“聯系方式”兩個頁面,不導航到新頁面,直接在主頁原有的大屏幕介紹底下(如圖所示局部加載區)打開:

為實現這個效果,原有的服務器端的Controller中的邏輯不需要做任何的改變。也不需要對視圖做任何的改變。
改變的只是一個模板頁_Layout.cshtml。
原有的_Layout.cshtml的源代碼變成這樣:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - 我的 ASP.NET 應用程序</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("應用程序名稱", "Index", "Home", null, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("主頁", "Index", "Home")</li>
<li>@Html.ActionLink("關於", "About", "Home")</li>
<li>@Html.ActionLink("聯系方式", "Contact", "Home")</li>
</ul>
@Html.Partial("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
<div id="partial"> @RenderBody() @RenderSection("innerScripts", required: false) </div> <hr />
<footer>
<p>© @DateTime.Now.Year - 我的 ASP.NET 應用程序</p>
</footer>
</div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") <script type="text/javascript" src="~/Scripts/loadpartial.js"></script> <script type="text/javascript"> $.loadPartial({ link: '.nav a', container: '#partial' }); </script>
}</body>
</html>
其中紅色加粗是加的代碼。 注意到有一個loadpartial.js文件。這就是對jquery作的一個小擴展。
調用方法是: $.loadPartial({ link: '.nav a', container: '#partial' });
link代表一批鏈接的選擇器,container代表頁面加載到的容器。
注意: 所有加載的頁面到容器內時,不會加載和主頁重復的內容,如導航,版權聲明(如果那樣就太失敗了).只會加載自己的#partial中的內容。
如果加載的頁面在加載時有js要執行,可以寫在innerScripts這個區域中。
另外,如果加載進的頁面本身也有超鏈接,它依然會限制在這個框框內,more, 如果頁面有表單,表單的提交也會限制在這個框框內;
更有,如果你的后台程序出了BUG,比如404或500錯誤,它也會忠實地把錯誤信息像普通頁面一樣顯示在框框中。
看起來的感覺就像是一個內框架,但並不會破壞原有頁面的dom結構。
是不是很神奇?
而這個神奇只是jquery的功勞,我只是對它寫了幾十行的小擴展:
好了,上核心loadpartial.js文件的代碼:
/* Parital Load (C) Copy right bwangel 2014 */ jQuery.extend({ /* 局部加載頁 options{ link : 超鏈接的選擇器 container: 要將頁面加載到的容的選擇器 updateNow: 是否立即將容器中的現有鏈接進行處理 } */ loadPartial: function (options) { var defaults = { link: '.nav a', container: '#razorContainer', updateNow: true }; defaults = $.extend(defaults, options); razorContainer = $(defaults.container); $(defaults.link).each(alink); if (defaults.updateNow) alinkInContainer(); //var razorUrl = ""; function alink(idx, link) { if ($(link).attr("onclick")) return; if (link.href.indexOf('###') >= 0) return; var addr1 = link.href.split('#'); var addr2 = location.href.split('#'); if (addr1[0] == addr2[0]) return; //說明是錨點跳轉 $(link).click(function (e) { e.preventDefault(); if ($(link).attr("action")) { ConfirmDelete(link); } else { getRazor(link.href, null, 'get'); } }); } function alinkInContainer() { razorContainer.find('a').each(alink); razorContainer.find('form').submit(function (e) { e.preventDefault(); getRazor(this.action, $(this).serialize(), "post"); }); } function getRazor(url, d, m) { $.ajax({ url: url, data: d, cache: false, type: m, success: function (data) { var r = $("<div>").append($.parseHTML(data, true)).find('#partial'); razorContainer.html(r); alinkInContainer(); }, error: function (response, status, xhr) { razorContainer.html(response.responseText); } }); } } });
關於此文的完整代碼,請在https://git.oschina.net/bwangel/LoadPartDemo下載
