2

我一直在研究这个。我发现了几个关于这个主题的资源,它们都倾向于使用相同的方法 - 覆盖 Page.Render,使用 HtmlTextWriter 将输出转换为字符串,然后使用一系列编译的正则表达式来删除额外的空格。这是一个例子

嗯,我试过了,它的工作原理......但是......

在 Safari 5.0 中,这似乎会导致加载图像时出现各种性能问题并出现“服务器太忙”错误。IE 7、FireFox 3.6 和 Google Chrome 6.0 似乎工作正常,但我没有对服务器进行太多压力测试。有时页面生成的时间似乎有滞后,但是一旦将html发送到浏览器,页面就会快速显示。

无论如何,当您考虑它时,让 .NET 构建所有这些选项卡和换行符只是为了再次使用字符串解析将它们全部删除似乎相当愚蠢 - 删除它们的效率最低的方法。覆盖 HtmlTextWriter 并将其传递到主页请求上的树中会更有意义,以避免将它们放入输出中 - 从逻辑上讲,在这种情况下应该有性能提升而不是命中。

即使我只能使用这种方法删除 50% 的空白,它仍然会为正则表达式留下更少的工作——这意味着它应该比单独使用正则表达式时表现得更好。

我尝试使用控制适配器并覆盖几个成员

  1. 将对 WriteLine() 的所有调用定向到相应的 Write() 方法
  2. 将 NewLine 属性设置为空字符串
  3. 覆盖 OutputTabs() 方法并简单地删除代码
  4. 覆盖 Indent 属性并返回 0

我还尝试覆盖 RenderChildren、Render、BeginRender 和 EndRender 以传入我的自定义 HtmlTextWriter,但我似乎无法让一个简单的标签控件删除其标签之前的选项卡。我还使用 Reflector 对框架进行了深入研究,但我根本无法弄清楚这些字符是如何生成的——我以为我使用的是“包罗万象”的方法,但显然我遗漏了一些东西。

无论如何,这就是我想出的。此代码未按我希望的方式运行。当然,我也尝试过直接覆盖页面上的各种 Render 方法并传入我的自定义 HtmlTextWriter 的实例,但这也不起作用。

Public Class PageCompressorControlAdapter
    Inherits System.Web.UI.Adapters.ControlAdapter

    Protected Overrides Sub RenderChildren(ByVal writer As System.Web.UI.HtmlTextWriter)
        MyBase.RenderChildren(New CompressedHtmlTextWriter(writer))
    End Sub

    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
        MyBase.Render(New CompressedHtmlTextWriter(writer))
    End Sub

    Protected Overrides Sub BeginRender(ByVal writer As System.Web.UI.HtmlTextWriter)
        MyBase.BeginRender(New CompressedHtmlTextWriter(writer))
    End Sub

    Protected Overrides Sub EndRender(ByVal writer As System.Web.UI.HtmlTextWriter)
        MyBase.EndRender(New CompressedHtmlTextWriter(writer))
    End Sub

End Class

Public Class CompressedHtmlTextWriter
    Inherits HtmlTextWriter

    Sub New(ByVal writer As HtmlTextWriter)
        MyBase.New(writer, "")
        Me.InnerWriter = writer.InnerWriter

        Me.NewLine = ""
    End Sub

    Sub New(ByVal writer As System.IO.TextWriter)
        MyBase.New(writer, "")
        MyBase.InnerWriter = writer

        Me.NewLine = ""
    End Sub

    Protected Overrides Sub OutputTabs()
        'Skip over the tabs
    End Sub

    Public Overrides Property NewLine() As String
        Get
            Return ""
        End Get
        Set(ByVal value As String)
            MyBase.NewLine = value
        End Set
    End Property


    Public Overrides Sub WriteLine()

    End Sub

    Public Overrides Sub WriteLine(ByVal value As Boolean)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Char)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal buffer() As Char)
        MyBase.Write(buffer)
    End Sub

    Public Overrides Sub WriteLine(ByVal buffer() As Char, ByVal index As Integer, ByVal count As Integer)
        MyBase.Write(buffer, index, count)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Decimal)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Double)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Integer)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Long)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Object)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As Single)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal s As String)
        MyBase.Write(s)
    End Sub

    Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object)
        MyBase.Write(format, arg0)
    End Sub

    Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object, ByVal arg1 As Object)
        MyBase.Write(format, arg0, arg1)
    End Sub

    Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object, ByVal arg1 As Object, ByVal arg2 As Object)
        MyBase.Write(format, arg0, arg1, arg2)
    End Sub

    Public Overrides Sub WriteLine(ByVal format As String, ByVal ParamArray arg() As Object)
        MyBase.Write(format, arg)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As UInteger)
        MyBase.Write(value)
    End Sub

    Public Overrides Sub WriteLine(ByVal value As ULong)
        MyBase.Write(value)
    End Sub

End Class

如果您不熟悉控制适配器,只需将下面的 xml 放在 ASP.NET App_Browsers 文件夹中的 .browser 文件中。您可以更改 controlType 以将控制适配器应用于标签或其他东西,以实现较小范围的测试。如果我能做到这一点,如果有必要在我的项目中添加所有控件并不是什么大不了的事。

<browsers>

    <browser refID="Default">
        <controlAdapters>
            <adapter controlType="System.Web.UI.Page"
                     adapterType="PageCompressorControlAdapter"/>
        </controlAdapters>
    </browser>

</browsers>

无论如何,您会认为只有一个简单的配置设置,例如 VerboseHtml="false" 或 PreserveHtmlFormatting="false" 或类似的东西。如果您查看 MSN.com 的输出,他们正在使用与此类似的某种压缩……而且它似乎非常高效。

4

1 回答 1

1

解决方案涉及使用我在这里找到的压缩模块的修改版本:

http://omari-o.blogspot.com/2009/09/aspnet-white-space-cleaning-with-no.html

此解决方案实现了一个自定义 PageParserFilter,然后在 web.config 文件中对其进行配置。这个解决方案的美妙之处在于它在编译时过滤了空白,因此在运行时不会影响性能。

然后感谢 Aristos 和 diamandiev,我寻找一种合理的方法来为支持它的浏览器的响应流添加压缩。我在这里找到了另一个可用于此的开源模块:http: //blowery.org/httpcompress/

结合使用这两种方法可以显着提高性能 - 远远超过使用其中一种方法。从页面中删除选项卡后,浏览器的渲染速度似乎要快得多。

至于静态内容,我也打算用本文中的方法来压缩那些文件(当然图片除外,它们已经被压缩了):http ://www.lostechies.com/blogs/dahlbyk/archive/2010 /01/08/script-to-enable-http-compression-gzip-deflate-in-iis-6.aspx

最终解决方案

我已将我的最终解决方案添加到此 Gist

于 2010-10-11T18:37:01.240 回答