优化网站设计(一):减少请求数

前言

网站设计的优化是一个很大的话题,有一些通用的原则,也有针对不同开发平台的一些建议。这方面的研究一直没有停止过,我在不同的场合也分享过这样的话题。

作为通用的原则,雅虎的工程师团队曾经给出过35个最佳实践。这个列表请参考

Best Practices for Speeding Up Your Web Site  http://developer.yahoo.com/performance/rules.html 

同时,他们还发布了一个相应的测试工具Yslow http://developer.yahoo.com/yslow/

我强烈推荐所有的网站开发人员都应该学习这些最佳实践,并结合自己的实际项目情况进行应用。

接下来的一段时间,我将结合ASP.NET这个开发平台,针对这些原则,通过一个系列文章的形式,做些讲解和演绎,以帮助大家更好地理解这些原则,并且更好地使用他们。

 

准备工作

为了跟随我进行后续的学习,你需要准备如下的开发环境和工具

  1. Google Chrome 或者firefox ,并且安装 Yslow这个扩展组件.请注意,这个组件是雅虎提供的,但目前没有针对IE的版本。
    1. https://chrome.google.com/webstore/detail/yslow/ninejjcohidippngpapiilnmkgllmakh
    2. https://addons.mozilla.org/en-US/firefox/addon/yslow/
    3. 你应该对这些浏览器的开发人员工具有所了解,你可以通过按下F12键调出这个工具。
  2. Visaul Studio 2010 SP1 或更高版本,推荐使用Visual Studio 2012
    1. http://www.microsoft.com/visualstudio/eng/downloads 
  3. 你需要对ASP.NET的开发基本流程和核心技术有相当的了解,本系列文章很难对基础知识做普及。

 

本文要讨论的话题

这一篇文章讨论的是第一个原则:应该尽可能加减少请求数。这个原则的说明请参考  http://developer.yahoo.com/performance/rules.html#num_http

我们的网页在加载的时候,为了提供更加丰富的内容和效果,除了页面本身这个请求之外,总是需要加载其他一些资源的,例如我们常见的Javascript文件,css文件,图片,甚至还会有一些Flash组件等等。这本无可厚非,但如果过多的外部请求,会很直接地降低页面加载的速度。

我们来看一个例子吧。例如我们经常访问的博客园的首页

image

这个网页的加载需要多少次请求呢?(如果不考虑缓存的话)

image

我们看到,请求数为55个。我们进一步通过Yslow来分析,可以得到综合的报表

imageimageimage

应该说博客园的设计已经比较注意很多细节了。他们得到了B级的评分。我们再来看看其他一些主要的门户的表现吧(从左至右,依次是新浪,搜狐,腾讯,淘宝),他们都只得到D级的评分。

【备注】这些评分只是作为一个参考,不做任何的结论和推论。

imageimageimageimage

 

如何减少请求数?

我们可以通过如下的几个方法来减少请求数:

  1. 合并外部资源文件(如javascript,css,图片文件)
    1. 图片的合并,是将多个图片合并为一个图片,然后采用css的一些设置(background-image,background-position) 来使用它们。这个很简单实用(但是效果也是显著的)。本文将不做演示。
    2. javascript和css文件的合并,这个可能大家不太经常注意到。本文的后续部分将对此进行演示。
  2. 使用Inline images 这种方式. 这个方式可能依赖于浏览器的实现,目前并不是所有的浏览器都支持。所以本文也不做演示。

 

合并javascript文件和css文件

对于这两种文件的合并而言,我们当然可以手工去做(copy,paste),把一个文件的内容粘贴在另外一个文件内容的底部即可。但这种方式有几个缺点:

  1. 破坏了原有文件的结构
  2. 不同页面需要的文件组合可能是不一样的,这种情况下会需要产生多个不同的文件,而且需要比较小心地维护它们
  3. 如果文件的内容发生变化,就需要重新再来一次

 

所以,我并不是很推荐用这种手工合并的方法,事实上,我们有更好的工具来实现, 并且在ASP.NET的一些框架(例如ASP.NET MVC)里面已经有了内置的实现。

