Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
本文題材來自于狂神說https://www.bilibili.com/video/BV18E411a7mC
主要涉及網(wǎng)頁的結(jié)構(gòu)和內(nèi)容。
css主要用來設(shè)置網(wǎng)頁的表現(xiàn)樣式,css的層疊樣式表只是一門標(biāo)記語言,也就是說沒有任何的語法支持,用過的人都知道css應(yīng)該有如下的缺點(diǎn):
基于如上的缺點(diǎn)誕生了【css預(yù)處理器】
什么是CSS預(yù)處理器
CSS預(yù)處理器定義了一種新的語言,其基本思想是,用一種專門的編程語言,為CSS增加了一些編程的特性,將CSS作為目標(biāo)生成文件,然后開發(fā)者就只需要使用這種語言進(jìn)行CSS的編碼工作。轉(zhuǎn)化成通俗易懂的話來說就是用一種專門的編程語言,進(jìn)行Web頁面樣式設(shè)計(jì),再通過編譯器轉(zhuǎn)化為正常的CSS文件,以供項(xiàng)目使用。
市面上常見的CSS預(yù)處理器
JavaScript一門弱類型腳本語言,其源代碼在發(fā)往客戶端運(yùn)行之前不需要經(jīng)過編譯,而是將文本格式的字符代碼發(fā)送給瀏覽器,由瀏覽器解釋運(yùn)行。
原生JS開發(fā)
我們?nèi)粘i_發(fā)都是按照【ECMAScript】標(biāo)準(zhǔn)的開發(fā)方式,簡稱ES特點(diǎn)是所有瀏覽器都支持此規(guī)范,我們需要知道的是ES5規(guī)范對所有的游覽器都支持,但是ES6是市面上用的最多的規(guī)范版本,主流游覽器支持,所以為了兼容某些游覽器,需要將ES6規(guī)范通過webpack降級為ES5。
TypeScript出現(xiàn)的背景
TypeScript 起源于使用JavaScript開發(fā)的大型項(xiàng)目 。由于JavaScript語言本身的局限性,難以勝任和維護(hù)大型項(xiàng)目開發(fā)。因此微軟開發(fā)了TypeScript ,使得其能夠勝任開發(fā)大型項(xiàng)目。
對于后端來學(xué)Vue的小伙伴是不是有個疑問,什么叫做DOM呢?有什么用處呢?
什么叫DOM?
DOM全稱 Document Object Model,即文檔對象模型,它允許腳本(js)控制Web頁面、窗口和文檔,通俗的說就是js操作html時的API。
主要目的是實(shí)現(xiàn)一套代碼三端統(tǒng)一(PC、Android:.apk、iOS:.ipa)并能夠調(diào)用到設(shè)備底層硬件(如:傳感器、GPS、攝像頭等),打包方式主要有以下兩種:
詳見微信官網(wǎng),這里就是介紹一個方便微信小程序UI開發(fā)的框架:WeUI。
前端人員為了方便開發(fā)也需要掌握一定的后端技術(shù)但我們Java后臺人員知道后臺知識體系極其龐大復(fù)雜,所以為了方便前端人員開發(fā)后臺應(yīng)用,就出現(xiàn)了Node JS這樣的技術(shù)。
Node JS的作者已經(jīng)聲稱放棄Node JS(說是架構(gòu)做的不好再加上笨重的node modules,可能讓作者不爽了吧)開始開發(fā)全新架構(gòu)的Deno
既然是后臺技術(shù),那肯定也需要框架和項(xiàng)目管理工具, Node JS框架及項(xiàng)目管理工具如下:
iview是一個強(qiáng)大的基于Vue的UI庫, 有很多實(shí)用的基礎(chǔ)組件比element ui的組件更豐富, 主要服務(wù)于PC界面的中后臺產(chǎn)品。使用單文件的Vue組件化開發(fā)模式基于npm+webpack+babel開發(fā), 支持ES 2015高質(zhì)量、功能豐富友好的API, 自由靈活地使用空間。
備注:屬于前端主流框架,選型時可考慮使用,主要特點(diǎn)是移動端支持較多
Element是餓了么前端開源維護(hù)的Vue UI組件庫, 組件齊全, 基本涵蓋后臺所需的所有組件,文檔講解詳細(xì), 例子也很豐富。主要用于開發(fā)PC端的頁面, 是一個質(zhì)量比較高的Vue UI組件庫。
備注:屬于前端主流框架,選型時可考慮使用,主要特點(diǎn)是桌面端支持較多
飛冰是阿里巴巴團(tuán)隊(duì)基于React/Angular/Vue的中后臺應(yīng)用解決方案, 在阿里巴巴內(nèi)部, 已經(jīng)有270多個來自幾乎所有BU的項(xiàng)目在使用。飛冰包含了一條從設(shè)計(jì)端到開發(fā)端的完整鏈路,幫助用戶快速搭建屬于自己的中后臺應(yīng)用。
備注:主要組件還是以React為主, 截止2019年02月17日更新博客前對Vue的支持還不太完善,目前尚處于觀望階段
Vant UI是有贊前端團(tuán)隊(duì)基于有贊統(tǒng)一的規(guī)范實(shí)現(xiàn)的Vue組件庫, 提供了-整套UI基礎(chǔ)組件和業(yè)務(wù)組件。通過Vant, 可以快速搭建出風(fēng)格統(tǒng)一的頁面,提升開發(fā)效率。
at-ui是一款基于Vue 2.x的前端UI組件庫, 主要用于快速開發(fā)PC網(wǎng)站產(chǎn)品。它提供了一套n pm+web pack+babel前端開發(fā)工作流程, CSS樣式獨(dú)立, 即使采用不同的框架實(shí)現(xiàn)都能保持統(tǒng)一的UI風(fēng)格。
cube-ui是滴滴團(tuán)隊(duì)開發(fā)的基于Vue js實(shí)現(xiàn)的精致移動端組件庫。支持按需引入和后編譯, 輕量靈活;擴(kuò)展性強(qiáng),可以方便地基于現(xiàn)有組件實(shí)現(xiàn)二次開發(fā)。
Flutter是谷歌的移動端UI框架, 可在極短的時間內(nèi)構(gòu)建Android和iOS上高質(zhì)量的原生級應(yīng)用。Flutter可與現(xiàn)有代碼一起工作, 它被世界各地的開發(fā)者和組織使用, 并且Flutter是免費(fèi)和開源的。
備注:Google出品, 主要特點(diǎn)是快速構(gòu)建原生APP應(yīng)用程序, 如做混合應(yīng)用該框架為必選框架
lonic既是一個CSS框架也是一個Javascript UI庫, lonic是目前最有潛力的一款HTML 5手機(jī)應(yīng)用開發(fā)框架。通過SASS構(gòu)建應(yīng)用程序, 它提供了很多UI組件來幫助開發(fā)者開發(fā)強(qiáng)大的應(yīng)用。它使用JavaScript MV VM框架和Angular JS/Vue來增強(qiáng)應(yīng)用。提供數(shù)據(jù)的雙向綁定, 使用它成為Web和移動開發(fā)者的共同選擇。
mpvue是美團(tuán)開發(fā)的一個使用Vue.js開發(fā)小程序的前端框架, 目前支持微信小程序、百度智能小程序,頭條小程序和支付寶小程序。框架基于Vue.js, 修改了的運(yùn)行時框架runtime和代碼編譯器compiler實(shí)現(xiàn), 使其可運(yùn)行在小程序環(huán)境中, 從而為小程序開發(fā)引入了Vue.js開發(fā)體驗(yàn)。
備注:完備的Vue開發(fā)體驗(yàn), 井且支持多平臺的小程序開發(fā), 推薦使用
WeUI是一套同微信原生視覺體驗(yàn)一致的基礎(chǔ)樣式庫, 由微信官方設(shè)計(jì)團(tuán)隊(duì)為微信內(nèi)網(wǎng)頁和微信小程序量身設(shè)計(jì), 令用戶的使用感知更加統(tǒng)一。包含button、cell、dialog、toast、article、icon等各式元素。
為了降低開發(fā)的復(fù)雜度, 以后端為出發(fā)點(diǎn), 比如:Struts、Spring MVC等框架的使用, 就是后端的MVC時代以SpringMVC流程為例:
優(yōu)點(diǎn)
缺點(diǎn)
時間回到2005年AJAX(Asynchronous JavaScript And XML, 異步JavaScript和XML,老技術(shù)新用法)被正式提出并開始使用CDN作為靜態(tài)資源存儲, 于是出現(xiàn)了JavaScript王者歸來(在這之前JS都是用來在網(wǎng)頁上貼狗皮膏藥廣告的) 的SPA(Single Page Application) 單頁面應(yīng)用時代。
優(yōu)點(diǎn)
這種模式下, **前后端的分工非常清晰, 前后端的關(guān)鍵協(xié)作點(diǎn)是AJAX接口。**看起來是如此美妙, 但回過頭來看看的話, 這與JSP時代區(qū)別不大。復(fù)雜度從服務(wù)端的JSP里移到了瀏覽器的JavaScript,瀏覽器端變得很復(fù)雜。類似Spring MVC, 這個時代開始出現(xiàn)瀏覽器端的分層架構(gòu):
缺點(diǎn)
此處的MVC模式如下:
優(yōu)點(diǎn)
缺點(diǎn)
前端為主的MVC模式解決了很多很多問題, 但如上所述, 依舊存在不少不足之處。隨著Node JS的興起, JavaScript開始有能力運(yùn)行在服務(wù)端。這意味著可以有一種新的研發(fā)模式:
在這種研發(fā)模式下,前后端的職責(zé)很清晰。對前端來說,兩個UI層各司其職:
通過Node, WebServer層也是JavaScript代碼, 這意味著部分代碼可前后復(fù)用, 需要SEO的場景可以在服務(wù)端同步渲染,由于異步請求太多導(dǎo)致的性能問題也可以通過服務(wù)端來緩解。前一種模式的不足,通過這種模式幾乎都能完美解決掉。
與JSP模式相比, 全棧模式看起來是一種回歸, 也的確是一種向原始開發(fā)模式的回歸, 不過是一種螺旋上升式的回歸。
MVVM(Model-View-ViewModel)是一種軟件設(shè)計(jì)模式,由微軟WPF(用于替代WinForm,以前就是用這個技術(shù)開發(fā)桌面應(yīng)用程序的)和Silverlight(類似于Java Applet,簡單點(diǎn)說就是在瀏覽器上運(yùn)行WPF)的架構(gòu)師Ken Cooper和Ted Peters開發(fā),是一種簡化用戶界面的事件驅(qū)動編程方式。由John Gossman(同樣也是WPF和Sliverlight的架構(gòu)師)與2005年在他的博客上發(fā)表。
MVVM源自于經(jīng)典的MVC(Model-View-Controller)模式。MVVM的核心是ViewModel層,負(fù)責(zé)轉(zhuǎn)換Model中的數(shù)據(jù)對象來讓數(shù)據(jù)變得更容易管理和使用。其作用如下:
MVVM已經(jīng)相當(dāng)成熟了,主要運(yùn)用但不僅僅在網(wǎng)絡(luò)應(yīng)用程序開發(fā)中。當(dāng)下流行的MVVM框架有Vue.js,Anfular JS
MVVM模式和MVC模式一樣,主要目的是分離視圖(View)和模型(Model),有幾大好處
View
View是視圖層, 也就是用戶界面。前端主要由HTH L和csS來構(gòu)建, 為了更方便地展現(xiàn)viewModel或者model層的數(shù)據(jù), 已經(jīng)產(chǎn)生了各種各樣的前后端模板語言, 比如FreeMarker,Thyme leaf等等, 各大MVVM框架如Vue.js.Angular JS, EJS等也都有自己用來構(gòu)建用戶界面的內(nèi)置模板語言。
Model
Model是指數(shù)據(jù)模型, 泛指后端進(jìn)行的各種業(yè)務(wù)邏輯處理和數(shù)據(jù)操控, 主要圍繞數(shù)據(jù)庫系統(tǒng)展開。這里的難點(diǎn)主要在于需要和前端約定統(tǒng)一的接口規(guī)則
ViewModel
ViewModel是由前端開發(fā)人員組織生成和維護(hù)的視圖數(shù)據(jù)層。在這一層, 前端開發(fā)者對從后端獲取的Model數(shù)據(jù)進(jìn)行轉(zhuǎn)換處理, 做二次封裝, 以生成符合View層使用預(yù)期的視圖數(shù)據(jù)模型。
??需要注意的是View Model所封裝出來的數(shù)據(jù)模型包括視圖的狀態(tài)和行為兩部分, 而Model層的數(shù)據(jù)模型是只包含狀態(tài)的
視圖狀態(tài)和行為都封裝在了ViewModel里。這樣的封裝使得ViewModel可以完整地去描述View層。由于實(shí)現(xiàn)了雙向綁定, ViewModel的內(nèi)容會實(shí)時展現(xiàn)在View層, 這是激動人心的, 因?yàn)榍岸碎_發(fā)者再也不必低效又麻煩地通過操縱DOM去更新視圖。
??MVVM框架已經(jīng)把最臟最累的一塊做好了, 我們開發(fā)者只需要處理和維護(hù)ViewModel, 更新數(shù)據(jù)視圖就會自動得到相應(yīng)更新,真正實(shí)現(xiàn)事件驅(qū)動編程。
??View層展現(xiàn)的不是Model層的數(shù)據(jù), 而是ViewModel的數(shù)據(jù), 由ViewModel負(fù)責(zé)與Model層交互, 這就完全解耦了View層和Model層, 這個解耦是至關(guān)重要的, 它是前后端分離方案實(shí)施的重要一環(huán)。
Vue(讀音/vju/, 類似于view)是一套用于構(gòu)建用戶界面的漸進(jìn)式框架, 發(fā)布于2014年2月。與其它大型框架不同的是, Vue被設(shè)計(jì)為可以自底向上逐層應(yīng)用。Vue的核心庫只關(guān)注視圖層, 不僅易于上手, 還便于與第三方庫(如:vue-router,vue-resource,vue x) 或既有項(xiàng)目整合。
在MVVM架構(gòu)中, 是不允許數(shù)據(jù)Model和視圖View直接通信的, 只能通過ViewModel來通信, 而ViewModel就是定義了一個Observer觀察者(俗稱雙向綁定)
至此, 我們就明白了, Vue.js就是一個MVVM的實(shí)現(xiàn)者, 他的核心就是實(shí)現(xiàn)了DOM監(jiān)聽與數(shù)據(jù)綁定
Vue不支持IE 8及以下版本, 因?yàn)閂ue使用了IE 8無法模擬的ECMAScript 5特性。但它支持所有兼容ECMAScript 5的瀏覽器
如果是Java開發(fā)的小伙伴喜歡用idea的那么可以直接安裝vue的插件即可。
**可能出現(xiàn)的問題:**安裝完插件右鍵新建文件發(fā)現(xiàn)沒有vue的模板。
解決辦法:
略!
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:"hello,vue!"
}
});
</script>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!--引用參數(shù)-->
<div id="app">{{message}}</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
// 定義一個vue實(shí)例對象
var vue = new Vue({
// 綁定元素
el:'#app',
// 定義參數(shù)并且賦值
data:{
message:"hello vue"
}
});
</script>
</body>
</html>
為了能夠更直觀的體驗(yàn)Vue帶來的數(shù)據(jù)綁定功能, 我們需要在瀏覽器測試一番, 操作流程如下:
關(guān)于 Web 方面的配置比較多,值得慶幸的是,Spring Boot 已經(jīng)幫我們預(yù)置初始化了很多基礎(chǔ)組件。但在實(shí)踐的過程中,某些基礎(chǔ)的組件并不能滿足我們的實(shí)際需求,這時就需要我們重新初始化相應(yīng)組件,甚至在某些極端的情況下需要完全接管 Spring Boot 的默認(rèn)配置。
本節(jié)將基于對前端模板框架 Thymeleaf 的集成,逐步向大家演示如何自定義 ViewResolver以及如何進(jìn)一步 擴(kuò)展 Spring MVC 配置。本實(shí)例涉及集成 Thymeleaf、自定義初始化ThymeleafViewResolver 以及擴(kuò)展 Spring MVC。
Thymeleaf 是一個 Java 類庫,能夠處理 HTML/HTML5、XML、JavaScript、CSS, 甚至純文本類型的文件。通常可以用作MVC中的View層,它可以完全替代 JSP。該框架是SpringBoot 首推的前端展示框架。
首先我們創(chuàng)建一個集成 Thymeleaf 的 SpringBootWeb 項(xiàng)目。集成 Thymeleaf 的核心操作就是引入對應(yīng)的 starter,對應(yīng)項(xiàng)目中 pom.xml 的依賴如下。
<dependency>
<groupId>org. springframework. boot</groupId>
<artifactId>spring- boot- starter-thymeleaf</ artifactId>
< /dependency>
<groupId>org. springframework . boot</groupId>
<artifactId>spring- boot - starter - web</artifactId>
</ dependency>
通過前面的學(xué)習(xí)我們已經(jīng)得知引入該 starter 之后,Spring Boot 便會進(jìn)行一個初始化的基本配置,因此針對 Thymeleaf 的最簡單集成便完成了,關(guān)于頁面展示和基礎(chǔ)配置我們暫時先不考慮。當(dāng)集成 Thymeleaf 之后,Thymeleaf 對應(yīng)的自動配置類 ThymeleafAutoConfiguration 中會初始化一個 ThymeleafViewResolver, 用來對 Thymeleaf 的頁面進(jìn)行解析和渲染。這一操作本質(zhì)上同默認(rèn)的 BeanNameViewResolver 作用-樣,都實(shí)現(xiàn)了 ViewResolver 接口。
此時,如果官方提供的 ThymeleafViewResolver 的默認(rèn)設(shè) 置無法滿足我們的需求,可以通過 兩 種 途 徑 進(jìn) 行 自 定 義 設(shè) 置 : 通 過 application 配 置 文 件 配 置 和 自 行 創(chuàng) 建ThymeleafViewResolver 對象。
通過 application 配置對應(yīng)的屬性定義位于 ThymeleafProperties 類中,我們已經(jīng)做過多次類似的配置,不再贅述。
我們可以通過以下方式自行創(chuàng)建 ThymeleafViewResolver 對象。先定義一個配置類ViewResolverConfig,并在類內(nèi)部通過@Bean 注解對實(shí)例化的 ThymeleafViewResolver對象進(jìn)行注入容器的操作。
@Configuration
public class ViewResolverConfig {
@Bean
public ThymeleafViewResolver thymeleafViewResolver() {
Thyme leafViewResolver resolver = new ThymeleafViewResolver();
//設(shè)置 ViewResolver 對應(yīng)的屬性 值
resolver. setCharacterEncoding("UTF-8");
resolver. setCache(false);
return resolver;
}
}
@Bean 默 認(rèn) 會 將 方 法 thymeleafViewResolver 作 為 Bean 的 key, 將 返 回 的Thymeleaf-ViewResolver 對 象 作 為 Value 存 入 容 器 當(dāng) 中 。 在 方 法 內(nèi) 部 , 可 通 過ThymeleafViewResolver 對應(yīng)的方法進(jìn)行屬性的初始化設(shè)置。通過以上代碼我們便完成了自定義 Thymeleaf-ViewResolver 的注入。
那么,原來默認(rèn)的 ThymeleafViewResolver 會怎么處理呢? 我們知道幾乎所有的自動配置類都是通過注解設(shè)置初始化條件的,比如 ThymeleafViewResolver 默認(rèn)實(shí)例化的條件是當(dāng)容器中不存在名稱為 thymeleafViewResolver 時才會使用默認(rèn)的初始化。當(dāng)自定義的ThymeleafViewResolver 類完成初始化之后,默認(rèn)配置的初始化條件便不再滿足了。
上面針對 SpringMVC 中 Thymeleaf 的 ViewResolver 的自定義進(jìn)行了講解。
其實(shí)在 Spring Boot 中,大多數(shù)組件都可以采用同樣的方式對默認(rèn)配置進(jìn)行覆蓋。除了上述方法,在 Spring Boot 項(xiàng)目中還可以通過實(shí)現(xiàn) WebMvcConfigurer 接口來進(jìn)行更靈活地自定義配置。
通過 WebMvcConfigurer 接口實(shí)現(xiàn)自定義配置是 Spring 內(nèi)部的一-種配置方式,它替代了傳統(tǒng)的 XML 形式的配置。通過對該接口具體方法的實(shí)現(xiàn),可以自定義一些 Handler、Interceptor 、ViewResolver 、MessageConverter 等參 數(shù) 。 以 上 面 配 置ThymeleafViewResolver 為例,我們也可以通過實(shí)現(xiàn)該接口的 configureViewResolvers 方法來進(jìn)行配置,達(dá)到同樣的效果,具體示例代碼如下:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers (ViewResolverRegistry registry) {
Thyme leafViewResolver resolver = new ThymeleafViewResolver();
//設(shè)置 ViewResolver 對應(yīng)的屬 性值
resolver. setCharacterEncoding("UTF-8");
resolver . setCache(false);
registry . viewResolver(resolver);
}
}
使用 WebMvcConfigurer 接口時需注意 Spring Boot 版本,以上代碼是基于 Spring Boot 2.0以后的版本。WebMvcConfigurer 接口還提供 了其他關(guān)于擴(kuò)展 SpringMVC 配置的接口,使用方法與上述示例基本一樣,大家可以查閱對應(yīng)的代碼進(jìn)一步了解, 這里就不再逐一舉例了。
最后,關(guān)于 SpringMVC 自定義配置的最徹底操作就是完全接管 SpringBoot 關(guān)于 SpringMVC的默認(rèn)配置,具體操作就是在 WebMvcConfigurer 的實(shí)現(xiàn)類上使用@EnableWebMvc 注解,示例如下。
@EnableWebMvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
使用該注解等于擴(kuò)展了 WebMvcConfigurationSupport,但是沒有重寫任何方法,因此所需的功能都需要開發(fā)人員自行實(shí)現(xiàn)。-般情況下不推薦使用這種方式,該方式更適合基于 SpringBoot 提供的默認(rèn)配置,針對特別需求進(jìn)行有針對性拓展的場景。
其實(shí),本節(jié)內(nèi)容的重點(diǎn)并不只是讓大家學(xué)會簡單的 Web 自定義配置,更深的用意是希望大家了解在 Spring Boot 默認(rèn)自動配置的基礎(chǔ)上,我們可以通過什么方式以及如何進(jìn)行自定義的拓展。本節(jié)中提到但未列出實(shí)例的內(nèi)容,大家可以根據(jù)已經(jīng)學(xué)習(xí)到的思路相應(yīng)練習(xí)。
本章重點(diǎn)針對 Spring Boot 中 Web 應(yīng)用的自動配置和 Spring MVC 的自動配置展開,并以Spring MVC 中的一些典型配置為例進(jìn)行了源碼講解。
其 實(shí) 圍 繞 Web 應(yīng) 用 還 有 一 系 列 的 自 動 配 置 比 如HttpEncodingAutoConfigurationMultipartAutoConfiguration和HttpMessageConvertersAutoConfiguration 等。我們只需領(lǐng)悟自動配置的精髓:這些相關(guān)配置只不過是將之前通過 xml 來配置 Bean,轉(zhuǎn)換成了基于類的形式來配置而已。讀者可按照以上方法對其他 Web 相關(guān)的配置項(xiàng)進(jìn)行相應(yīng)的閱讀和分析。
雖然現(xiàn)在流行前后端分離,但是后端模版在一些關(guān)鍵地方還是非常有用的,例如郵件模版、代碼模版等。當(dāng)然也不排除一些古老的項(xiàng)目后端依然使用動態(tài)模版。
Thymeleaf 簡潔漂亮、容易理解,并且完美支持 HTML5,可以直接打開靜態(tài)頁面,同時不新增標(biāo)簽,只需增強(qiáng)屬性,這樣也降低了學(xué)習(xí)成本。
因此松哥今天花點(diǎn)時間和大家仔細(xì)分享一下 Thymeleaf。
Thymeleaf 是新一代 Java 模板引擎,它類似于 Velocity、FreeMarker 等傳統(tǒng) Java 模板引擎,但是與傳統(tǒng) Java 模板引擎不同的是,Thymeleaf 支持 HTML 原型。
它既可以讓前端工程師在瀏覽器中直接打開查看樣式,也可以讓后端工程師結(jié)合真實(shí)數(shù)據(jù)查看顯示效果,同時,SpringBoot 提供了 Thymeleaf 自動化配置解決方案,因此在 SpringBoot 中使用 Thymeleaf 非常方便。
事實(shí)上, Thymeleaf 除了展示基本的 HTML ,進(jìn)行頁面渲染之外,也可以作為一個 HTML 片段進(jìn)行渲染,例如我們在做郵件發(fā)送時,可以使用 Thymeleaf 作為郵件發(fā)送模板。
另外,由于 Thymeleaf 模板后綴為 .html,可以直接被瀏覽器打開,因此,預(yù)覽時非常方便。
Spring Boot 中整合 Thymeleaf 非常容易,只需要創(chuàng)建項(xiàng)目時添加 Thymeleaf 即可:
創(chuàng)建完成后,pom.xml 依賴如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
當(dāng)然,Thymeleaf 不僅僅能在 Spring Boot 中使用,也可以使用在其他地方,只不過 Spring Boot 針對 Thymeleaf 提供了一整套的自動化配置方案,這一套配置類的屬性在 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties 中,部分源碼如下:
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = DEFAULT_PREFIX;
private String suffix = DEFAULT_SUFFIX;
private String mode = "HTML";
private Charset encoding = DEFAULT_ENCODING;
private boolean cache = true;
//...
}
而我們剛剛提到的,Spring Boot 為 Thymeleaf 提供的自動化配置類,則是 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration ,部分源碼如下:
@Configuration
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration {
}
可以看到,在這個自動化配置類中,首先導(dǎo)入 ThymeleafProperties ,然后 @ConditionalOnClass 注解表示當(dāng)當(dāng)前系統(tǒng)中存在 TemplateMode 和 SpringTemplateEngine 類時,當(dāng)前的自動化配置類才會生效,即只要項(xiàng)目中引入了 Thymeleaf 相關(guān)的依賴,這個配置就會生效。
這些默認(rèn)的配置我們幾乎不需要做任何更改就可以直接使用了。如果開發(fā)者有特殊需求,則可以在 application.properties 中配置以 spring.thymeleaf 開頭的屬性即可。
接下來我們就可以創(chuàng)建 Controller 了,實(shí)際上引入 Thymeleaf 依賴之后,我們可以不做任何配置。新建的 IndexController 如下:
@Controller
public class IndexController {
@GetMapping("/index")
public String index(Model model) {
List<User> users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
User u = new User();
u.setId((long) i);
u.setName("javaboy:" + i);
u.setAddress("深圳:" + i);
users.add(u);
}
model.addAttribute("users", users);
return "index";
}
}
public class User {
private Long id;
private String name;
private String address;
//省略 getter/setter
}
在 IndexController 中返回邏輯視圖名+數(shù)據(jù),邏輯視圖名為 index ,意思我們需要在 resources/templates 目錄下提供一個名為 index.html 的 Thymeleaf 模板文件。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1">
<tr>
<td>編號</td>
<td>用戶名</td>
<td>地址</td>
</tr>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.address}"></td>
</tr>
</table>
</body>
</html>
在 Thymeleaf 中,通過 th:each 指令來遍歷一個集合,數(shù)據(jù)的展示通過 th:text 指令來實(shí)現(xiàn),
注意 index.html 最上面引入 thymeleaf 名稱空間(最新版并無強(qiáng)制要求)。
配置完成后,就可以啟動項(xiàng)目了,訪問 /index 接口,就能看到集合中的數(shù)據(jù)了:
前面我們說的是返回一個 Thymeleaf 模板,我們也可以手動渲染 Thymeleaf 模板,這個一般在郵件發(fā)送時候有用,例如我在 resources/templates 目錄下新建一個郵件模板,如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>hello 歡迎 <span th:text="${username}"></span>加入 XXX 集團(tuán),您的入職信息如下:</p>
<table border="1">
<tr>
<td>職位</td>
<td th:text="${position}"></td>
</tr>
<tr>
<td>薪水</td>
<td th:text="${salary}"></td>
</tr>
</table>
<img src="http://www.javaboy.org/images/sb/javaboy.jpg" alt="">
</body>
</html>
這一個 HTML 模板中,有幾個變量,我們要將這個 HTML 模板渲染成一個 String 字符串,再把這個字符串通過郵件發(fā)送出去,那么如何手動渲染呢?
@Autowired
TemplateEngine templateEngine;
@Test
public void test1() throws MessagingException {
Context context = new Context();
context.setVariable("username", "javaboy");
context.setVariable("position", "Java工程師");
context.setVariable("salary", 99999);
String mail = templateEngine.process("mail", context);
//省略郵件發(fā)送
}
前面兩個案例讓小伙伴們大致上理解了在 Spring Boot 中要如何使用 Thymeleaf,接下來,松哥將詳細(xì)介紹 Thymeleaf 本身的一些具體用法。
${...}
直接使用 th:xx = "${}" 獲取對象屬性。這個在前面的案例中已經(jīng)演示過了,不再贅述。
*{...}
可以像 ${...} 一樣使用,也可以通過 th:object 獲取對象,然后使用 th:xx = "*{}" 獲取對象屬性,這種簡寫風(fēng)格極為清爽,推薦大家在實(shí)際項(xiàng)目中使用。
<table border="1" th:object="${user}">
<tr>
<td>用戶名</td>
<td th:text="*{username}"></td>
</tr>
<tr>
<td>地址</td>
<td th:text="*{address}"></td>
</tr>
</table>
#{...}
通常的國際化屬性:#{...} 用于獲取國際化語言翻譯值。
在 resources 目錄下新建兩個文件:messages.properties 和 messages_zh_CN.properties,內(nèi)容如下:
messages.properties:
message = javaboy
messages_zh_CN.properties:
message = 江南一點(diǎn)雨
然后在 thymeleaf 中引用 message,系統(tǒng)會根據(jù)瀏覽器的語言環(huán)境顯示不同的值:
<div th:text="#{message}"></div>
@{...}
<script type="text/javascript" th:src="@{http://localhost:8080/hello.js}"></script>
等價于:
<script type="text/javascript" src="http://localhost:8080/hello.js"></script>
首先在 application.properties 中配置 Spring Boot 的上下文,以便于測試:
server.servlet.context-path=/myapp
引用路徑:
<script type="text/javascript" th:src="@{/hello.js}"></script>
等價于:
<script type="text/javascript" src="/myapp/hello.js"></script>
這個相對是指相對于服務(wù)器的 URL,例如如下引用:
<script type="text/javascript" th:src="@{~/hello.js}"></script>
等價于:
<script type="text/javascript" src="/hello.js"></script>
應(yīng)用程序的上下文 /myapp 將被忽略。
<script type="text/javascript" th:src="@{//localhost:8080/hello.js}"></script>
等價于:
<script type="text/javascript" src="//localhost:8080/hello.js"></script>
<script type="text/javascript" th:src="@{//localhost:8080/hello.js(name='javaboy',age=99)}"></script>
等價于:
<script type="text/javascript" th:src="//localhost:8080/hello.js?name=javaboy&age=99"></script>
~{...}
片段表達(dá)式是 Thymeleaf 的特色之一,細(xì)粒度可以達(dá)到標(biāo)簽級別,這是 JSP 無法做到的。片段表達(dá)式擁有三種語法:
舉個簡單例子。
在 resources/templates 目錄下新建 my_fragment.html 文件,內(nèi)容如下:
<div th:fragment="javaboy_link"><a href="http://www.javaboy.org">www.javaboy</a></div>
<div th:fragment="itboyhub_link"><a href="http://www.itboyhub.com">www.itboyhub.com</a></div>
這里有兩個 div,通過 th:fragment 來定義片段,兩個 div 分別具有不同的名字。
然后在另外一個頁面中引用該片段:
<table border="1" th:object="${user}" th:fragment="aaa">
<tr>
<td>用戶名</td>
<td th:text="*{username}"></td>
</tr>
<tr>
<td>地址</td>
<td th:text="*{address}"></td>
</tr>
</table>
<hr>
<div th:replace="my_fragment.html"></div>
<hr>
<div th:replace="~{my_fragment.html::javaboy_link}"></div>
<hr>
<div th:replace="~{::aaa}"></div>
通過 th:replace 來引用片段。第一個表示引用完整的 my_fragment.html 頁面;第二個表示引用 my_fragment.html 中的名為 javaboy_link 的片段;第三個表示引用當(dāng)前頁面名為 aaa 的片段,也就是上面那個 table。
這些是一些可以直接寫在表達(dá)式中的字符,主要有如下幾種:
案例:
<div th:text="'這是 文本字面量(有空格)'"></div>
<div th:text="javaboy"></div>
<div th:text="99"></div>
<div th:text="true"></div>
如果文本是英文,并且不包含空格、逗號等字符,可以不用加單引號。
文本可以使用 + 進(jìn)行拼接。
<div th:text="'hello '+'javaboy'"></div>
<div th:text="'hello '+${user.username}"></div>
如果字符串中包含變量,也可以使用另一種簡單的方式,叫做字面量置換,用 | 代替 '...' + '...',如下:
<div th:text="|hello ${user.username}|"></div>
<div th:text="'hello '+${user.username}+' '+|Go ${user.address}|"></div>
算術(shù)運(yùn)算有:+, -, *, / 和 %。
<div th:with="age=(99*99/99+99-1)">
<div th:text="${age}"></div>
</div>
th:with 定義了一個局部變量 age,在其所在的 div 中可以使用該局部變量。
案例:
<div th:with="age=(99*99/99+99-1)">
<div th:text="9 eq 9 or 8 ne 8"></div>
<div th:text="!(9 eq 9 or 8 ne 8)"></div>
<div th:text="not(9 eq 9 or 8 ne 8)"></div>
</div>
表達(dá)式里的值可以使用 >, <, >= 和 <= 符號比較。== 和 != 運(yùn)算符用于檢查相等(或者不相等)。注意 XML規(guī)定 < 和 > 標(biāo)簽不能用于屬性值,所以應(yīng)當(dāng)把它們轉(zhuǎn)義為 < 和 >。
如果不想轉(zhuǎn)義,也可以使用別名:gt (>);lt (<);ge (>=);le (<=);not (!)。還有 eq (==), neq/ne (!=)。
舉例:
<div th:with="age=(99*99/99+99-1)">
<div th:text="${age} eq 197"></div>
<div th:text="${age} ne 197"></div>
<div th:text="${age} ge 197"></div>
<div th:text="${age} gt 197"></div>
<div th:text="${age} le 197"></div>
<div th:text="${age} lt 197"></div>
</div>
類似于我們 Java 中的三目運(yùn)算符。
<div th:with="age=(99*99/99+99-1)">
<div th:text="(${age} ne 197)?'yes':'no'"></div>
</div>
其中,: 后面的部分可以省略,如果省略了,又同時計(jì)算結(jié)果為 false 時,將返回 null。
基本內(nèi)置對象:
在頁面可以訪問到上面這些內(nèi)置對象,舉個簡單例子:
<div th:text='${#session.getAttribute("name")}'></div>
實(shí)用內(nèi)置對象:
這是一些內(nèi)置對象以及工具方法,使用方式也都比較容易,如果使用的是 IntelliJ IDEA,都會自動提示對象中的方法,很方便。
舉例:
<div th:text="${#execInfo.getProcessedTemplateName()}"></div>
<div th:text="${#arrays.length(#request.getAttribute('names'))}"></div>
這個是給 HTML 元素設(shè)置屬性值。可以一次設(shè)置多個,多個之間用 , 分隔開。
例如:
<img th:attr="src=@{/1.png},title=${user.username},alt=${user.username}">
會被渲染成:
<img src="/myapp/1.png" title="javaboy" alt="javaboy">
當(dāng)然這種設(shè)置方法不太美觀,可讀性也不好。Thymeleaf 還支持在每一個原生的 HTML 屬性前加上 th: 前綴的方式來使用動態(tài)值,像下面這樣:
<img th:src="@{/1.png}" th:alt="${user.username}" th:title="${user.username}">
這種寫法看起來更清晰一些,渲染效果和前面一致。
上面案例中的 alt 和 title 則是兩個特殊的屬性,可以一次性設(shè)置,像下面這樣:
<img th:src="@{/1.png}" th:alt-title="${user.username}">
這個等價于前文的設(shè)置。
數(shù)組/集合/Map/Enumeration/Iterator 等的遍歷也算是一個非常常見的需求,Thymeleaf 中通過 th:each 來實(shí)現(xiàn)遍歷,像下面這樣:
<table border="1">
<tr th:each="u : ${users}">
<td th:text="${u.username}"></td>
<td th:text="${u.address}"></td>
</tr>
</table>
users 是要遍歷的集合/數(shù)組,u 則是集合中的單個元素。
遍歷的時候,我們可能需要獲取遍歷的狀態(tài),Thymeleaf 也對此提供了支持:
u 后面的 state 表示遍歷狀態(tài),通過遍歷狀態(tài)可以引用上面的屬性。
<table border="1">
<tr th:each="u,state : ${users}">
<td th:text="${u.username}"></td>
<td th:text="${u.address}"></td>
<td th:text="${state.index}"></td>
<td th:text="${state.count}"></td>
<td th:text="${state.size}"></td>
<td th:text="${state.current}"></td>
<td th:text="${state.even}"></td>
<td th:text="${state.odd}"></td>
<td th:text="${state.first}"></td>
<td th:text="${state.last}"></td>
</tr>
</table>
只顯示奇數(shù)次的遍歷,可以使用 th:if,如下:
<table border="1">
<tr th:each="u,state : ${users}" th:if="${state.odd}">
<td th:text="${u.username}"></td>
<td th:text="${u.address}"></td>
<td th:text="${state.index}"></td>
<td th:text="${state.count}"></td>
<td th:text="${state.size}"></td>
<td th:text="${state.current}"></td>
<td th:text="${state.even}"></td>
<td th:text="${state.odd}"></td>
<td th:text="${state.first}"></td>
<td th:text="${state.last}"></td>
</tr>
</table>
th:if 不僅僅只接受布爾值,也接受其他類型的值,例如如下值都會判定為 true:
但是如果值為 null,th:if 會求值為 false。
th:unless 的判定條件則與 th:if 完全相反。
<table border="1">
<tr th:each="u,state : ${users}" th:unless="${state.odd}">
<td th:text="${u.username}"></td>
<td th:text="${u.address}"></td>
<td th:text="${state.index}"></td>
<td th:text="${state.count}"></td>
<td th:text="${state.size}"></td>
<td th:text="${state.current}"></td>
<td th:text="${state.even}"></td>
<td th:text="${state.odd}"></td>
<td th:text="${state.first}"></td>
<td th:text="${state.last}"></td>
</tr>
</table>
這個顯示效果則與上面的完全相反。
當(dāng)可能性比較多的時候,也可以使用 switch:
<table border="1">
<tr th:each="u,state : ${users}">
<td th:text="${u.username}"></td>
<td th:text="${u.address}"></td>
<td th:text="${state.index}"></td>
<td th:text="${state.count}"></td>
<td th:text="${state.size}"></td>
<td th:text="${state.current}"></td>
<td th:text="${state.even}"></td>
<td th:text="${state.odd}"></td>
<td th:text="${state.first}"></td>
<td th:text="${state.last}"></td>
<td th:switch="${state.odd}">
<span th:case="true">odd</span>
<span th:case="*">even</span>
</td>
</tr>
</table>
th:case="*" 則表示默認(rèn)選項(xiàng)。
這個我們前面已經(jīng)涉及到了,使用 th:with 可以定義一個本地變量。
我們可以使用屬性將數(shù)據(jù)放入頁面模版中,但是很多時候,內(nèi)聯(lián)的方式看起來更加直觀一些,像下面這樣:
<div>hello [[${user.username}]]</div>
用內(nèi)聯(lián)的方式去做拼接也顯得更加自然。
[[...]] 對應(yīng)于 th:text (結(jié)果會是轉(zhuǎn)義的 HTML),[(...)]對應(yīng)于 th:utext,它不會執(zhí)行任何的 HTML 轉(zhuǎn)義。
像下面這樣:
<div th:with="str='hello <strong>javaboy</strong>'">
<div>[[${str}]]</div>
<div>[(${str})]</div>
</div>
最終的顯示效果如下:
不過內(nèi)聯(lián)方式有一個問題。我們使用 Thymeleaf 的一大優(yōu)勢在于不用動態(tài)渲染就可以直接在瀏覽器中看到顯示效果,當(dāng)我們使用屬性配置的時候確實(shí)是這樣,但是如果我們使用內(nèi)聯(lián)的方式,各種表達(dá)式就會直接展示在靜態(tài)網(wǎng)頁中。
也可以在 js 或者 css 中使用內(nèi)聯(lián),以 js 為例,使用方式如下:
<script th:inline="javascript">
var username=[[${user.username}]]
console.log(username)
</script>
js 中需要通過 th:inline="javascript" 開啟內(nèi)聯(lián)。
好啦,Thymeleaf 跟大家也介紹的差不多了,應(yīng)付日常的工作應(yīng)該是可以了。對 Thymeleaf 感興趣的小伙伴,也可以看看它的官方文檔: https://www.thymeleaf.org。
最后,松哥還搜集了 50+ 個項(xiàng)目需求文檔,想做個項(xiàng)目練練手的小伙伴不妨看看哦~
需求文檔地址:https://gitee.com/lenve/javadoc
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。