Das Verständnis der gemeinsamen Schritte im Projekt-Setup ist entscheidend, bevor man sich mit den Besonderheiten der einzelnen Client-erweiternden Technologien befasst. Meine Anforderungen aus dem letzten Beitrag waren ziemlich unkompliziert:
- Ich werde die Sicht eines Backend-Entwicklers einnehmen
- Kein Front-End-Build-Schritt: kein TypeScript, keine Minimierung usw.
- Alle Abhängigkeiten werden von der Backend-App verwaltet, d. h . Maven
Es ist wichtig zu beachten, dass die Technologie, die ich im Detail beschreiben werde, mit Ausnahme von Vaadin, einem ähnlichen Ansatz folgt. Vaadin sticht mit seinem einzigartigen Paradigma unter den Ansätzen wirklich hervor.
WebJars ist eine Technologie , die 2012 von James Ward entwickelt wurde, um genau diese Anforderungen zu erfüllen.
WebJars sind clientseitige Webbibliotheken (z. B. jQuery und Bootstrap), die in JAR-Dateien (Java Archive) gepackt sind.
- Explizite und einfache Verwaltung der clientseitigen Abhängigkeiten in JVM-basierten Webanwendungen
- Verwenden Sie JVM-basierte Build-Tools (z. B. Maven, Gradle, sbt, ...), um Ihre clientseitigen Abhängigkeiten herunterzuladen
- Wissen, welche clientseitigen Abhängigkeiten Sie verwenden
- Transitive Abhängigkeiten werden automatisch aufgelöst und optional über RequireJS geladen
- Auf Maven Central bereitgestellt
- Öffentliches CDN, großzügig bereitgestellt von JSDelivr
Ein WebJar ist ein normales JAR, das Web-Assets enthält. Das Hinzufügen eines WebJar zu den Abhängigkeiten eines Projekts ist nichts Besonderes:
<dependencies> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>alpinejs</artifactId> <version>3.14.1</version> </dependency> </dependencies>
Die Aufgabe des Frameworks besteht darin, die Assets unter einer URL verfügbar zu machen. Spring Boot macht dies beispielsweise in der Klasse WebMvcAutoConfiguration
:
public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(), //1 "classpath:/META-INF/resources/webjars/"); addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION); registration.addResourceLocations(resource); } }); }
"/webjars/**"
Innerhalb des JAR können Sie Assets über ihren jeweiligen Pfad und Namen erreichen. Die vereinbarte Struktur besteht darin, die Assets in resources/webjars/<library>/<version>
zu speichern. Hier ist die Struktur des alpinejs-3.14.1.jar
:
META-INF |_ MANIFEST.MF |_ maven.org.webjars.npm.alpinejs |_ resources.webjars.alpinejs.3.14.1 |_ builds |_ dist |_ cdn.js |_ cdn.min.js |_ src |_ package.json
Innerhalb von Spring Boot können Sie mit /webjars/alpinejs/3.14.1/dist/cdn.js
auf die nicht minimierte Version zugreifen.
Entwickler veröffentlichen recht häufig clientseitige Bibliotheken. Wenn Sie eine Abhängigkeitsversion im POM ändern, müssen Sie den Front-End-Pfad ändern, möglicherweise an mehreren Stellen. Das ist langweilig, hat keinen Mehrwert und Sie riskieren, eine Änderung zu übersehen.
Das WebJars Locator-Projekt zielt darauf ab, all diese Probleme zu vermeiden, indem es einen Pfad ohne Version bereitstellt, z. B. /webjars/alpinejs/dist/cdn.js
. Sie können dies erreichen, indem Sie das webjars-locator
-JAR zu Ihren Abhängigkeiten hinzufügen:
<dependencies> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>alpinejs</artifactId> <version>3.14.1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <version>0.52</version> </dependency> </dependencies>
Ich werde diesen Ansatz für jede Front-End-Technologie verwenden. Ich werde auch die Bootstrap -CSS-Bibliothek hinzufügen, um eine besser aussehende Benutzeroberfläche bereitzustellen.
Thymeleaf ist eine serverseitige Rendering-Technologie.
Thymeleaf ist eine moderne serverseitige Java-Template-Engine für Web- und Standalone-Umgebungen.
Das Hauptziel von Thymeleaf besteht darin, Ihren Entwicklungsworkflow mit eleganten, natürlichen Vorlagen zu ergänzen – HTML, das in Browsern korrekt angezeigt werden kann und auch als statische Prototypen funktioniert, was eine stärkere Zusammenarbeit in Entwicklungsteams ermöglicht.
Mit Modulen für das Spring Framework, zahlreichen Integrationen mit Ihren bevorzugten Tools und der Möglichkeit, Ihre eigenen Funktionen einzubinden, ist Thymeleaf ideal für die moderne HTML5-JVM-Webentwicklung – es kann aber noch viel mehr.
-- Thymianblatt
Ich war noch Berater, als ich zum ersten Mal von Thymeleaf hörte. Damals waren Java Server Pages am Ende ihrer Lebensdauer angelangt. Java Server Faces versuchte, sie zu ersetzen; meiner Meinung nach ist ihnen das nicht gelungen.
Ich fand, dass Thymeleaf ein fantastischer Ansatz ist: Es ermöglicht Ihnen, die Ergebnisse zur Entwurfszeit in einer statischen Umgebung und zur Entwicklungszeit in einer Serverumgebung anzuzeigen. Und noch besser: Sie können mithilfe derselben Datei nahtlos zwischen beiden Umgebungen wechseln. Ich habe diese Funktion noch nie im Einsatz gesehen.
Spring Boot unterstützt jedoch Thymeleaf vollständig. Das Sahnehäubchen: Letzteres ist über einen HTML-Namespace auf der Seite verfügbar. Wenn Sie nicht auf JSF setzen (Spoiler: ich nicht), ist Thymeleaf heute die bevorzugte SSR-Vorlagensprache.
Hier ist das Demobeispiel von der Website:
<table> <thead> <tr> <th th:text="#{msgs.headers.name}">Name</th> <th th:text="#{msgs.headers.price}">Price</th> </tr> </thead> <tbody> <tr th:each="prod: ${allProducts}"> <td th:text="${prod.name}">Oranges</td> <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td> </tr> </tbody> </table>
Hier ist eine Einführung in Thymeleaf, falls Sie sich mit der Technologie vertraut machen möchten.
Name
und Price
. Wenn Sie es auf dem Server verwenden, greift Thymeleaf ein und rendert den aus th:text
, #{msgs.headers.name}
und #{msgs.headers.price}
berechneten Wert.$
-Operator fragt nach einer Spring-Bean mit demselben Namen, die an das Modell übergeben wurde. ${prod.name}
ist gleichbedeutend mit model.getBean("prod").getName()"
.#
ruft eine Funktion auf.th:each
ermöglicht Schleifen.Die meisten, wenn nicht alle Front-End-Frameworks arbeiten mit einem clientseitigen Modell. Wir müssen eine Brücke zwischen dem serverseitigen und dem clientseitigen Modell schlagen.
Der serverseitige Code, den ich verwende, ist der folgende:
data class Todo(val id: Int, var label: String, var completed: Boolean = false) //1 fun config() = beans { bean { mutableListOf( //2 Todo(1, "Go to the groceries", false), Todo(2, "Walk the dog", false), Todo(3, "Take out the trash", false) ) } bean { router { GET("/") { ok().render( //3 "index", //4 mapOf("title" to "My Title", "todos" to ref<List<Todo>>()) //5 ) } } } }
Definieren Sie die Todo
Klasse.
Fügen Sie der Bean Factory eine In-Memory-Liste hinzu. In einer normalen App würden Sie ein Repository
verwenden, um aus der Datenbank zu lesen.
Rendern Sie eine HTML-Vorlage.
Die Vorlage ist src/main/resources/templates/index.html
mit Thymeleaf-Attributen.
Fügen Sie das Modell in den Kontext der Seite ein.
Thymeleaf bietet ein th:inline="javascript"
-Attribut für das <script>
-Tag. Es rendert die serverseitigen Daten als JavaScript-Variablen. Die Dokumentation erklärt es viel besser, als ich es je könnte:
Das erste, was wir mit der Inline-Erstellung von Skripten tun können, ist, den Wert von Ausdrücken in unsere Skripte zu schreiben, wie:
<script th:inline="javascript"> /*<![CDATA[*/ ... var username = /*[[${session.user.name}]]*/ 'Sebastian'; ... /*]]>*/ </script>
Die Syntax
/*[[...]]*/
weist Thymeleaf an, den enthaltenen Ausdruck auszuwerten. Aber es gibt noch weitere Implikationen:
Da es sich bei unserem Ausdruck um einen Javascript-Kommentar
(/*...*/)
handelt, wird er bei der statischen Anzeige der Seite in einem Browser ignoriert.Der Code nach dem Inline-Ausdruck (
'Sebastian'
) wird bei der statischen Anzeige der Seite ausgeführt.Thymeleaf führt den Ausdruck aus und fügt das Ergebnis ein, entfernt aber auch den gesamten Code in der Zeile nach dem Inline-Ausdruck selbst (der Teil, der bei statischer Anzeige ausgeführt wird).
Wenn wir das oben Gesagte auf unseren Code anwenden, können wir die von Spring übergebenen Modellattribute wie folgt erhalten:
<script th:inline="javascript"> /*<![CDATA[*/ window.title = /*[[${title}]]*/ 'A Title' window.todos = /*[[${todos}]]*/ [{ 'id': 1, 'label': 'Take out the trash', 'completed': false }] /*]]>*/ </script>
Beim serverseitigen Rendern lautet das Ergebnis:
<script> /*<![CDATA[*/ window.title = "My title"; window.todos: [{"id":1,"label":"Go to the groceries","completed":false},{"id":2,"label":"Walk the dog","completed":false},{"id":3,"label":"Take out the trash","completed":false}] /*]]>*/ </script>
In diesem Beitrag habe ich zwei Komponenten beschrieben, die ich im weiteren Verlauf dieser Serie verwenden werde:
Den vollständigen Quellcode für diesen Beitrag finden Sie auf GitHub.
Weiterführende Informationen:
Ursprünglich veröffentlicht bei A Java Geek am 15. September 2024