问题引入

当直接访问HTML文件时,可以访问到的部分静态资源可能由于路径配置的原因,在Springboot中报错,我们希望能在不影响当前网页功能、不破坏结构的情况下修改路径达到效果
又或者在前端各页面中存在大量雷同的代码块,例如博客项目中存在Navigation Bar和footer等布局大量相同的情况,如果想对这些布局中等元素进行统一修改将会非常麻烦

而Thymeleaf很好的解决了这样的痛点

项目示例

以下将做几个博客中简单的示例:
我们定义一个Thymeleaf模版_fragment.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head(title)">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:replace="${title}">博客详情</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.2.4/dist/semantic.min.css">
<link rel="stylesheet" href="../static/css/typo.css" th:href="@{/css/typo.css}">
<link rel="stylesheet" href="../static/css/me.css" th:href="@{/css/me.css}">
<link rel="stylesheet" href="../static/css/animate.css" th:href="@{/css/animate.css}">
<link rel="stylesheet" href="../static/lib/prism/prism.css" th:href="@{/lib/prism/prism.css}">
<link rel="stylesheet" href="../static/lib/tocbot/tocbot.css" th:href="@{/lib/tocbot/tocbot.css}">
<link rel="stylesheet" href="../static/lib/editormd/css/editormd.css" th:href="@{/lib/editormd/css/editormd.css}">
</head>
<body>
<nav th:fragment="menu(n)" class="ui inverted attached segment m-padded-tb-mini m-shadow-small">
<div class="ui container">
<div class="ui inverted secondary stackable menu">
<h2 class="ui teal header item">Blog</h2>
<a href="#" class="m-item item m-mobile-hide" th:classappend="${n==1}?'active'"><i class="home icon"></i>首页</a>
<a href="#" class="m-item item m-mobile-hide" th:classappend="${n==2}?'active'"><i class="idea icon"></i>分类</a>
<a href="#" class="m-item item m-mobile-hide" th:classappend="${n==3}?'active'"><i class="tags icon"></i>标签</a>
<a href="#" class="m-item item m-mobile-hide" th:classappend="${n==4}?'active'"><i class="clone icon"></i>归档</a>
<a href="#" class="m-item item m-mobile-hide" th:classappend="${n==5}?'active'"><i class="info icon"></i>关于我</a>
<div class="right item m-mobile-hide">
<div class="ui icon inverted transparent input">
<input type="text" placeholder="Search....">
<i class="search link icon"></i>
</div>
</div>
</div>
</div>
<a href="#" class="ui menu toggle black icon button m-right-top m-mobile-show">
<i class="sidebar icon"></i>
</a>
</nav>

<footer th:fragment="footer" class="ui inverted vertical segment m-padded-tb-massive">
......
</footer>

<th:block th:fragment="script">
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery.scrollto@2.1.3/jquery.scrollTo.min.js"></script>
<script src="../static/lib/prism/prism.js" th:src="@{/lib/prism/prism.js}"></script>
<script src="../static/lib/tocbot/tocbot.min.js" th:src="@{/lib/tocbot/tocbot.min.js}"></script>
<script src="../static/lib/qrcode/qrcode.min.js" th:src="@{/lib/qrcode/qrcode.min.js}"></script>
<script src="../static/lib/waypoint/jquery.waypoints.min.js" th:src="@{/lib/waypoint/jquery.waypoints.min.js}"></script>
<script src="../../static/lib/editormd/editormd.js" th:src="@{/lib/editormd/editormd.js}"></script>
</th:block>
</body>
</html>

其中

  • head块中,引入参数title,用于替换标签页标题“博客详情”字样
  • menu块中,引入参数n,不同的n对应不同的顶栏按钮被追加active属性
  • footer和script块也为类似模版代码
  • 注意到src路径在Springboot中由于静态资源的路径问题,不生效,我们改由Thymeleaf使用th:hrefth:src标签引入static下的新路径

具体调用方法如下:
在博客首页index.html中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="_fragments :: head(~{::title})">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.2.4/dist/semantic.min.css">
<link rel="stylesheet" href="../static/css/me.css">
</head>
<body>

