以下内容大部分都不是原创,因为各位写手都是抄来抄去,所以我也不知道原创是谁,写这些只是为了方便自己的学习。
首先,Nop.Web也是一个MVC Web应用程序项目,一个公有区域的展示层。它就是你实际能够运行的应用程序。它是应用程序的启动项目。
一、概况
nopcommerce其布局页文件分布在Nop.Web/Views/shared当中,主要涉及到五个布局文件:_Root.Head.cshtml、_Root.cshtml、_ColumnsOne.cshtml、_ColumnsTwo.cshtml、_ColumnsThree.cshtml。_ColumnsOne(Two/Three).cshtml三个布局页继承自_Root.cshtml、_Root.cshtml继承自_Root.Head.cshtml。
所有继承_Root.Head.cshtml的页面将会使用相同的<head>标签内容,<body>体由它的子布局页来进一步细化。
_Root.cshtml此页面替换掉_Root.Head.cshtml中@RenderBody(),大致结构如下图:
nopcommerc有三个不同的具体布局页:_ColumnsOne(Two/Three).cshtml,三者形式如下:
1._ColumnsOne.cshtml
<body>结构与_Root.cshtml一致。
2._ColumnsTwo.cshtml
<body>有两种:
3._ColumnsThree.cshml Layout
而到了这里,其结构就有三种:
也就是说_Root.Head主要管<head>中内容设置,以及全局CSS、JS文件引入;_Root.cshtml将网页主体内容<body>设计完成;_ColumnsOne(Two/Three).cshtml对_Root.cshtml变形处理。
二 、细读
1._Root.Head.cshtml
顶层_Root.Head.cshtml内容如下:
1 @using Nop.Core 2 @using Nop.Core.Domain.Common; 3 @using Nop.Core.Infrastructure; 4 @{ 5 var displayMiniProfiler = CommonHelper.GetTrustLevel() >= AspNetHostingPermissionLevel.High && 6 EngineContext.Current.Resolve<Nop.Core.Domain.StoreInformationSettings>().DisplayMiniProfilerInPublicStore; 7 8 //resources 9 Html.AppendCssFileParts("~/Content/jquery-ui-themes/smoothness/jquery-ui-1.10.3.custom.min.css"); 10 11 Html.AppendScriptParts("~/Scripts/public.ajaxcart.js"); 12 Html.AppendScriptParts("~/Scripts/public.common.js"); 13 Html.AppendScriptParts("~/Scripts/jquery-migrate-1.2.1.min.js"); 14 Html.AppendScriptParts("~/Scripts/jquery-ui-1.10.3.custom.min.js"); 15 Html.AppendScriptParts("~/Scripts/jquery.validate.unobtrusive.min.js"); 16 Html.AppendScriptParts("~/Scripts/jquery.validate.min.js"); 17 Html.AppendScriptParts("~/Scripts/jquery-1.10.2.min.js"); 18 19 //X-UA-Compatible tag 20 var commonSettings = EngineContext.Current.Resolve<CommonSettings>(); 21 if (commonSettings.RenderXuaCompatible) 22 { 23 Html.AppendHeadCustomParts(string.Format("<meta http-equiv="X-UA-Compatible" content="{0}"/>", commonSettings.XuaCompatibleValue)); 24 } 25 } 26 <!DOCTYPE html> 27 <html @Html.Partial("LanguageAttributes")> 28 <head> 29 <title>@Html.NopTitle(true)</title> 30 <meta http-equiv="Content-type" content="text/html;charset=UTF-8" /> 31 <meta name="description" content="@(Html.NopMetaDescription())" /> 32 <meta name="keywords" content="@(Html.NopMetaKeywords())" /> 33 <meta name="generator" content="nopCommerce" /> 34 @Html.NopHeadCustom() 35 @*This is used so that themes can inject content into the header*@ 36 @Html.Partial("Head") 37 @Html.Widget("head_html_tag") 38 @Html.NopCssFiles(this.Url, ResourceLocation.Head) 39 @Html.NopScripts(this.Url, ResourceLocation.Head) 40 @Html.NopCanonicalUrls() 41 @Html.Action("RssHeaderLink", "News") 42 @Html.Action("RssHeaderLink", "Blog") 43 @*Favicon - upload favicon.ico file to the root directory*@ 44 @Html.Action("Favicon", "Common") 45 @if (displayMiniProfiler) 46 { 47 @StackExchange.Profiling.MiniProfiler.RenderIncludes() 48 } 49 <!--Powered by nopCommerce - http://www.nopCommerce.com--> 50 <!--Copyright (c) 2008-2013--> 51 </head> 52 <body> 53 @RenderBody() 54 @Html.NopCssFiles(this.Url, ResourceLocation.Foot) 55 @Html.NopScripts(this.Url, ResourceLocation.Foot) 56 </body> 57 </html>
1.1. Html.AppendCssFileParts() 与AppendScriptParts()
两个方法都是nop为HtmlHelper类定义拓展方法。见名知意,AppendScriptParts附加脚本文件,AppendCssFileParts附加CSS文件:
// private readonly Dictionary<ResourceLocation, List<string>> _cssParts; public virtual void AppendCssFileParts(ResourceLocation location, string part) { if (!_cssParts.ContainsKey(location)) _cssParts.Add(location, new List<string>()); if (string.IsNullOrEmpty(part)) return; _cssParts[location].Insert(0, part); }
_cssParts为字典类型,根据传入的location确定键值,而字符串参数 part是CSS文件的路径。此方法最终就是将传入的CSS文件路径附加到_cssParts Dictionary当中。
与此对应还有一个AddCssFileParts。
public virtual void AddCssFileParts(ResourceLocation location, string part) { if (!_cssParts.ContainsKey(location)) _cssParts.Add(location, new List<string>()); if (string.IsNullOrEmpty(part)) return; _cssParts[location].Add(part); }
注意到两者的差别仅仅是给Dictionary<ResourceLocation, List<string>>添加值顺序的不同,Append在Dictionary索引为0处添加,Add在队列末尾添加。因此产生的效果是:AppendCssFileParts()调用越靠后,在界面上显示反而越靠前。大家在_Root.Head.cshtml代码中可以看到 jquery-1.7.1.min.js的引用是在最后,但是通常我们是应该将其引用位置尽量考前。
AppendScriptParts()与AppendCssFileParts()非常相似,这里就不再贴代码说明。
1.2 @Html.Partial("LanguageAttributes")
就是字符串:
@if (this.ShouldUseRtlTheme()) { <text>dir="rtl"</text> //<text>dir="rtl" xml:lang="he" lang="he"</text> }
ShouldUseRtlTheme()方法从当前用户的配置信息中读取其阅读方式是左到右,还是右到左,其实现依托<html>标签 的dir属性。
1.3 @(Html.NopMetaDescription()
NopMetaDescription方法中调用下面关键方法:
public virtual string GenerateMetaDescription() { var metaDescription = string.Join(", ", _metaDescriptionParts.AsEnumerable().Reverse().ToArray()); var result = !String.IsNullOrEmpty(metaDescription) ? metaDescription : _seoSettings.DefaultMetaDescription; return result; }
DefaultMetaDescription是属性,从数据库中查取。
1.4 @Html.Action("RssHeaderLink", "News")、@Html.Action("RssHeaderLink", "Blog")
返回形如这样的字符串:<link href="xx" rel="alternate" ……>,用于RSS。
1.5 @Html.Action("Favicon", "Common")
返回的字符串形式这样: <link rel="shortcut icon" href="XX" />,href属性默认寻找Nop.Web根目录下名字为favicon.ico文件。
2._Root.cshtml
_Root.cshtml内容如下:
1 @{ 2 Layout = "~/Views/Shared/_Root.Head.cshtml"; 3 } 4 @Html.Widget("body_start_html_tag_after") 5 @Html.Partial("_Notifications") 6 @Html.Action("AdminHeaderLinks", "Common") 7 <div class="master-wrapper-page"> 8 @Html.Action("JavaScriptDisabledWarning", "Common") 9 <div class="master-wrapper-content"> 10 <script type="text/javascript"> 11 AjaxCart.init(false, '.header-links .cart-qty', '.header-links .wishlist-qty', '#flyout-cart'); 12 </script> 13 @Html.Partial("Header") 14 <div class="header-menu"> 15 @Html.Action("TopMenu", "Catalog") 16 </div> 17 @Html.Widget("content_before") 18 @*ajax loading window*@ 19 <div class="ajax-loading-block-window" style="display: none"> 20 <div class="loading-image"> 21 </div> 22 </div> 23 <div class="master-wrapper-main"> 24 @RenderBody() 25 </div> 26 @Html.Widget("content_after") 27 </div> 28 @Html.Action("Footer", "Common") 29 </div> 30 @Html.Action("EuCookieLaw", "Common") 31 @Html.Widget("body_end_html_tag_before")
2.1 @Html.Action("JavaScriptDisabledWarning", "Common")
返回一个PartialView:
@model dynamic <noscript> <div class="noscript"> <p> <strong>JavaScript seems to be disabled in your browser.</strong></p> <p> You must have JavaScript enabled in your browser to utilize the functionality of this website.</p> </div> </noscript>
其目的就是检测是否禁用JS,如果禁用就提示。
2.2 @Html.Partial("_Notifications")
弹出提示:
1 @{ 2 //success messages 3 var successMessages = new List<string>(); 4 if (TempData[string.Format("nop.notifications.{0}", NotifyType.Success)] != null) 5 { 6 successMessages.AddRange(TempData[string.Format("nop.notifications.{0}", NotifyType.Success)] as IList<string>); 7 } 8 if (ViewData[string.Format("nop.notifications.{0}", NotifyType.Success)] != null) 9 { 10 successMessages.AddRange(ViewData[string.Format("nop.notifications.{0}", NotifyType.Success)] as IList<string>); 11 } 12 13 14 //error messages 15 var errorMessages = new List<string>(); 16 if (TempData[string.Format("nop.notifications.{0}", NotifyType.Error)] != null) 17 { 18 errorMessages.AddRange(TempData[string.Format("nop.notifications.{0}", NotifyType.Error)] as IList<string>); 19 } 20 if (ViewData[string.Format("nop.notifications.{0}", NotifyType.Error)] != null) 21 { 22 errorMessages.AddRange(ViewData[string.Format("nop.notifications.{0}", NotifyType.Error)] as IList<string>); 23 } 24 } 25 @foreach (var message in successMessages) 26 { 27 <script type="text/javascript"> 28 $(document).ready(function () { 29 displayPopupNotification('@Html.Raw(HttpUtility.JavaScriptStringEncode(message))', 'success', false); 30 }); 31 </script> 32 } 33 @foreach (var message in errorMessages) 34 { 35 <script type="text/javascript"> 36 $(document).ready(function () { 37 displayPopupNotification('@Html.Raw(HttpUtility.JavaScriptStringEncode(message))', 'error', false); 38 }); 39 </script> 40 } 41 <div id="dialog-notifications-success" title="@T("Common.Notification")" style="display:none;"> 42 </div> 43 <div id="dialog-notifications-error" title="@T("Common.Error")" style="display:none;"> 44 </div> 45 <div id="bar-notification" class="bar-notification"> 46 <span class="close" title="@T("Common.Close")"> </span> 47 </div> 48 @Html.Widget("notifications")
通知内容通过TempData[nop.notifications.sucess]获取,注意到使用的是TempData,所以nop的通知是跨action的。
2.3 @Html.Partial("Header")
对应第一部分图中header,包含了头部链接、搜索框等内容。
2.4 @Html.Action("Menu", "Common")
菜单,对应第一部分图中的Menu,这个好理解。
主人寄语:很详细的一篇文章,希望你读通。