最近有一個項目是用SmartAdmin + Jquery + EasyUI 一個ASP.NET MVC5的項目,一直存在一個性能問題,加載速度比較慢,第一次加載須要(在沒有cache的狀況下)須要4-5秒完成所有的加載.javascript
以下圖就是用Chrome PageSpeed 測試的結果 css
有幾個很是重要的指標html
Fist Contentfu Paint:第一次繪出頁面,須要4秒,前4秒都是白屏,確實有點長java
Fist Meaningfull Paint:第一次繪出有意義的內容,須要8.6秒,纔出現可見的操做頁面.jquery
Eliminate render-blocking resources:阻塞加載資源文件,由於的項目在head中加載了jquery和css,由於有些代碼必須先執行致使的web
Remove unused css:存在大量的沒用的css樣式定義,這也很難避免.json
出現上述問題的主要緣由,頁面自己的大小,全部資源加起來超過3.2M,Jquery EasyUI的JS+css 就接近3M,另外頁面裏有嵌入了好幾個PartialView,還有就是執行js的時間,EasyUI DataGrid須要從後臺抓起數據並生成複雜的Dom結構這都須要時間.瀏覽器
第一想到的就是使用cache,單隻能解決第二次訪問的速度問題,對少有點用,我通常會這樣作,設置的方法有緩存
[OutputCache(Duration = 360, VaryByParam = "none")] public ActionResult Index() => this.View();
<system.webServer> <staticContent> <remove fileExtension=".js" /> <mimeMap fileExtension=".js" mimeType="text/javascript" /> <remove fileExtension=".ico" /> <mimeMap fileExtension=".ico" mimeType="image/x-icon" /> <remove fileExtension=".eot" /> <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" /> <remove fileExtension=".woff" /> <mimeMap fileExtension=".woff" mimeType="application/x-font-woff" /> <remove fileExtension=".woff2" /> <mimeMap fileExtension=".woff2" mimeType="application/x-font-woff2" /> <remove fileExtension=".svg" /> <mimeMap fileExtension=".svg" mimeType="image/svg+xml" /> <remove fileExtension=".ttf" /> <mimeMap fileExtension=".ttf" mimeType="application/x-font-ttf" /> <clientCache cacheControlMode="UseMaxAge" httpExpires="365.00:00:00" cacheControlMaxAge="365.00:00:00" /> </staticContent> </system.webServer>
儘可能減小資源文件的大小和請求次數,一般的作法就是使用BundleConfig.cs合併和壓縮js,css文件.我如今使用bundleconfig.json配置代替System.Web.Optimization.配置靈活一點,若是使用bundleconfig.json 編譯壓縮還須要解決客戶端更新的緩存的問題,我使用一下代碼添加一個指紋標誌服務器
public class Fingerprint { public static string Tag(string rootRelativePath) { if (HttpRuntime.Cache[rootRelativePath] == null) { string absolute = HostingEnvironment.MapPath("~" + rootRelativePath); DateTime date = File.GetLastWriteTime(absolute); int index = rootRelativePath.LastIndexOf('/'); string result = rootRelativePath.Insert(index, "/v-" + date.Ticks); HttpRuntime.Cache.Insert(rootRelativePath, result, new CacheDependency(absolute)); } return HttpRuntime.Cache[rootRelativePath] as string; } }
<system.webServer>
<urlCompression doStaticCompression="true" doDynamicCompression="true" dynamicCompressionBeforeCache="false" /> <rewrite> <rules> <rule name="fingerprint"> <match url="([\S]+)(/v-[0-9]+/)([\S]+)" /> <action type="Rewrite" url="{R:1}/{R:3}" /> </rule> </rules> </rewrite> </system.webServer>
<link rel="stylesheet" href="@Fingerprint.Tag("/content/site.css")" />
ETags 是用於 Web 緩存驗證的工具,容許有條件的客戶端請求。經過 ETags,瀏覽器能夠判斷某項資源是否被須要。若是不須要,瀏覽器就不會向 Web 服務器發送請求,從而最小化請求數量。配置方法
public class ETagHttpModule : IHttpModule { #region IHttpModule Members void IHttpModule.Dispose() { // Nothing to dispose; } void IHttpModule.Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); WebPageHttpHandler.DisableWebPagesResponseHeader = true; } #endregion void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; //if (app.Request.CurrentExecutionFilePath.EndsWith("/") || app.Request.CurrentExecutionFilePath.EndsWith(".cshtml")) //{ app.Response.Filter = new ETagStream(app.Response, app.Request); //} } #region Stream filter public class ETagStream : MemoryStream { private HttpResponse _response = null; private HttpRequest _request; private Stream _filter = null; public ETagStream(HttpResponse response, HttpRequest request) { _response = response; _request = request; _filter = response.Filter; } private string GetToken(Stream stream) { var checksum = new byte[0]; checksum = MD5.Create().ComputeHash(stream); return Convert.ToBase64String(checksum, 0, checksum.Length); } public override void Write(byte[] buffer, int offset, int count) { var data = new byte[count]; Buffer.BlockCopy(buffer, offset, data, 0, count); var token = GetToken(new MemoryStream(data)); var clientToken = _request.Headers["If-None-Match"]; if (token != clientToken) { _response.AddHeader("ETag", token); _filter.Write(data, 0, count); } else { _response.SuppressContent = true; _response.StatusCode = 304; _response.StatusDescription = "Not Modified"; _response.AddHeader("Content-Length", "0"); } } } #endregion }
<modules> <remove name="FormsAuthentication" /> <!--<add type="WhitespaceModule" name="WhitespaceModule" />--> <add type="WebApp.ETagHttpModule" name="ETagHttpModule" /> </modules>
public class ETagAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) => filterContext.HttpContext.Response.Filter = new ETagFilter(filterContext.HttpContext.Response, filterContext.RequestContext.HttpContext.Request); } public class ETagFilter : MemoryStream { private HttpResponseBase _response = null; private HttpRequestBase _request; private Stream _filter = null; public ETagFilter(HttpResponseBase response, HttpRequestBase request) { _response = response; _request = request; _filter = response.Filter; } private string GetToken(Stream stream) { var checksum = new byte[0]; checksum = MD5.Create().ComputeHash(stream); return Convert.ToBase64String(checksum, 0, checksum.Length); } public override void Write(byte[] buffer, int offset, int count) { var data = new byte[count]; Buffer.BlockCopy(buffer, offset, data, 0, count); var token = GetToken(new MemoryStream(data)); var clientToken = _request.Headers["If-None-Match"]; if (token != clientToken) { _response.AddHeader("ETag", token); _filter.Write(data, 0, count); } else { _response.SuppressContent = true; _response.StatusCode = 304; _response.StatusDescription = "Not Modified"; _response.AddHeader("Content-Length", "0"); } } }
//[OutputCache(Duration = 360, VaryByParam = "none")] [ETag] public ActionResult Index() => this.View();
效果圖,回發的字節數確實減小了不少,單響應時間差很少,不是很明顯.
優化方案有不少,可是感受效果都不是很理想,要作到極致的用戶體驗,可能真的要拋棄Jquery,EasyUI,這類肥大又複雜的類庫.
另外你們有沒有很是好用又簡單的方法解決初始加載白屏的問題,我試過用js preloading圖層動畫,可是效果仍是不理想.但看過一些網址和APP作的效果很是好,不知道具體是如何實現的,在Asp.net mvc環境下能不能用
參考文章
(ASP.NET MVC 應用提速的十種方法)http://blog.oneapm.com/apm-tech/679.html