Web应用性能优化黄金法则——转

本文提供了一系列前端性能优化的实用法则,旨在减少数据传输量和频率,包括减少HTTP请求、使用CDN、压缩文件等,有助于提升用户体验和网络带宽利用效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大概浏览了一下,是好文章,有时间要详细看。

 

Web 应用性能优化黄金法则:先优化前端程序 (front-end) 的性能,因为这是 80% 或以上的最终用户响应时间的花费所在。

法则 1. 减少 HTTP 请求次数
80%
的最终用户响应时间花在前端程序上,而其大部分时间则花在各种页面元素,如图像、样式表、脚本和 Flash 等,的下载上。减少页面元素将会减少 HTTP 请求次数。这是快速显示页面的关键所在。
一种减少页面元素个数的方法是简化页面设计。但是否存在其他方式,能做到既有丰富内容,又能获得快速响应时间呢?以下是这样一些技术:
Image maps
组合多个图片到一张图片中。总文件大小变化不大,但减少了 HTTP 请求次数从而加快了页面显示速度。该方式只适合图片连续的情况;同时坐标的定义是烦人又容易出错的工作。
CSS Sprites
是更好的方法。它可以组合页面中的图片到单个文件中,并使用 CSS background-image background-position 属性来现实所需的部分图片。
Inline images
使用 data: URL scheme 来在页面中内嵌图片。这将增大 HTML 文件的大小。组合 inline images 到你的(缓存)样式表是既能较少 HTTP 请求,又能避免加大 HTML 文件大小的方法。
Combined files
通过组合多个脚本文件到单一文件来减少 HTTP 请求次数。样式表也可采用类似方法处理。这个方法虽然简单,但没有得到大规模的使用。 10 大美国网站每页平均有 7 个脚本文件和 2 个样式表。当页面之间脚本和样式表变化很大时,该方式将遇到很大的挑战,但如果做到的话,将能加快响应时间。

减少 HTTP 请求次数是性能优化的起点。这最提高首次访问的效率起到很重要的作用。据 Tenni Theurer 的文章 Browser Cache Usage - Exposed! 描述, 40-60% 的日常访问是首次访问,因此为首次访问者加快页面访问速度是用户体验的关键。
我们的应用 :
外贸 : 将首页的几十个小图标合并为一个,通过 CSS 控制它们的显示,减少了 HTTP 请求数。

法则 2. 使用 CDN(Content Delivery Network, 内容分发网络 )
用户离 web server 的远近对响应时间也有很大影响。从用户角度看,把内容部署到多个地理位置分散的服务器上将有效提高页面装载速度。但是该从哪里开始呢?
作为实现内容地理分布的第一步,不要试图重构 web 应用以适应分布架构。改变架构将导致多个周期性任务,如同步 session 状态,在多个 server 之间复制数据库交易。这样缩短用户与内容距离的尝试可能被应用架构改版所延迟,或阻止。
我们还记得 80-90% 的最终用户响应时间花在下载页面中的各种元素上,如图像文件、样式表、脚本和 Flash 等。与其花在重构系统这个困难的任务上,还不如先分布静态内容。这不仅能大大减少响应时间,而且由于 CDN 的存在,分布静态内容非常容易实现。
CDN
是地理上分布的 web server 的集合,用于更高效地发布内容。通常基于网络远近来选择给具体用户服务的 web server
一些大型网站拥有自己的 CDN ,但是使用如 Akamai Technologies, Mirror Image Internet, Limelight Networks CDN 服务提供商的服务将是划算的。在 Yahoo! 把静态内容分布到 CDN 减少了用户影响时间 20% 或更多。切换到 CDN 的代码修改工作是很容易的,但能达到提高网站的速度。
我们的应用:
目前还没用到,不过据客户反映,广东山东等地网络情况比较差,如果可以将占据主要带宽的静态资源通过 CDN 发布,相信可以大大缓解目前网站访问速度的问题。

