WEB 高性能开发(10) - 疯狂的HTML压缩

前言:    

     上一篇随笔中网友 skyaspnet  问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的 html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动 态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对js,css启动gzip比较好是因为js,css都会使用 缓存。我个人觉得的压缩html的最大好处就是一本万利,只要写好了一次,以后所有程序都可以使用,不会增加任何额外的开发工作。

 

     在“JS、CSS的合并、压缩、缓存管理 ”一文中说到自己写过的1个自动合并、压缩JS,CSS,并添加版本号的组件。这次把压缩 html的功能也加入到该组件中,流程很简单,就是在程序启动(contextInitialized or Application_Start)的时候扫描所有html,jsp(aspx)进行压缩。

 

压缩的注意事项:

     实现的方式主要是用正则表达式去查找,替换。在html压缩的时候,主要要注意下面几点:

          1. pre,textarea 标签里面的内容格式需要保留,不能压缩。

          2. 去掉html注释的时候,有些注释是不能去掉的,比如:<!--[if IE 6]> ..... <![endif]-->

          3. 压缩嵌入式js中的注释要注意,因为可能注释符号会出现在字符串中,比如: var url = "http://www.cnblogs.com";    // 前面的//不是注释

              去掉JS换行符的时候,不能直接跟一下行动内容,需要有空格,考虑下面的代码:

              else

                 return;

             如果不带空格,则变成elsereturn。

          4. jsp(aspx) 中很有可能会使用<% %>嵌入一些服务器代码,这个时候也需要单独处理,里面注释的处理方法跟js的一样。

 

源代码:

    下面是java实现的源代码,也可以 猛击此处 下载该代码,相信大家都看的懂,也很容易改成net代码:

import java.io.StringReader;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.*;

/*******************************************
 * 压缩jsp,html中的代码,去掉所有空白符、换行符
 * @author  bearrui(ak-47)
 * @version 0.1
 * @date     2010-5-13
 *******************************************/
public class HtmlCompressor {
	private static String tempPreBlock = "%%%HTMLCOMPRESS~PRE&&&";
	private static String tempTextAreaBlock = "%%%HTMLCOMPRESS~TEXTAREA&&&";
	private static String tempScriptBlock = "%%%HTMLCOMPRESS~SCRIPT&&&";
	private static String tempStyleBlock = "%%%HTMLCOMPRESS~STYLE&&&";
	private static String tempJspBlock = "%%%HTMLCOMPRESS~JSP&&&";
	
