@seanox/aspect-js
Version:
full stack JavaScript framework for SPAs incl. reactivity rendering, mvc / mvvm, models, expression language, datasource, virtual paths, unit test and some more
337 lines (263 loc) • 10.5 kB
Markdown
[SiteMap](sitemap.md) | [Inhalt](README.md#komponenten) | [Reaktives Rendering](reactive.md)
- - -
# Komponenten
Seanox aspect-js ist auf eine modulare und komponentenbasierte Architektur
ausgerichtet. Das Framework unterstützt dazu eine deklarative Kennzeichnung
von Komponenten im Markup sowie automatische Mechanismen für das
View-Model-Binding und das Laden von ausgelagerten Ressourcen zur Laufzeit.
## Inhalt
* [Modul](#modul)
* [Komponente](#komponente)
* [Composite](#composite)
* [Aufbau](#aufbau)
* [Ressourcen](#ressourcen)
* [Laden](#laden)
* [CSS](#css)
* [JavaScript](#javascript)
* [HTML](#html)
* [Common Standard-Komponente](#common-standard-komponente)
* [Namespace](#namespace)
* [Ergänzung](#erg%C3%A4nzung)
## Modul
Ein Modul stellt eine geschlossene funktionale Programmeinheit dar, die meist
als Programmbibliothek bereitgestellt wird.
## Komponente
Eine Komponente bezeichnet einen funktional oder technisch eigenständigen
Bestandteil, der aus einem oder mehreren Modulen bestehen kann.
## Composite
Ein Composite bezeichnet eine funktional eigenständige Komponente die sich
aus Markup, CSS und JavaScript sowie optional aus weiteren Ressourcen
zusammensetzt. Im Hinblick auf den Model-View-Controller-Ansatz stellt ein
Composite Komponenten für Model und View bereit.
__Damit unterscheidet sich das Composite-Konzept vom JavaScript-Modul-Konzept,
dsa aber ebenfalls verwendet werden kann. Ein anderer wichtiger Unterschied
besteht beim Laden und Ausführen von JavaScript, was bei Composites in
einem Renderzyklus linear/sequenziell und somit synchron erfolgt.__
## Aufbau
Eine Komponente besteht im Markup aus einem als Composite gekennzeichneten
HTML-Element mit einer eindeutigen ID, die als Composite-ID bezeichnet wird.
```html
<html>
<head>
<script src="aspect-js.js"></script>
</head>
<body>
<div id="example" composite></div>
</bod>
</html>
```
Im JavaScript wird die ID vom HTML-Element, das als Composite deklariert wurde,
als Name für das korrespondierende Model verwendet.
```javascript
const example = {
...
}
```
Und auch im CSS wird die ID vom HTML-Element des Composites als Zuordnung
verwendet.
```css
#example {
...
}
```
## Ressourcen
Das innere Markup, CSS und JavaScript von Composites lassen sich auslagern. Das
Standard-Verzeichnis `./modules` kann über die Eigenschaft
`Composite.MODULES` geändert werden. Die Dateinamen der ausgelagerten
Ressourcen leiten sich von der ID des als Composite gekennzeichneten
HTML-Elements ab. Welche Ressourcen bzw. Teile der Komponente ausgelagert
werden, kann für jede Komponente individuell entschieden werden.
```
+ modules
- example.css
- example.js
- example.html
- index.html
```
Beim Standardverhalten wird der Name der Ressourcen von der Composite-ID
abgeleitet und nutzt diese dann als Kleinschreibung am Anfang. Dieses Verhalten
kann mit dem Attribute [strict](markup.md#strict) geändert werden, so dass
die Composite-ID unverändert für die Ressourcen verwendet werden.
## Laden
Das Laden der Ressourcen und das View-Model-Binding erfolgen partiell, wenn das
Composite in der View benötigt wird, was die Ladezeit stark minimiert, da
situationsabhängig nur Ressourcen geladen werden, welche im Moment der
Darstellung verwendet werden.
Das Auslagern und Laden der Ressourcen zur Laufzeit ist optional und lässt
sich komplett, teilweise und nicht anwenden. Beim Nachladen und Einbinden gibt
es eine feste Reihenfolge: CSS, JavaScript, HTML/Markup.
Wird die Anfrage einer Ressource mit Status 404 beantwortet, wird angenommen,
dass diese Ressource nicht ausgelagert wurde. Andere Status als 200 oder 404
führen zu einem Fehler.
Das Laden von Ressourcen wird nur einmalig mit der ersten Anforderung der
Komponente für die View ausgeführt und der Inhalt dann
zwischengespeichert.
### CSS
CSS wird als Style-Element in das HEAD-Element eingefügt. Ohne ein
HEAD-Element verursacht das Einfügen einen Fehler.
### JavaScript
JavaScript wird nicht als Element eingefügt, sondern direkt mit der
eval-Methode in einem immer wieder frischen isolierten Gültigkeitsbereich
ausgeführt. Da die dort erstellten Konstanten, Variablen und Methoden nicht
global verfügbar sind, müssen diese z.b. mit dem Makro [#export](
scripting.md#export) publiziert werden.
```javascript
const login = {
validate(element, value) {
},
logon: {
onClick(event) {
}
}
};
#export login;
```
### HTML
HTML/Markup wird für ein Composite nur geladen, wenn das HTML-Element der
Komponente selbst kein inneres HTML besitzt und für das Element nicht die
Attribute `import` oder `output` deklariert wurden. Nur dann wird von einer
leeren Komponente mit ausgelagertem HTML/Markup ausgegangen.
## Common Standard-Komponente
Mit dem Laden der Seite und der Initialisierung des Frameworks und der Anwendung
wird automatisch auch die Commons-Komponente im Modul-Verzeichnis geladen, die
aus der JavaScript-Datei `common.js` und/oder dem CSS-Stylesheet `common.css`
bestehen kann. Beide Dateien sind zur Ablage initialer und anwendungsweiter
Logik bzw. Styles gedacht.
```
+ modules
- common.css
- common.js
- ...
- index.html
```
## Namespace
Vergleichbar mit Packages in anderen Programmiersprachen, lassen sich Namespaces
(Namensräume) zur hierarchischen Strukturierung von Komponenten, Ressourcen
und Geschäftslogik nutzen.
Auch wenn Packages kein Merkmal von JavaScript sind, lassen sich diese auf
Objektebene durch das Verketten von Objekten zu einem Objektbaum abbilden. Dabei
bildet jede Ebene des Objektbaums einen Namespace, was auch als Domain betrachtet
werden kann.
Wie für die Bezeichner von Objekten typisch, verwenden auch Namespaces
Buchstaben, Zahlen und Unterstriche, die durch einen Punkt getrennt werden. Als
Besonderheit werden auch Arrays unterstützt. Nutzt eine Ebene im Namespace
eine Ganzzahl, wird diese Ebene als Array interpretiert.
Composites bzw. Datenobjekte (Models) sind vergleichbar mit managed Beans, die
statisch als Singletons/Facades/Delegates den globalen Namespace nutzen. Um
diese Datenobjekte zu strukturieren und Domain-Konzepte umzusetzen, sind
entsprechende Namespaces erforderlich.
__Für Module wird auch hier die Verwendung vom Makro [#export](
scripting.md#export) empfhohlen, da dieses neben dem zu publizierenden
JavaScript-Element auch ein Namespace als Ziel enthalten kann.__
```javascript
const masterdata = {
regions: {
...
},
languages: {
...
}
};
#use example.administration;
#export masterdata@example.administration;
```
Das Beispiel erstellt mit [#use](scripting.md#use), wenn noch nicht existent,
den Namensraum `example.administration` und exportiert dann mit [#export](
scripting.md#export) `masterdata` nach `example.administration.masterdata`.
Im Markup bilden sich Namespaces aus den IDs verschachtelter Composites, wenn
diese das Attribut `namespace` verwenden.
```html
<div id="example" composite namespace>
<div id="administration" composite namespace>
<div id="masterdata" composite namespace>
<div id="regions" composite namespace>
Namespace: masterdata.regions
</div>
</div>
</div>
</div>
```
Befinden sich weitere Elemente mit einer ID zwischen den Composites, haben diese
keine Auswirkungen.
```html
<div id="example" composite namespace>
<div id="administration" composite namespace>
<div id="masterdata" composite namespace>
<section id="section">
<form id="form">
<div id="regions" composite namespace>
Namespace: masterdata.regions
Elements section and form are ignored
</div>
</form>
</section>
</div>
</div>
</div>
```
Noch spezieller sind in einer Namespace-Kette Composites ohne das Attribut
`namespace`. Diese Composites wirken entkoppelnd und haben selbst keinen
Namespace. Innerhalb dieser Composites können dann neue Namespaces begonnen
werden, unabhängig von übergeordneten Namespaces.
```html
<div id="Imprint" composite namespace>
Namespace: Imprint
<div id="Contact" composite>
Namespace: Contact
<div id="Support" composite namespace>
Namespace: Support
<div id="Mail" composite namespace>
Namespace: Support.Mail
</div>
<div id="Channel" composite namespace>
Namespace: Support.Channel
</div>
...
</div>
<div id="Community" composite namespace>
Namespace: Community
<div id="Channel" composite namespace>
Namespace: Community.Channel
</div>
...
</div>
</div>
</div>
```
Eingeführt wurde dieses Verhalten mit dem Gedanken an Micro-Frontends,
welche eigene Domains nutzen und an verschiedenen Stellen wiederverwendet werden
sollen. So lassen sich in der statischen Welt von Seanox aspect-js
Domain-bezogene Komponenten umsetzen.
Namespaces haben zudem Auswirkungen auf Ressourcen und Module. So haben
Namespaces im Markup erstmal nur textuellen Charakter und können auch ohne
ein korrespondierendes JavaScript-Objekt existieren und verwendet werden. Im
Markup wird lediglich die Syntax der Namespaces geprüft. Ist diese
gültig, werden die Namespaces direkt auf den Pfad von Modulen und deren
Ressourcen angewendet und erweitern den Pfad ab dem Modul-Verzeichnis.
```
+ modules
- common.css
- common.js
+ community
- channel.css
- channel.html
- channel.js
- ...
- imprint.css
- imprint.html
- imprint.js
+ support
- mail.css
- mail.html
- mail.js
- ...
- ...
- index.html
```
## Ergänzung
Wegen der engen Verkn&auuml;pfung sei an dieser Stelle bewusst auf die
weiterführenden Abschnitte [Model-View-Controler](mvc.md) und
[View-Model-Binding](mvc.md#view-model-binding) verwiesen.
- - -
[SiteMap](sitemap.md) | [Inhalt](README.md#komponenten) | [Reaktives Rendering](reactive.md)