法则 3. 增加 Expires Header
网页内容正变得越来越丰富,这意味着更多的脚本文件、样式表、图像文件和 Flash 。首次访问者将不得不面临多次 HTTP 请求,但通过使用 Expires header ,您可以在客户端缓存这些元素。这在后续访问中避免了不必要的 HTTP 请求。 Expires header 最常用于图像文件,但是它也应该用于脚本文件、样式表和 Flash
浏览器(和代理)使用缓存来减少 HTTP 请求的次数和大小,使得网页加速装载。 Web server 通过 Expires header 告诉客户端一个元素可以缓存的时间长度。
如果服务器是 Apache 的话,您可以使用 ExpiresDefault 基于当期日期来设置过期日期,如:
ExpiresDefault “access plus 10 years”
设置过期时间为从请求时间开始计算的 10 年。
请记住,如果使用超长的过期时间,则当内容改变时,您必须修改文件名称。在 Yahoo! 我们经常把改名作为 release 的一个步骤:版本号内嵌在文件名中,如 yahoo_2.0.6.js
我们的应用:
外贸:在 Apache 配置了 JS,CSS,image 的缓存,如果静态资源需要更新,则采用修改文件版本号的方案确保客户端取得最新版本;
E
网打尽: E 网打尽的探头规则( JS )是根据客户的设置生成的,但是在相当长的一段时间内基本上不会有变化,因此,在生成规则的同时附加一个 expires 响应头,尽量减少客户端的请求和探头规则生成的次数。

法则 4. 压缩页面元素
通过压缩 HTTP 响应内容可减少页面响应时间。从 HTTP/1.1 开始, web 客户端在 HTTP 请求中通过 Accept-Encoding 头来表明支持的压缩类型,如:
Accept-Encoding: gzip, deflate.
如果 Web server 检查到 Accept-Encoding 头,它会使用客户端支持的方法来压缩 HTTP 响应,会设置 Content-Encoding 头,如: Content-Encoding: gzip
Gzip
是目前最流行及有效的压缩方法。其他的方式如 deflate ,但它效果较差,也不够流行。通过 Gzip ,内容一般可减少 70% 。如果是 Apache ,在 1.3 版本下需使用 mod_gzip 模块,而在 2.x 版本下,则需使用 mod_deflate
Web server
根据文件类型来决定是否压缩。大部分网站对 HTML 文件进行压缩。但对脚本文件和样式表进行压缩也是值得的。实际上,对包括 XML JSON 在内的任务文本信息进行压缩都是值得的。图像文件和 PDF 文件不应该被压缩,因为它们本来就是压缩格式保存的。对它们进行压缩,不但浪费 CPU ,而且还可能增加文件的大小。
因此,对尽量多的文件类型进行压缩是一种减少页面大小和提高用户体验的简便方法。
我们的应用:
外贸、 E 网打尽、 K 计划: 600 K ext2 包,是人都会想到要去压缩它,压缩后的效果还不错,只有 150 K 。另外, JS CSS HTML 也尽量压缩,要知道我们的很多客户还在使用 1M ADSL

法则 5. 把样式表放在头上
我们发现把样式表移到 HEAD 部分可以提高界面加载速度,因此这使得页面元素可以顺序显示。
在很多浏览器下,如 IE ,把样式表放在 document 的底部的问题在于它禁止了网页内容的顺序显示。浏览器阻止显示以免重画页面元素,那用户只能看到空白页了。 Firefox 不会阻止显示,但这意味着当样式表下载后,有些页面元素可能需要重画,这导致闪烁问题。
HTML
规范明确要求样式表被定义在 HEAD 中,因此,为避免空白屏幕或闪烁问题,最好的办法是遵循 HTML 规范,把样式表放在 HEAD 中。
我们的应用:
目前还没有碰到把样式表放在文档后面的情况吧?

法则 6. 把脚本文件放在底部
与样式文件一样,我们需要注意脚本文件的位置。我们需尽量把它们放在页面的底部,这样一方面能顺序显示,另方面可达到最大的并行下载。
浏览器会阻塞显示直到样式表下载完毕,因此我们需要把样式表放在 HEAD 部分。而对于脚本来说,脚本后面内容的顺序显示将被阻塞,因此把脚本尽量放在底部意味着更多内容能被快速显示。
脚本引起的第二个问题是它阻塞并行下载数量。 HTTP/1.1 规范建议浏览器每个主机的并行下载数不超过 2 个。因此如果您把图像文件分布到多台机器的话,您可以达到超过 2 个的并行下载。但是当脚本文件下载时,浏览器不会启动其他的并行下载,甚至其他主机的下载也不启动。
在某些情况下,不是很容易就能把脚本移到底部的。如,脚本使用 document.write 方法来插入页面内容。同时可能还存在域的问题。不过在很多情况下,还是有一些方法的。
一个备选方法是使用延迟脚本( deferred script )。 DEFER 属性表明脚本未包含 document.write ,指示浏览器刻继续显示。不幸的是, Firefox 不支持 DEFER 属性。在 IE 中,脚本可能被延迟执行,但不一定得到需要的长时间延迟。不过从另外角度来说,如果脚本能被延迟执行,那它就可以被放在底部了。
我们的应用:
这点之前大家可能都没有意识到,不过在我们的 XCube XUI 中我们已经实施了这条法则,相信可以进一步提升页面的访问性能。