	private static Pattern commentPattern = Pattern.compile("<!--\\s*[^\\[].*?-->", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	private static Pattern itsPattern = Pattern.compile(">\\s+?<", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	private static Pattern prePattern = Pattern.compile("<pre[^>]*?>.*?</pre>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); 
	private static Pattern taPattern = Pattern.compile("<textarea[^>]*?>.*?</textarea>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	private static Pattern jspPattern = Pattern.compile("<%([^-@][\\w\\W]*?)%>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	// <script></script>
	private static Pattern scriptPattern = Pattern.compile("(?:<script\\s*>|<script type=['\"]text/javascript['\"]\\s*>)(.*?)</script>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	private static Pattern stylePattern = Pattern.compile("<style[^>()]*?>(.+)</style>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);

	// 单行注释,
	private static Pattern signleCommentPattern = Pattern.compile("//.*");
	// 字符串匹配
	private static Pattern stringPattern = Pattern.compile("(\"[^\"\\n]*?\"|'[^'\\n]*?')");
	// trim去空格和换行符
	private static Pattern trimPattern = Pattern.compile("\\n\\s*",Pattern.MULTILINE);
	private static Pattern trimPattern2 = Pattern.compile("\\s*\\r",Pattern.MULTILINE);
	// 多行注释
	private static Pattern multiCommentPattern = Pattern.compile("/\\*.*?\\*/", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);

	private static String tempSingleCommentBlock = "%%%HTMLCOMPRESS~SINGLECOMMENT&&&";  // //占位符
	private static String tempMulitCommentBlock1 = "%%%HTMLCOMPRESS~MULITCOMMENT1&&&";  // /*占位符
	private static String tempMulitCommentBlock2 = "%%%HTMLCOMPRESS~MULITCOMMENT2&&&";  // */占位符
	
	
	public static String compress(String html) throws Exception {
		if(html == null || html.length() == 0) {
			return html;
		}
		
		List<String> preBlocks = new ArrayList<String>();
		List<String> taBlocks = new ArrayList<String>();
		List<String> scriptBlocks = new ArrayList<String>();
		List<String> styleBlocks = new ArrayList<String>();
		List<String> jspBlocks = new ArrayList<String>();
		
		String result = html;
		
		//preserve inline java code
		Matcher jspMatcher = jspPattern.matcher(result);
		while(jspMatcher.find()) {
			jspBlocks.add(jspMatcher.group(0));
		}
		result = jspMatcher.replaceAll(tempJspBlock);
		
		//preserve PRE tags
		Matcher preMatcher = prePattern.matcher(result);
		while(preMatcher.find()) {
			preBlocks.add(preMatcher.group(0));
		}
		result = preMatcher.replaceAll(tempPreBlock);
		
		//preserve TEXTAREA tags
		Matcher taMatcher = taPattern.matcher(result);
		while(taMatcher.find()) {
			taBlocks.add(taMatcher.group(0));
		}
		result = taMatcher.replaceAll(tempTextAreaBlock);
		
		//preserve SCRIPT tags
		Matcher scriptMatcher = scriptPattern.matcher(result);
		while(scriptMatcher.find()) {
			scriptBlocks.add(scriptMatcher.group(0));
		}
		result = scriptMatcher.replaceAll(tempScriptBlock);
		
		// don't process inline css 
		Matcher styleMatcher = stylePattern.matcher(result);
		while(styleMatcher.find()) {
			styleBlocks.add(styleMatcher.group(0));
		}
		result = styleMatcher.replaceAll(tempStyleBlock);
		
		//process pure html
		result = processHtml(result);
		
		//process preserved blocks
		result = processPreBlocks(result, preBlocks);
		result = processTextareaBlocks(result, taBlocks);
		result = processScriptBlocks(result, scriptBlocks);
		result = processStyleBlocks(result, styleBlocks);
		result = processJspBlocks(result, jspBlocks);
		
		preBlocks = taBlocks = scriptBlocks = styleBlocks = jspBlocks = null;
		
		return result.trim();
	}
	
	private static String processHtml(String html) {
		String result = html;
		
		//remove comments
//		if(removeComments) {
			result = commentPattern.matcher(result).replaceAll("");
//		}
		
		//remove inter-tag spaces
//		if(removeIntertagSpaces) {
			result = itsPattern.matcher(result).replaceAll("><");
//		}
		
		//remove multi whitespace characters
//		if(removeMultiSpaces) {
			result = result.replaceAll("\\s{2,}"," ");
//		}
				
		return result;
	}
	
	private static String processJspBlocks(String html, List<String> blocks){
		String result = html;
		for(int i = 0; i < blocks.size(); i++) {
			blocks.set(i, compressJsp(blocks.get(i)));
		}
		//put preserved blocks back
		while(result.contains(tempJspBlock)) {
			result = result.replaceFirst(tempJspBlock, Matcher.quoteReplacement(blocks.remove(0)));
		}
		
		return result;
	}
	private static String processPreBlocks(String html, List<String> blocks) throws Exception {
		String result = html;
		
		//put preserved blocks back
		while(result.contains(tempPreBlock)) {
			result = result.replaceFirst(tempPreBlock, Matcher.quoteReplacement(blocks.remove(0)));
		}
		
		return result;
	}
	
	private static String processTextareaBlocks(String html, List<String> blocks) throws Exception {
		String result = html;
		
		//put preserved blocks back
		while(result.contains(tempTextAreaBlock)) {
			result = result.replaceFirst(tempTextAreaBlock, Matcher.quoteReplacement(blocks.remove(0)));
		}
		
		return result;
	}
	
	private static String processScriptBlocks(String html, List<String> blocks) throws Exception {
		String result = html;
		
//		if(compressJavaScript) {
			for(int i = 0; i < blocks.size(); i++) {
				blocks.set(i, compressJavaScript(blocks.get(i)));
			}
//		}
		
		//put preserved blocks back
		while(result.contains(tempScriptBlock)) {
			result = result.replaceFirst(tempScriptBlock, Matcher.quoteReplacement(blocks.remove(0)));
		}
		
		return result;
	}
	
	private static String processStyleBlocks(String html, List<String> blocks) throws Exception {
		String result = html;
		
//		if(compressCss) {
			for(int i = 0; i < blocks.size(); i++) {
				blocks.set(i, compressCssStyles(blocks.get(i)));
			}
//		}
		
		//put preserved blocks back
		while(result.contains(tempStyleBlock)) {
			result = result.replaceFirst(tempStyleBlock, Matcher.quoteReplacement(blocks.remove(0)));
		}
		
		return result;
	}
	
	private static String compressJsp(String source)  {
		//check if block is not empty
		Matcher jspMatcher = jspPattern.matcher(source);
		if(jspMatcher.find()) {
			String result = compressJspJs(jspMatcher.group(1));
			return (new StringBuilder(source.substring(0, jspMatcher.start(1))).append(result).append(source.substring(jspMatcher.end(1)))).toString();
		} else {
			return source;
		}
	}	
	private static String compressJavaScript(String source)  {
		//check if block is not empty
		Matcher scriptMatcher = scriptPattern.matcher(source);
		if(scriptMatcher.find()) {
			String result = compressJspJs(scriptMatcher.group(1));
			return (new StringBuilder(source.substring(0, scriptMatcher.start(1))).append(result).append(source.substring(scriptMatcher.end(1)))).toString();
		} else {
			return source;
		}
	}
		
	private static String compressCssStyles(String source)  {
		//check if block is not empty
		Matcher styleMatcher = stylePattern.matcher(source);
		if(styleMatcher.find()) {
			// 去掉注释,换行
			String result= multiCommentPattern.matcher(styleMatcher.group(1)).replaceAll("");
			result = trimPattern.matcher(result).replaceAll("");
			result = trimPattern2.matcher(result).replaceAll("");
			return (new StringBuilder(source.substring(0, styleMatcher.start(1))).append(result).append(source.substring(styleMatcher.end(1)))).toString();
		} else {
			return source;
		}
	}
	
	private static String compressJspJs(String source){
		String result = source;
		// 因注释符合有可能出现在字符串中,所以要先把字符串中的特殊符好去掉
		Matcher stringMatcher = stringPattern.matcher(result);
		while(stringMatcher.find()){
			String tmpStr = stringMatcher.group(0);
			
			if(tmpStr.indexOf("//") != -1 || tmpStr.indexOf("/*") != -1 || tmpStr.indexOf("*/") != -1){
				String blockStr = tmpStr.replaceAll("//", tempSingleCommentBlock).replaceAll("/\\*", tempMulitCommentBlock1)
								.replaceAll("\\*/", tempMulitCommentBlock2);
				result = result.replace(tmpStr, blockStr);
			}
		}
		// 去掉注释
		result = signleCommentPattern.matcher(result).replaceAll("");
		result = multiCommentPattern.matcher(result).replaceAll("");
		result = trimPattern2.matcher(result).replaceAll("");
		result = trimPattern.matcher(result).replaceAll(" ");
		// 恢复替换掉的字符串
		result = result.replaceAll(tempSingleCommentBlock, "//").replaceAll(tempMulitCommentBlock1, "/*")
				.replaceAll(tempMulitCommentBlock2, "*/");
		
		return result;
	}
}

使用注意事项 :

 

      使用了上面方法后,再运行程序,是不是发现每个页面查看源代码的时候都变成1行啦,还不错吧,但是在使用的时候还是要注意一些问题:

           1. 嵌入js本来想调用yuicompressor来压缩,yuicompressor压缩JS前,会先编译js是否合法,因我们嵌入的js中可能很多会用到 一些服务器端代码,比如 var now = <%=DateTime.now %> ,这样的代码会编译不通过,所以无法使用yuicompressor。

              最后只能自己写压缩JS代码,自己写的比较粗燥,所以有个问题还解决,就是如果开发人员在一句js代码后面没有加分号的话,压缩成1行就很有可能出问 题。所以使用这个需要保证每条语句结束后都必须带分号。

 

           2. 因为是在程序启动的时候压缩所有jsp(aspx),所以如果是用户请求的时候动态产生的html就无法压缩。

CMap数据库实战:从基因表达谱到药物重定位的可视化分析 本文详细介绍了如何利用CMap数据库进行药物重定位的可视化分析。通过实战演示,从差异基因列表准备、CMap查询参数设置,到结果筛选与高级可视化(如热图与OncoPrint风格图),系统讲解了从数据到洞察的全流程,并提供了关键的解读与验证指南,帮助研究者高效挖掘老药新用的潜力。 阅读详情

相关推荐

从钕铁硼到电机磁钢:用COMSOL玩转新能源永磁体磁场仿真(含3D模型导入技巧)

本文详细介绍了如何利用COMSOL Multiphysics进行新能源永磁体磁场仿真,从基础的单块钕铁硼磁钢建模到复杂的3D电机磁钢组件分析。重点讲解了3D模型导入技巧、辐射环等特殊结构的充磁定义,以及如何通过仿真结果优化磁路设计、评估气隙磁密和退磁风险,为工程师提供了一套高效、精准的有限元仿真解决方案。

2y3u4i5o6p的博客 1038

WEB高性能开发 - 疯狂的HTML压缩

前言: 上一篇随笔中网友skyaspnet问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对...

weixin_33804990的博客 132

金蝶个版本下载地址,记得保存收藏。

专业版 V10.0 http://downloads.cmcloud.cn/kis/kis_pro/10.0/kiszybV10sp1.rar V11.0 http://downloads.cmcloud.cn/kis/kis_pro/11.0/KISV11Setup.rar V12.0 http://dow...

饺子的博客 2506

WEB高性能开发:HTML压缩

WEB高性能开发:HTML压缩 [日期:2011-01-09] 来源:     作者: [字体:大 中 小] 上一篇随笔中网友 skyaspnet 问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的htm

Aking的专栏 3458

ADS(Arm Developer Suite)安装的坑!

实验课老师让我们区安装ADS,作为嵌入式开发的工具,原本以为这只是一个简单的软件,但是当我安装的时候,我才发现这软件太难了!安装了我大半天,看了其他的博客之后,最终才安装成功???????? 安装过程的一个巨坑就是:安装路径!以及安装许可注册文件 如果没有注意路径,最后安装成功以后打开软件就是蜜汁报错???? 报错了怎么办?删掉已安装的东西,重新安装吧,而且要删干净 再次安装的时候,打开安装包是这个提示...

weixin_42795819的博客 8218

LabVIEW QMH模板实战:从创建到错误处理的完整指南

本文提供LabVIEW QMH模板从创建到错误处理的完整实战指南。详细解析了队列消息处理器(QMH)作为LabVIEW核心设计模式如何实现任务解耦与异步通信,涵盖事件处理循环、消息处理循环的搭建、用户事件创建、队列引用管理、消息优先级控制以及健壮的错误处理机制,帮助开发者构建稳定、可维护的复杂应用程序。

white的博客 725

WEB高性能开发(10) - 疯狂的HTML压缩

1. 嵌入js本来想调用yuicompressor来压缩,yuicompressor压缩JS前,会先编译js是否合法,因我们嵌入的js中可能很多会用到一些服务器端代码,比如 var now = <%=DateTime.now %> ,这样的代码会编译不通过,所以无法使用yuicompressor。最后只能自己写压缩JS代码,自己写的比较粗燥,所以有个问题还解决,就是如果开发人员在一句js代码后面没有加分号的话,压缩成1行就很有可能出问题。2. 去掉html注释的时候,有些注释是不能去掉的,比如:<!

BQC383SLw的博客 228

SystemRDL:从寄存器描述到自动化生成的完整指南

本文详细介绍了SystemRDL如何通过“一份描述,多处生成”的理念,彻底解决芯片与FPGA设计中手动维护寄存器带来的不一致与低效问题。文章提供了从语言快速上手、核心组件讲解到使用PeakRDL工具链实现自动化代码与文档生成的完整实战指南,帮助硬件工程师构建高效、可靠的寄存器开发流程。

weixin_29243279的博客 659

html压缩java_WEB高性能开发(10) - 疯狂的HTML压缩

前言:上一篇随笔中网友skyaspnet问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对js,css启动gzip比较好是因为js,css...

weixin_42298922的博客 215

手把手教你用Cursor + 自定义MCP工具链,让AI帮你搞定App逆向(附adb-mcp/frida-mcp源码)

本文详细介绍了如何利用Cursor IDE与自定义MCP工具链构建AI驱动的App逆向工作流,涵盖adb-mcp和frida-mcp的源码实现与实战应用。通过自然语言指令,AI可自动化完成设备控制、动态Hook和数据分析,显著提升移动安全测试效率。适合安全研究人员和开发者快速掌握智能逆向技术。

weixin_33737134的博客 1301

基于Multisim的三相桥式全控整流电路建模、仿真与综合分析

本文基于Multisim 14.2平台,系统研究了三相桥式全控整流电路的建模与仿真方法。首先阐述了电路拓扑结构和工作原理,建立了数学模型。重点介绍了在Multisim中搭建主电路、设计六脉冲触发系统(含两种实现方案)的关键步骤,以及参数设置技巧。通过仿真分析了不同负载(阻性、阻感性)和触发角(0°-90°)下的输出特性,包括电压波形、电流谐波等性能指标。研究为电力电子教学提供了直观的仿真范例,并探讨了模型改进方向与实际工程应用价值。

huanghm88的专栏 353

html 中ip控件,WEB高性能开发(10) - 疯狂的HTML压缩

前言:上一篇随笔中网友skyaspnet问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对js,css启动gzip比较好是因为js,css...

weixin_33622043的博客 194

多准则决策方法体系(AHP、熵权法、随机森林、加权线性法、TOPSIS、模糊综合评价)

多准则决策方法体系的基础,是由三大赋权方法和三大评价框架构成的 “3+3” 核心模块。赋权方法聚焦于准则重要性的量化,评价框架则专注于多准则信息的综合集成,二者共同覆盖了从 “确定权重” 到 “输出结果” 的全流程决策需求。

```On Earth, Be Water, By Coding``` 522

java web 页面压缩_WEB高性能开发(10) - 疯狂的HTML压缩

2importjava.io.StringWriter;3importjava.util.*;4importjava.util.regex.*;56/*******************************************7*压缩jsp,html中的代码,去掉所有空白符、换行符8*@authorbearrui(ak-47)9*@version0.110*@dat...

weixin_33557333的博客 153

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

html压缩java_WEB高性能开发之疯狂的HTML压缩

一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对js,css启动gzip比较好是因为js,css都会使用缓存。我个人觉得的压缩html的最大好处就是一本万利,只要写好了一次,以后所有程序都可以使用,不会增加任何额外的开发工作。在“JS、CSS的合并、压缩、缓存管理”一文中说到自己写过...

weixin_42358594的博客 477

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

高性能WEB开发之疯狂的HTML压缩

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33953384的博客 88

WEB高性能开发() - 疯狂的HTML压缩

UnifiedTTS 通过统一的 API 屏蔽了不同 TTS 模型的差异,使你无需维护多个 SDK,即可在成本与效果之间自由切换。UnifiedTTS 抽象了不同模型的请求,这样用户可以用同一套请求参数标准来实现对不同TTS模型的调用,这个非常方便。所以,为了简化TTS的客户端调用,非常推荐使用 UnifiedTTS。下面根据API 文档:https://unifiedtts.com/zh/api-docs/tts-sync 实现一个可运行的参考实现,包括配置文件、请求模型、服务类与控制器。

2501_94607402的博客 364

疯狂特技赛车2源码解析:Unity引擎的Web化实践

WebGL技术:将Unity游戏直接编译为WebAssembly,在浏览器中实现接近原生的性能分块资源加载:将游戏代码、框架、内存、资源数据分离为不同的.unityweb文件,支持按需加载和缓存优化压缩传输:所有资源文件均经过gzip压缩,减少约70%的传输体积。例如原始的data.unityweb可能超过100MB,压缩后仅35.4MB渐进式加载:用户无需等待完整下载即可看到加载进度,改善了等待体验响应式布局:游戏自动适配不同屏幕尺寸,填满整个浏览器视口IL2CPP技术。

努力是为了站在万人之中,成为别人的光 1万+

HTML语义感知压缩:构建高性能Web的基建实践

HTML压缩是Web性能优化中最基础且高回报的技术手段之一,其本质是通过消除冗余空白、注释与格式化字符,在不改变语义的前提下显著减小响应体体积。其核心原理并非简单正则替换,而是基于HTML树状结构、嵌入式脚本(JS/CSS)及服务端模板(JSP/Thymeleaf)的分层状态机解析,实现安全、可预测的预处理压缩。该技术直接降低TTFB、提升Lighthouse评分、缓解服务器CPU压力,并与Gzip形成叠加增益。适用于所有服务端渲染或静态生成场景,尤其在电商、SaaS、政府服务平台等对首屏性能与稳定性要求严

as2886089的博客 413
上一篇: 该如何加载google-analytics(或其他第三方)的JS
下一篇: 高性能WEB开发(11) - flush让页面分块,逐步呈现
Fangrn
博客等级 码龄20年 9粉丝 266原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值