我们先来看一个例子,下面是一个典型的ASP.NET MVC项目

image

我找了其中的一个用户注册页面,在IE中进行查看

image

我们看到默认情况下,完成这个页面其实要执行8个请求。但经过简单的处理(添加一行代码)之后,我们可以看到如下的效果

image

而且如果你细心看的话,在这个页面中请求的javascript文件似乎看起来经过了特殊的处理(路径比较特殊)。那么,这是如何实现的呢?

原来,在MVC项目中,默认会有一个所谓的BundleConfig的类,它提供了一个方法RegisterBundles,如下所示

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

namespace MvcApplication1
{
    public class BundleConfig
    {
        // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
        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*"));

            // Use the development version of Modernizr to develop with and learn from. Then, when you're
            // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
            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"));

        }
    }
}

 

这个方法会在Global.asax文件中调用

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;

namespace MvcApplication1
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            //BundleTable.EnableOptimizations = false;//启用这一行,则使用Bundle的机制进行文件打包
            AuthConfig.RegisterAuth();
        }
    }
}

 

在具体的页面中,如果需要用到上述的脚本组合,则可以使用下面这样的语法来调用

@Scripts.Render("~/bundles/jqueryval")

 

这就是所有的秘密。

那么,这个技术是不是只能使用在MVC中,在我们另外一种开发框架(ASP.NET Web Forms)中是否也可以使用呢?

答案是:可以的。而且这个技术,确实是最早就是用在ASP.NET Web Forms里面,只不过,因为这方面的文档较少,所以可能大家用的不多而已。下面是我作为演示用的一个简单的ASP.NET Web Forms的项目:

image

我们看到,在页面中,我们添加了三个脚本引用,这样的话,自然在打开页面的时候,需要单独请求这三个脚本文件。

image

我们是否可以将这三个文件合并成一个请求呢?请跟随我来进行如下的操作

首先,添加一个组件,这是微软官方发布的System.Web.Optimization,顾名思义,这就是为了优化网络开发用的

image

按照一般的惯例,我们在项目中添加一个文件,来进行Bundle的注册

using System.Web.Optimization;

namespace WebApplication1
{
    public class BundleConfig
    {
        public static void RegisterBundle(BundleCollection config)
        {
            config.Add(new ScriptBundle("~/default").Include("~/scripts/jquery-2.0.0.min.js", "~/scripts/knockout-2.2.1.js", "~/default.js"));
        }
    }
}

 

然后,我们修改Global.asax文件,添加如下的代码

using System;
using System.Web.Optimization;

namespace WebApplication1
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            BundleConfig.RegisterBundle(BundleTable.Bundles);
            BundleTable.EnableOptimizations = true;
        }

    }
}

最后,我们在页面上也做相应的修改,如下所示(请注意粗体部分)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>
<%@ Import Namespace="System.Web.Optimization" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <%= Scripts.Render("~/default") %>
</head>
<body>
    <form id="form1" runat="server">
        <div>
        </div>
    </form>
</body>
</html>

很不错,我们现在可以查看一下页面运行起来的效果

image

很明显,原先的三个请求,现在变成了一个请求。需要注意的是,如果我们去计算文件大小,这个合并之后的文件,体积会比之前三个文件体积总和略小一些。所以你可以理解为这里存在一定的压缩,但这个压缩比是不大的(尤其是原有的javascript文件本身就经过了压缩的情况下)。关于javascript文件或者css文件的压缩,后续会有专门的文章介绍。

上面的例子,演示的是javascript文件的合并。其实,css文件的合并也是类似的做法,区别在于要使用StyleBundle : http://msdn.microsoft.com/en-us/library/system.web.optimization.stylebundle.aspx

 

总结

本文介绍了网站优化的第一个原则(尽量减少请求数),我带领大家分析了为什么需要考虑这个原则,以及具体如何实现(包括在MVC和Web Forms的做法)

posted @ 2013-04-29 15:37  陈希章  阅读(8629)  评论(16编辑  收藏  举报