<!--导航-->
<nav th:replace="_fragments :: menu(1)" class="ui inverted attached segment m-padded-tb-mini m-shadow-small">
<div class="ui container">
<div class="ui inverted secondary stackable menu">
<h2 class="ui teal header item">Blog</h2>
<a href="#" class="m-item item m-mobile-hide"><i class="home icon"></i>首页</a>
<a href="#" class="m-item item m-mobile-hide"><i class="idea icon"></i>分类</a>
<a href="#" class="m-item item m-mobile-hide"><i class="tags icon"></i>标签</a>
<a href="#" class="m-item item m-mobile-hide"><i class="clone icon"></i>归档</a>
<a href="#" class="m-item item m-mobile-hide"><i class="info icon"></i>关于我</a>
<div class="right item m-mobile-hide">
<div class="ui icon inverted transparent input">
<input type="text" placeholder="Search....">
<i class="search link icon"></i>
</div>
</div>
</div>
</div>
<a href="#" class="ui menu toggle black icon button m-right-top m-mobile-show">
<i class="sidebar icon"></i>
</a>
</nav>
<!--底部footer-->
<footer th:replace="_fragments :: footer" class="ui inverted vertical segment m-padded-tb-massive">
............
</footer>
<!--/*/<th:block th:replace="_fragments :: script">/*/-->
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
<!--/*/</th:block>/*/-->

<script>
$('.menu.toggle').click(function (){
$('.m-item').toggleClass('m-mobile-hide');
});
</script>
</body>
</html>
  • 通过th:replace="_fragments :: head(~{::title})"的方式,我们从原先inedx的代码块中获取到了title的值并将它覆盖到了_fragments对应模版上,随后使用模版替换了当前代码块
  • 通过th:replace="_fragments :: menu(1)" 加入了参数n = 1,触发了_fragments中对首页按钮追加的active属性,随后使用模版替换了代码块(甚至导航整块删除后Springboot访问仍然不受影响,因为已经被Thymeleaf替换了,但是本地静态网页渲染时结构会遭到破坏)
  • 通过加入<!--/*/<th:block th:replace="_fragments :: script">/*/--><!--/*/</th:block>/*/-->注释的方式,我们可以让浏览器直接加载HTML时忽略外部的包裹,而实际Springboot启动时,Thymeleaf代码块会视其为有效

PS:Thymeleaf的妙处就在这里,在本地打开对应的HTML时,Thymeleaf代码不生效,使用对应的本地路径,可以确保浏览器正常渲染,而使用Springboot时,Thymeleaf会注入模版和参数,按照我们之前配置的形式去运行,我们只需要对对应代码块加入一个标签就能实现托管

类似的我们还可以使用th:inline对Javascript和jQuery代码进行修改

例如,在博客输入页面中,我们引入了markdown-editor部件,并引入了css和js文件(已经通过Thymeleaf完成了路径替换)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="_fragments :: head(~{::title})">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客发布</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.2.4/dist/semantic.min.css">
<link rel="stylesheet" href="../../static/lib/editormd/css/editormd.css">
<link rel="stylesheet" href="../../static/css/me.css">
</head>
<body>
..................
<!--/*/<th:block th:replace="_fragments :: script">/*/-->
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
<script src="../../static/lib/editormd/editormd.js"></script>
<!--/*/</th:block>/*/-->
.................
</body>

关键在于为了markdown对正常使用,官方使用了一段jQuery代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
var contentEditor;
$(function() {
contentEditor = editormd("md-content", {
width : "100%",
height : 640,
syncScrolling : "single",
path : "../../static/lib/editormd/lib/",
tex: true,
flowChart : true,
sequenceDiagram : true
});
});
</script>

对应资源目录如下:

本地环境下浏览器渲染运行无问题

而部署到Springboot上时,插件开始转圈,并且无法加载:

显然由于路径配置问题,"../../static/lib/editormd/lib/"路径下的js和css文件不能生效,爆出404错误

这里我们采用th:inline对jQuery进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script th:inline="javascript">
var contentEditor;
$(function() {
contentEditor = editormd("md-content", {
width : "100%",
height : 640,
syncScrolling : "single",
path : /*[[@{/lib/editormd/lib/}]]*/ "../../static/lib/editormd/lib/",
tex: true,
flowChart : true,
sequenceDiagram : true
});
});
</script>

由于引入了注释,本地访问时浏览器仍然会去请求"../../static/lib/editormd/lib/"目录,而使用Springboot进行部署时,Thymeleaf会使用@{/lib/editormd/lib/}路径对原先路径进行替换

如图样式加载正常:

达到了预期效果