法则 7. 避免 CSS 表达式
CSS
表达式是功能强大的 ( 同时也是危险的 ) 用于动态设置 CSS 属性的方式。 IE ,从版本 5 开始支持 CSS 表达式,如 backgourd-color: expression((new Date()).getHours()%2?”#B8D4FF”:”#F08A00”) ,即背景色每个小时切换一次。
CSS
表达式的问题是其执行次数超过大部分人的期望。不仅页面显示和 resize 时计算表达式,而且当页面滚屏,甚至当鼠标在页面上移动时都会重新计算表达式。
一种减少 CSS 表达式执行次数的方法是一次性表达式,即当第一次执行时就以明确的数值代替表达式。如果必须动态设置的话,可使用事件处理函数代替。如果您必须使用 CSS 表达式的话,请记住它们可能被执行上千次,从而影响页面性能。
我们的应用:
目前 CSS 的维护工作主要由 UI 人员负责,他们已经尽量在避免这种情况了。

法则 8. JavaScript CSS 放到外部文件中
上述很多性能优化法则都基于外部文件进行优化。现在,我们必须问一个问题: JavaScript CSS 应该包括在外部文件,还是在页面文件中?
在现实世界中,使用外部文件会加快页面显示速度,因为外部文件会被浏览器缓存。如果内置 JavaScript CSS 在页面中虽然会减少 HTTP 请求次数,但增大了页面的大小。另外一方面,使用外部文件,会被浏览器缓存,则页面大小会减小,同时又不增加 HTTP 请求次数。
因此,一般来说,外部文件是更可行的方式。唯一的例外是内嵌方式对主页更有效,如 Yahoo! My Yahoo! 都使用内嵌方式。一般来说,在一个 session 中,主页访问此时较少,因此内嵌方式可以取得更快的用户响应时间。
我们的应用:
外贸、 E 网打尽、 K 计划: ext2 的代码作了很好的引导,目前前端开发人员都非常注意客户端模块的封装、重用,尽量以外部 JS 的方式提高代码的重用度,当然也要注意不要引入过多的外部资源,因为这违反了法则 1
目前 CSS 的封装也不错,但是主要是针对 IE 系列的解决方案,可以考虑引入 YAML blueprint CSS 框架,轻松解决浏览器兼容性问题。

法则 9. 减少 DNS 查询次数
DNS 用于映射主机名和 IP 地址,一般一次解析需要 20 120 毫秒。为达到更高的性能, DNS 解析通常被多级别地缓存,如由 ISP 或局域网维护的 caching server ,本地机器操作系统的缓存(如 windows 上的 DNS Client Service ),浏览器。 IE 的缺省 DNS 缓存时间为 30 分钟, Firefox 的缺省缓冲时间是 1 分钟。
减少主机名可减少 DNS 查询的次数,但可能造成并行下载数的减少。避免 DNS 查询可减少响应时间,而减少并行下载数可能增加响应时间。一个可行的折中是把内容分布到至少 2 个,最多 4 个不同的主机名上。
我们的应用:
外贸:为了绕开浏览器对下载线程数的限制,我们对静态资源启用了多域名,但是这么做违反了该法则。不过,对 windows IE 来说, DNS 的缓存可以缓解该问题。

法则 10. 最小化 JavaScript 代码
最小化 JavaScript 代码指在 JS 代码中删除不必要的字符,从而降低下载时间。两个流行的工具是 JSMin YUI Compressor
混淆是最小化于源码的备选方式。象最小化一样,它通过删除注释和空格来减少源码大小,同时它还可以对代码进行混淆处理。作为混淆的一部分,函数名和变量名被替换成短的字符串,这使得代码更紧凑,同时也更难读,使得难于被反向工程。 Dojo Compressor (ShrinkSafe) 是最常见的混淆工具。
最小化是安全的、直白的过程,而混淆则更复杂,而且容易产生问题。从对美国 10 大网站的调查来看,通过最小化,文件可减少 21% ,而混淆则可减少 25%
除了最小化外部脚本文件外,内嵌的脚本代码也应该被最小化。即使脚本根据法则 4 被压缩后传输,最小化脚本刻减少文件大小 5% 或更高。
我们的应用:
我们没有直接使用 JS 压缩,但是我们用的许多组件例如 ext2 jquery 等,已经在为我们实践该法则。

法则 11. 避免重定向
重定向功能是通过 301 302 这两个 HTTP 状态码完成的,如:
    HTTP/1.1 301 Moved Permanently
    Location: http://example.com/newuri
    Content-Type: text/html

浏览器自动重定向请求到 Location 指定的 URL 上,重定向的主要问题是降低了用户体验。
一种最耗费资源、经常发生而很容易被忽视的重定向是 URL 的最后缺少 / ,如访问 http://astrology.yahoo.com/astrology 将被重定向到 http://astrology.yahoo.com/astrology/ 。在 Apache 下,可以通过 Alias mod_rewrite DirectorySlash 等方式来解决该问题。
我们的应用:
经验丰富的 SA 已经为我们考虑了这个问题,有兴趣的同学可以看看线上环境的 Apache 配置文件: httpd.conf

法则 12. 删除重复的脚本文件
在一个页面中包含重复的 JS 脚本文件会影响性能,即它会建立不必要的 HTTP 请求和额外的 JS 执行。
不必要的 HTTP 请求发生在 IE 下,而 Firefox 不会产生多余的 HTTP 请求。额外的 JS 执行,不管在 IE 下,还是在 Firefox 下,都会发生。
一个避免重复的脚本文件的方式是使用模板系统来建立脚本管理模块。除了防止重复的脚本文件外,该模块还可以实现依赖性检查和增加版本号到脚本文件名中,从而实现超长的过期时间。
我们的应用:
旧版本的 Xplatform 中这个问题比较严重,不过相信新版的 XCube 不会重蹈覆辙。

法则 13. 配置 ETags
ETags
是用于确定浏览器缓存中元素是否与 Web server 中的元素相匹配的机制,它是比 last-modified date 更灵活的元素验证机制。 ETag 是用于唯一表示元素版本的字符串,它需被包括在引号中。 Web server 首先在 response 中指定 ETag
    HTTP/1.1 200 OK
    Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
    ETag: "10c24bc-4ab-457e1c1f"
    Content-Length: 12195
后来,如果浏览器需要验证某元素,它使用 If-None-Match 头回传 ETag Web server ,如果 ETag 匹配,则服务器返回 304 代码,从而节省了下载时间:
    GET /i/yahoo.gif HTTP/1.1
    Host: us.yimg.com
    If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
    If-None-Match: "10c24bc-4ab-457e1c1f"
    HTTP/1.1 304 Not Modified

ETags
的问题在于它们是基于服务器唯一性的某些属性构造的,如 Apache1.3 2.x ,其格式是 inode-size-timestamp ,而在 IIS5.0 6.0 下,其格式是 Filetimestamp:ChangeNumber 。这样同一个元素在不同的 web server 上,其 ETag 是不一样的。这样在多 Web server 的环境下,浏览器先从 server1 请求某元素,后来向 server2 验证该元素,由于 ETag 不同,所以缓存失效,必须重新下载。
因此,如果您未用到 ETags 系统提供的灵活的验证机制,最好删除 ETag 。删除 ETag 会减少 http response 及后续请求的 HTTP 头的大小。微软支持文章描述了如何删除 ETags ,而在 Apache 下,只要在配置文件中设置 FileETag none 即可。
我们的应用:
E
网打尽:自定义 ETag 的生成策略,以尽量减少探头规则的生成次数。由于不是采用服务器默认的 ETag ,不存在该问题。
其他产品线:要注意了,这点大家都没有关注过吧,赶快检查一下 Apache 中的配置。

法则 14. 缓存 Ajax
性能优化法则同样适用于 web 2.0 应用。提高 Ajax 的性能最重要的方式是使得其 response 可缓存,就象 法则 3 增加 Expires Header” 讨论的那样。以下其他法则同样适用于 Ajax ,当然法则 3 是最有效的方式 :
法则 4. 压缩页面元素
法则 9. 减少 DNS 查询次数
法则 10. 最小化脚本文件
法则 11. 避免重定向
法则 13. 配置 ETags.
我们的应用:
更多情况下,我们倒不希望 Ajax 请求被缓存,此时为每个 Ajax 请求的 url 附加一个时间戳就可以了

 

 

 

这是基于Web应用性能有关的两个简单法则:

  • 尽可能的减少数据的传输量
  • 尽可能的减少数据的传输频率

若使用得当,此两条法则会:

  • 提高网页的加载速度
  • 降低服务器使用的资源
  • 提高网络带宽利用率

使用这些技巧来开发Web应用,不仅能够提高用户对基于web的一个应用的满意度,更可以节约网站数据传输的成本。在这里讲述的技术细节可帮助我们写出很好很实用的代码,从更广泛的角度来讲,这也将会给Web应用打造出良好的可用性基础。

1. Markup优化

典型的markup要么是手工编辑出来的,在非常紧凑,注重标准的格式基础上加入注释和空白区域(white space)的文件;要么是编辑器生成的,非常之肥胖,带有过分的格式编排及编辑器特有的通常用来控制结构的注释,甚至还会有不少重复的和没有用修饰或者 代码。这两者都不是最优传输的情况。下列技巧既安全又容易,是减小文件尺寸的好方法:

  • 尽可能的除去空白区域

一般而言,空白区域字符(空格、制表符、换行符等)都可以安全删除,但要避免修改pre, textarea, 及受CSS属性中white-space影响的标签。

  •   除去注释

除了在客户端给IE和doctype声明的条件注释外,几乎所有的注释都可以安全去除掉。

  • 使用最短格式的颜色表示

使用颜色时,不要一股脑的使用十六进制或全颜色名称(full color name),要尽可能根据实际情况使用最短格式的颜色表示。比如,一个为#ff0000 的颜色属性可以直接用red来说明,而lightgoldenrodyellow可以换成 #fafad2。颜色全使用小写。

  • 使用最短格式的字符表示

和最短颜色表示一样,一些名称可以用最短字符来表示,我们可以用较短的数字来代替某些长长的字母。比如:È 可以变成È。或者,偶尔这个方法反过来也行,比如:ð 如果变成ð则可以省一个字节。不过,这个方法不太安全,而且成效有限。

  • 除去无用的标签

有些‘垃圾’markup,比如使用了多次的重复标签或者某些编辑器里用作广告的meta 标签,都可以安全地被删除。

2.CSS优化

CSS也有一套成熟而又简单的优化方法。实际上,时下大多数的CSS都较 (X)HTML更容易压缩。下面所列的技巧除了最后一条都是安全的。最后一条涉及到客户端的网页技术,可能会变得比较复杂。

  • 除去CSS中的空白区域

如同除去markup代码中的注释一样,由于CSS中的注释对普通的最终用户来说并没有什么实用价值,所以也应该被除去。不过,如果考虑到较低级的浏览器,则在CSS中的style标签中的屏蔽注释信息不可以被除去。

  • 使用最短格式来表示颜色值

和HTML一样,CSS颜色也可以用词语或十六进制格式表示。注意,在CSS中这样做的效果会稍微明显一些。主要是因为CSS中支持3位的十六进制色值,例如对白色可用#fff 来表示。

  • 对CSS的规则进行合并、减少或删除

CSS中的诸如字体大小、字体重量等规则往往可以使用一种单属性字体的速记注释方式来表示。使用得当的话,这个技巧可以让您把如下的规则:

p {
    font-size: 36pt;
    font-family: Arial;
    line-height: 48pt;
    font-weight: bold;
}

改写成下面简短的形式:

p{font:bold 36pt/48pt Arial;}

如果继承方法使用得当的话,您还会发现在样式表单中的一些规则可以显著的减少或干脆删掉。到目前为止尚没有能自动移除规则的工具,所以只能通过手工调整CSS向导(Wizard)来进行这些工作。

  • 对类和ID值进行重命名

.superSpecial {color: red; font-size: 36pt;}

可将其更名为sS。而对ID值一样可以遵循这样的原则,例如对于:

#firstParagraph {background-color: yellow;}

则可将原来的"#firstParagraph" 重命名为"#fp",并在整个文档中重复这一动作。诚然,这样做可能会涉及到“标识-样式-脚本”互相依赖的问题:如果一个"tag"有一个ID值,而这 个值又可能不但用于样式表,还可能用于脚本参考,甚至可能是一个链接目标地址。在这种情况下,您一旦修改了这个值,您就必须得保证对所有相关的脚本和链接 参考都进行了相应的修改,包括其他文件中的这个值,所以千万要小心细致。

改变类的值相对改变ID值来说,危险性小一些。因为经验告诉我们,比较起ID值来说,大多数JavaScript程序员都不太经常处理类的值。然而,改变类的名称来缩减CSS的尺寸也面临着和改变ID名称同样的问题,所以再次强调,要小心谨慎。

请注意:最好不要更改名称属性,尤其是表单区域中的名称属性。因为这些数值也会被服务器端程序所操作。虽然不是不可能,但对多数的网站来讲,要计算好这些相互依赖关系是困难的。

3.JavaScript优化

  • 除去JavaScript注释

所有的 // or /* */ 注释都可以安全删除,因为 它们对于最终使用者来说没有任何意义(除非有人想了解您的脚本是如何工作的)。

  • 除去JavaScript中的空白区域

除去JavaScript中的空白区域并不象想象的那么有用。一方面,像如下代码:

x = x + 1;

显然可以简短得写成

x=x+1;

然而,很多随便的JavaScript程序员会忘记在两行之间加上分号,这时空白区域的除去就会带来问题。比如,下面合法的JavaScript使用了暗示的(implied)分号:

x=x+1

y=y+1

草率地删除了空白区域则会产生如下表达式:

x=x+1y=y+1

显然,错误就产生了。但如果您加上必需的分号,如下:

x=x+1;y=y+1;

  • 进行代码优化

简单的方法如除去暗示的(implied)分号,某些情形下的变量声明或者空回车语句都可以进一步减少脚本代码。一些简略的表达方式也会产生很好的优化,例如:

x=x+1;

可以写成:

x++;

不过得小心谨慎,不然代码很容易出错。

  • 重命名用户自定义的变量和函数

为了阅读方便,我们都知道在脚本中应该使用象sumTotal这样的变量而不是s。不过,考虑到下载的速度,sumTotal这个变量就显得冗长 了。这个长度对于最终使用者来说没有意义,但对浏览器下载则是个负担。这个时候s就成为较好的选择了。先写好方便阅读的代码,然后再使用一些工具来处理以 供交付。这种处理方式在这里再一次展示了其价值所在。将所有的名称都重新用一个或两个字母来命名将带来显著的改善。

  • 改写内建(built-in)对象

长用户变量名会造成JavaScript代码过长,除此之外,内建(built-in)对象(比如Window、Document、Navigator等)也是原因之一。例如:

alert(window.navigator.appName);

alert(window.navigator.appVersion);

alert(window.navigator.userAgent);

可以改写成如下简短的代码:

w=window;n=w.navigator;a=alert;

a(n.appName);

a(n.appVersion);

a(n.userAgent);

如果这几个对象使用频繁的话,这样改写带来的好处就不言而喻了。事实上这些对象也的确经常被调用。然而我要提醒的是,如果Window或 Navigator对象仅仅被使用了一次的话,这样的替换反而使代码变得更长。所以手工进行这种优化时要格外小心,不过好在目前市面的常用的 JavaScript代码优化工具都已经考虑到这个因素了。

这个技巧带来一个对象更名后脚本执行效率的问题:除了代码长短上带来的好处,这种改写更名实际上还会稍微的提高一点脚本执行的速度,因为这些对象将 会被放在所有被调用对象中比较靠前的位置。JavaScript游戏开发程序员使用这个技巧已经有多年了,下载和执行速度都会有所提高,并且对本地浏览器 的内存花销也会降低,可谓一石三

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值