Thymeleaf thymeleaf怎么发音
myzbx 2024-12-20 17:25 13 浏览
一、Thymeleaf简介
- Thymeleaf是用来开发Web和独立环境项目的服务器端的Java模版引擎
- Spring官方支持的服务的渲染模板中,并不包含jsp。而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVC的视图技术,及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,你只用关注Thymeleaf的语法即可。
二、Thymeleaf的特点
- 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- 开箱即用:它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- 多方言支持:Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速地实现表单绑定、属性编辑器、国际化等功能。
- 与SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
三.ssm中添加thymeleaf
1.导入jar包
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
2.在springmvc.xml文件中设置视图解析器为thymeleaf解析
<!-- 使用thymeleaf解析 -->
<bean id="templateResolver"
class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML" />
<property name="cacheable" value="false" />
<property name="characterEncoding" value="UTF-8"/><!--不加会乱码-->
</bean>
<bean id="templateEngine"
class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<!--解决中文乱码-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
3.删除之前视图解析器为jsp的配置信息,如果两个都配置,谁在前面,用谁
<!-- 配置视图解析器 -->
<!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!--<property name="prefix" value="/WEB-INF/" />-->
<!--<property name="suffix" value=".jsp" />-->
<!--</bean>-->
4.配置web.xml文件中thymeleaf部分
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-ioc.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--SpringMVC配置文件的名字 <servlet-name>-servlet.xml
默认位置:src / resources
如果放在了 src/resources(maven)
contextConfigLocation:classpath:文件名即可!
Web-INF/xx.xml
contextConfigLocation:/WEB-INF/xx.xml
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 访问DispatcherServlet对应的路径 -->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern> <!--/不过滤jsp防止死循环-->
</servlet-mapping>
<!--配置thymeleaf -->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
5.创建控制器
package com.test.action;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("thymeleaf")
public class ThymeleafAction {
@RequestMapping("/testThymeleaf")
public String testThymeleaf(Model model)
{
model.addAttribute("uname","zhangsan");
return "testThymeleaf";// WEB-INF/views/testThymeleaf.html
}
}
6.创建html页面
根据之前设置的前缀
在web-inf/views/文件夹下创建html文件
访问传递过来的数据,注意声明Thymeleaf命名空间
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${uname}">aaaa</h1>
</body>
</html>
7.测试
? http://localhost:8080/testssm/thymeleaf/testThymeleaf
? 成功
?
张三
四.常用th属性解读
html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先级从1~8,数字越低优先级越高。
一、th:text :设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。优先级不高:order=7
二、th:value:设置当前元素的value值,类似修改指定属性的还有th:src,th:href。优先级不高:order=6
三、th:each:遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置,详细往后看。优先级很高:order=2
四、th:if:条件判断,类似的还有th:unless,th:switch,th:case。优先级较高:order=3
五、th:insert:代码块引入,类似的还有th:replace,th:include,三者的区别较大,若使用不恰当会破坏html结构,常用于公共代码块提取的场景。优先级最高:order=1
六、th:fragment:定义代码块,方便被th:insert引用。优先级最低:order=8
七、th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4
八、th:attr:修改任意属性,实际开发中用得较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。优先级一般:order=5
五.基础使用语法
1.创建HTML
由上文也可以知道需要在html中添加:
<html xmlns:th="http://www.thymeleaf.org">
这样,下文才能正确使用th:*形式的标签!
2.获取变量值${…}
通过**${…}进行取值,这点和ONGL表达式语法一致!**
<h1 th:text="${uname}"></h1>
<input type="text" th:value="${uname}" />
选择变量表达式*{…}
<div th:object="${items}">
<p th:text="*{name}" >产品名</p>
<p th:text="*{detail}" >产品名</p>
</div>
至于p里面的原有的值只是为了给前端开发时做展示用的.这样的话很好地做到了前后端分离。
这也是Thymeleaf非常好的一个特性:在无网络的情况下也能运行,也就是完全可以前端先写出页面,模拟数据展现效果,后端人员再拿此模板修改即可!
3.链接表达式: @{…}
用来配合link src href使用的语法,类似的标签有:th:href和th:src
链接表达式结构
无参:@{/xxx}
有参:@{/xxx(k1=v1,k2=v2)} 对应url结构:xxx?k1=v1&k2=v2
引入本地资源:@{/项目本地的资源路径}
引入外部资源:@{/webjars/资源在jar包中的路径}
列举:第三部分的实战引用会详细使用该表达式
<link th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<link th:href="@{/main/css/itdragon.css}" rel="stylesheet">
<form class="form-login" th:action="@{/user/login}" th:method="post" >
<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
<a href="details.html" th:href="@{/thymeleaf/showItemsById(id=${items.id})}">view</a>
4.${…}变量表达式
变量表达式有丰富的内置方法,使其更强大,更方便。
变量表达式功能
一、可以获取对象的属性和方法
二、可以使用ctx,vars,locale,request,response,session,servletContext内置对象
三、可以使用dates,numbers,strings,objects,arrays,lists,sets,maps等内置方法(重点介绍)
常用的内置对象
一、ctx :上下文对象。
二、vars :上下文变量。
三、locale:上下文的语言环境。
四、request:(仅在web上下文)的 HttpServletRequest 对象。
五、response:(仅在web上下文)的 HttpServletResponse 对象。
六、session:(仅在web上下文)的 HttpSession 对象。
七、servletContext:(仅在web上下文)的 ServletContext 对象
这里以常用的Session举例,用户刊登成功后,会把用户信息放在Session中,Thymeleaf通过内置对象将值从session中获取。
// java 代码将用户名放在session中
session.setAttribute("userinfo",username);
// Thymeleaf通过内置对象直接获取
th:text="${session.userinfo}"
常用的内置方法
一、strings:字符串格式化方法,常用的Java方法它都有。比如:equals,equalsIgnoreCase,length,trim,toUpperCase,toLowerCase,indexOf,substring,replace,startsWith,endsWith,contains,containsIgnoreCase等
二、numbers:数值格式化方法,常用的方法有:formatDecimal等
三、bools:布尔方法,常用的方法有:isTrue,isFalse等
四、arrays:数组方法,常用的方法有:toArray,length,isEmpty,contains,containsAll等
五、lists,sets:集合方法,常用的方法有:toList,size,isEmpty,contains,containsAll,sort等
六、maps:对象方法,常用的方法有:size,isEmpty,containsKey,containsValue等
七、dates:日期方法,常用的方法有:format,year,month,hour,createNow等
文章底部提供了对应的官网链接
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ITDragon Thymeleaf 内置方法</title>
</head>
<body>
<h2>ITDragon Thymeleaf 内置方法</h2>
<h3>#strings </h3>
<div th:if="${not #strings.isEmpty(itdragonStr)}" >
<p>Old Str : <span th:text="${itdragonStr}"/></p>
<p>toUpperCase : <span th:text="${#strings.toUpperCase(itdragonStr)}"/></p>
<p>toLowerCase : <span th:text="${#strings.toLowerCase(itdragonStr)}"/></p>
<p>equals : <span th:text="${#strings.equals(itdragonStr, 'itdragonblog')}"/></p>
<p>equalsIgnoreCase : <span th:text="${#strings.equalsIgnoreCase(itdragonStr, 'itdragonblog')}"/></p>
<p>indexOf : <span th:text="${#strings.indexOf(itdragonStr, 'r')}"/></p>
<p>substring : <span th:text="${#strings.substring(itdragonStr, 2, 8)}"/></p>
<p>replace : <span th:text="${#strings.replace(itdragonStr, 'it', 'IT')}"/></p>
<p>startsWith : <span th:text="${#strings.startsWith(itdragonStr, 'it')}"/></p>
<p>contains : <span th:text="${#strings.contains(itdragonStr, 'IT')}"/></p>
</div>
<h3>#numbers </h3>
<div>
<p>formatDecimal 整数部分随意,小数点后保留两位,四舍五入: <span th:text="${#numbers.formatDecimal(itdragonNum, 0, 2)}"/></p>
<p>formatDecimal 整数部分保留五位数,小数点后保留两位,四舍五入: <span th:text="${#numbers.formatDecimal(itdragonNum, 5, 2)}"/></p>
</div>
<h3>#bools </h3>
<div th:if="${#bools.isTrue(itdragonBool)}">
<p th:text="${itdragonBool}"></p>
</div>
<h3>#arrays </h3>
<div th:if="${not #arrays.isEmpty(itdragonArray)}">
<p>length : <span th:text="${#arrays.length(itdragonArray)}"/></p>
<p>contains : <span th:text="${#arrays.contains(itdragonArray, 5)}"/></p>
<p>containsAll : <span th:text="${#arrays.containsAll(itdragonArray, itdragonArray)}"/></p>
</div>
<h3>#lists </h3>
<div th:if="${not #lists.isEmpty(itdragonList)}">
<p>size : <span th:text="${#lists.size(itdragonList)}"/></p>
<p>contains : <span th:text="${#lists.contains(itdragonList, 0)}"/></p>
<p>sort : <span th:text="${#lists.sort(itdragonList)}"/></p>
</div>
<h3>#maps </h3>
<div th:if="${not #maps.isEmpty(itdragonMap)}">
<p>size : <span th:text="${#maps.size(itdragonMap)}"/></p>
<p>containsKey : <span th:text="${#maps.containsKey(itdragonMap, 'thName')}"/></p>
<p>containsValue : <span th:text="${#maps.containsValue(itdragonMap, '#maps')}"/></p>
</div>
<h3>#dates </h3>
<div>
<p>format : <span th:text="${#dates.format(itdragonDate)}"/></p>
<p>custom format : <span th:text="${#dates.format(itdragonDate, 'yyyy-MM-dd HH:mm:ss')}"/></p>
<p>day : <span th:text="${#dates.day(itdragonDate)}"/></p>
<p>month : <span th:text="${#dates.month(itdragonDate)}"/></p>
<p>monthName : <span th:text="${#dates.monthName(itdragonDate)}"/></p>
<p>year : <span th:text="${#dates.year(itdragonDate)}"/></p>
<p>dayOfWeekName : <span th:text="${#dates.dayOfWeekName(itdragonDate)}"/></p>
<p>hour : <span th:text="${#dates.hour(itdragonDate)}"/></p>
<p>minute : <span th:text="${#dates.minute(itdragonDate)}"/></p>
<p>second : <span th:text="${#dates.second(itdragonDate)}"/></p>
<p>createNow : <span th:text="${#dates.createNow()}"/></p>
</div>
</body>
</html>
后台给负责给变量赋值,和跳转页面。
@RequestMapping("varexpressions")
public String varexpressions(ModelMap map) {
map.put("itdragonStr", "itdragonBlog");
map.put("itdragonBool", true);
map.put("itdragonArray", new Integer[]{1,2,3,4});
map.put("itdragonList", Arrays.asList(1,3,2,4,0));
Map itdragonMap = new HashMap();
itdragonMap.put("thName", "${#...}");
itdragonMap.put("desc", "变量表达式内置方法");
map.put("itdragonMap", itdragonMap);
map.put("itdragonDate", new Date());
map.put("itdragonNum", 888.888D);
return "grammar/varexpressions";
}
?
5.运算符
数学运算
- 二元操作:+, - , * , / , %
- 一元操作: - (负)
逻辑运算
- 一元 : and or
- 二元 : !,not
比较运算(为避免转义尴尬,可以使用括号中的英文进行比较运算!)
- 比较:> , < , >= , <= ( gt , lt , ge , le )
- 等于:== , != ( eq , ne )
条件运算
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
6.选择
if/unless
使用th:if和th:unless属性进行条件判断,th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容。
<td ><span th:if="${items.price gt 1000}" >精品</span></td>
<td ><span th:unless="${items.price gt 1000}" >次品</span></td>
switch
<div th:switch="${items.name}">
<p th:case="'aa'">aaaaaaaaa</p>
<p th:case="'bb'">bbbbbbb</p>
<p th:case="'cc'">cccccccc</p>
</div>
7.循环
th:each
thymeleaf的th:each常见用法
一.th:eath迭代集合用法:
<table border="1" id="stuTable">
<tr>
<td>是否选中</td>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr th:each="stu,userStat:${studentList}" >
<td><input th:type="checkbox" th:name="id" th:value="${stu.id}"></td>
<td th:text="${stu.id}">编号</td>
<td th:text="${stu.name}">姓名</td>
<td th:text="${stu.age}">年龄</td>
</tr>
</table>
二.迭代下标变量用法:
状态变量定义在一个th:每个属性和包含以下数据:
1.当前迭代索引,从0开始。这是索引属性。index
2.当前迭代索引,从1开始。这是统计属性。count
3.元素的总量迭代变量。这是大小属性。 size
4.iter变量为每个迭代。这是目前的财产。 current
5.是否当前迭代是奇数还是偶数。这些even/odd的布尔属性。
6.是否第一个当前迭代。这是first布尔属性。
7.是否最后一个当前迭代。这是last布尔属性。
用法实例:
<table border="1" id="stuTable">
<tr>
<td>是否选中</td>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr th:each="stu,userStat:${studentList}" th:class="${userStat.odd}?'odd':'even'">
<td th:text="${userStat.index}"></td>
<td><input th:type="checkbox" th:name="id" th:value="${stu.id}"></td>
<td th:text="${stu.id}">编号</td>
<td th:text="${stu.name}">姓名</td>
<td th:text="${stu.age}">年龄</td>
</tr>
</table>
控制器端代码:
package com.test.action;
import com.test.pojo.Items;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("thymeleaf")
public class ThymeleafAction {
@RequestMapping("/testThymeleaf")
public String testThymeleaf(Model model)
{
model.addAttribute("uname","zhangsan");
Items items=new Items();
items.setId(1);
items.setName("iphone");
items.setDetail("9999");
items.setPrice(6000);
model.addAttribute("items",items);
List<Items> itemsList=new ArrayList<Items>();
itemsList.add(new Items(101,"aa",1000,"aaa","1.jpg",new Date()));
itemsList.add(new Items(102,"bb",1000,"aaa","1.jpg",new Date()));
itemsList.add(new Items(103,"cc",1000,"aaa","1.jpg",new Date()));
model.addAttribute("itemsList",itemsList);
return "testThymeleaf";
}
@RequestMapping("/showItemsById")
public String showItemsById(Model model,int id)
{
model.addAttribute("id",id);
return "showItemsById";
}
}
tems);
List<Items> itemsList=new ArrayList<Items>();
itemsList.add(new Items(101,"aa",1000,"aaa","1.jpg",new Date()));
itemsList.add(new Items(102,"bb",1000,"aaa","1.jpg",new Date()));
itemsList.add(new Items(103,"cc",1000,"aaa","1.jpg",new Date()));
model.addAttribute("itemsList",itemsList);
return "testThymeleaf";
}
@RequestMapping("/showItemsById")
public String showItemsById(Model model,int id)
{
model.addAttribute("id",id);
return "showItemsById";
}
}
相关推荐
- 一键生成高颜值图表!让你的文字瞬间有画面感,职场人必备!
-
哈喽,打工人们!忙碌的周中,大熊又来给你们带来一个超实用的效率神器啦!这次的宝藏网站绝对是那种用过就离不开的"真香"型产品!假设你明天就要做重要汇报,可面对一大堆密密麻麻的文字材料,你...
- 批量将 Word 转换为 PDF/Excel/Txt/图片等多种格式
-
Word文档是我们工作中经常会打交道的一种文档格式,我们也经常会有需要对Word文档进行格式转换的需求,比如将Word格式转换为PDF、将Word文档转换为Excel、将Word...
- 绝了!一键用AI生成高颜值动态PPT(附详细步骤+Prompt)
-
大家好,我是一名酷爱研究AI的产品经理,最近我有个新发现:那些花了你3天做出来的PPT,现在用AI可以1小时搞定!而且颜值还高!为什么AI做PPT比传统方式效率高10倍?我用一张图就能告诉你:AI生成...
- ztext - 简单几行代码创建酷炫3D特效文字的开源JS库
-
把网页上的文字变成酷炫的3D风格,还能制作旋转动效,有了ztext.js,只需要几行代码。ztext能做什么ztext.js是一个能把常规的平面文字变成3D样式的前端开源代码库,让开发者...
- 文字内插入小图片,也太可爱了吧(文字中怎么插图片)
-
图文排版H5手机版秀米有小伙伴留言问添加图片的时候可不可以把图片添加到文字之间比如下面这句话中的小贴纸图片后面可以接着输入文字其实吧这就是咱们的『文字内插入小图片』功能嘛可以用来在文字内加个表情包又...
- Linux环境下C++代码性能分析方法(linux怎么写c++代码)
-
技术背景在开发C++应用程序时,找出代码中运行缓慢的部分是进行性能优化的关键。在Linux系统上,有多种工具和方法可用于对C++代码进行性能分析,每种方法都有其特点和适用场景。实现步骤手动中断调试法在...
- SVG互动图文,让你的文章更有趣!教你4种简单易学的黑科技玩法!
-
如果你是一个公众号创作者,那么你一定想知道如何让你的文章更加吸引人,更加有趣,更加有创意。你可能已经尝试过各种图文排版技巧,但是你是否知道,有一种黑科技可以让你的文章变得更加酷炫,更加互动,更加爆款?...
- Videoscribe怎么实现实心中文汉字的手绘制作
-
很多朋友在制作手绘视频的时候,不知道怎么输入实心的中文汉字,之前我们已经给大家分享了怎么输入汉字的方法,但是有一点遗憾的是输出的汉字是空心的手绘展示,在视觉上并不是非常的美观。经过大家不断的探索,终于...
- 一款用于将文本转化成图表的现代化脚本语言
-
大家好,又见面了,我是GitHub精选君!今天要给大家推荐一个GitHub开源项目terrastruct/d2,该项目在GitHub有超过10.3kStar,用一句话介绍该项目就是:...
- 探秘 Web 水印技术(制作水印网站)
-
作者:fransli,腾讯PCG前端开发工程师Web水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印)...
- 不忍心卸载的五款神仙工具(不忍心卸载的五款神仙工具是什么)
-
001.效率工具uTools-装机必备的生产力工具集uTools是一款非常强大的可以装下几乎所有效率工具的电脑生产力工具集,目前拥有Windows、Mac和Linux三个版本。软件界面...
- 「SVG」飞花令!这份最高检工作报告“超有料”
-
原标题:【SVG】飞花令!这份最高检工作报告“超有料”栏目主编:秦红文字编辑:沈佳灵来源:作者:最高人民检察院...
- svg|2025政府工作报告,有没有你关心的数据?
-
··<setattributeName="visibility"begin="click+0s"dur="1ms"fill="freeze"restart="never"to="hi...
- videoscribe只能输入英文,如何输入中文文本?
-
videoscribe只能输入英文,如何输入中文文本?打开VideoScribe软件,打开要添加中文字体的位置。打开Photoshop并在文件中创建一个新的透明背景图层。注意:必须是透明背景层。...
- 五个流行的SVG在线编辑器(svg编辑工具)
-
随着响应网络的发展,越来越多的高质量的SVG在线编辑器被公众所熟知。SVG矢量图形也越来越受欢迎,以便在任何设备上呈现图像,甚至一些易于使用的SVG在线编辑器,可以替代PS,本文总结了五种流行的SVG...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 选择器 (30)
- CSS 轮廓 (30)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 中级教程 (30)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)