| | |
| | | <ul> |
| | | <li>JavaScript</li> |
| | | <li>Responsive Design</li> |
| | | <li>NodeJS</li> |
| | | <li>WebComponents / lit</li> |
| | | <li>NodeJS</li> |
| | | <li>Datenbank</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <h3>Betrieb</h3> |
| | | <ul> |
| | | <li>Server einrichten</li> |
| | | <li>SSL- / TLS-Zertifikate</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | |
| | | <li>Progressive Web Application</li> |
| | | <li>CEF / Electron</li> |
| | | <li>WebAssembly</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <h3>Betrieb</h3> |
| | | <ul> |
| | | <li>Server einrichten</li> |
| | | <li>SSL- / TLS-Zertifikate</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers="1-15|17"> |
| | | <code class="js" data-trim data-line-numbers="1-16|19"> |
| | | <script type="text/template"> |
| | | class HelloWorldComponent extends HTMLElement { |
| | | connectedCallback() { |
| | | constructor() { |
| | | super(); |
| | | |
| | | const shadowRoot = this.attachShadow({mode: "open"}); |
| | | |
| | | shadowRoot.innerHTML = ` |
| | |
| | | <iframe data-src="/assets/html/hello-world.html"></iframe> |
| | | </section> |
| | | <section> |
| | | <h4>Light DOM vs Shadow DOM</h4> |
| | | <img data-src="/assets/images/web-components-shadow-root.png" width="500px"> |
| | | </section> |
| | | <section> |
| | | <h4>Suche von Elementen im Light- und Shadow DOM</h4> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | // Light DOM |
| | | const span = document.querySelector("span"); // Hi! |
| | | const divs = document.querySelectorAll("div"); |
| | | |
| | | console.log(divs.length); // => 1 |
| | | |
| | | // Shadow DOM |
| | | const helloWorld = document.querySelector("hello-world"); |
| | | |
| | | helloWorld.shadowRoot.querySelector("div"); // Hello World! |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Auf Attribute reagieren</h3> |
| | | <pre> |
| | | <code class="html" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | <hello-world my-attribute="a"></hello-world> |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers="2|8-10"> |
| | | <script type="text/template"> |
| | | class HelloWorldComponent extends HTMLElement { |
| | | static observedAttributes = ["my-attribute"]; |
| | | |
| | | connectedCallback() { |
| | | constructor() { |
| | | // ... |
| | | } |
| | | |
| | |
| | | <p>Lasse die eigene Komponente "hello-sayer" auf eine Änderung des "name"-Attributs reagieren, indem der dort eingetragene Name in der Komponente angezeigt wird (Beispiel: name="Joe" => Hello Joe!)</p> |
| | | <p>(/examples/templates/web-components/hello-sayer.html)</p> |
| | | </section> |
| | | <section> |
| | | <h3>Musterlösung</h3> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | class HelloSayerComponent extends HTMLElement { |
| | | static observedAttributes = ["name"]; |
| | | |
| | | constructor() { |
| | | super(); |
| | | |
| | | this.root = this.attachShadow({mode: "open"}); |
| | | |
| | | this.root.innerHTML = ` |
| | | <style> |
| | | div { |
| | | background-color: coral; |
| | | color: white; |
| | | } |
| | | </style> |
| | | <div>Hello!</div> |
| | | `; |
| | | } |
| | | |
| | | attributeChangedCallback(name, oldValue, newValue) { |
| | | this.root.querySelector("div").innerHTML = `Hello ${newValue}!`; |
| | | } |
| | | } |
| | | |
| | | window.customElements.define("hello-sayer", HelloSayerComponent); |
| | | |
| | | const names = ["Joe", "Allie"]; |
| | | let counter = 0; |
| | | |
| | | setInterval(() => document.querySelector("hello-sayer").setAttribute("name", names[counter++ % 2]), 2000); |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <iframe data-src="/assets/html/hello-sayer.html"></iframe> |
| | | </section> |
| | | <section> |
| | | <h3>Elemente aus dem LightDOM platzieren mit <code><slot></code></h3> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | this.root.innerHTML = ` |
| | | <span>Hello <slot></slot>!</span> |
| | | `; |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h4>Beispiel</h4> |
| | | <pre> |
| | | <code class="html" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | <hello-sayer>Joe</hello-sayer> |
| | | <hello-sayer>Allie</hello-sayer> |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | <iframe data-src="/assets/html/hello-sayer-2.html"></iframe> |
| | | </section> |
| | | <section> |
| | | <h3>Mehrfache benannte <code><slot></code>s</h3> |
| | | <pre> |
| | | <code class=js data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | this.root.innerHTML = ` |
| | | <span> |
| | | first name: <slot name="first"></slot> | |
| | | last name: <slot name="last"></slot> |
| | | </span> |
| | | `; |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h4>Beispiel</h4> |
| | | <pre> |
| | | <code class="html" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | <name-field> |
| | | <span slot="first">Leia</span> |
| | | <span slot="last">Organa</span> |
| | | </name-field> |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | <iframe data-src="/assets/html/name-field.html"></iframe> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Entwerfe eine <code>CardComponent</code> (Vorlage "card-component.html") in folgendem Stil und nutze dabei sinnvolle <code><slot></code>s:</p> |
| | | <pre> |
| | | <code class="html" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | <card-component> |
| | | <span slot="title">This is a title</span> |
| | | <span slot="content">...Lorem ipsum dolor sit amet, consetetur sadipscing elitr...</span> |
| | | </card-component> |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | <iframe data-src="/assets/html/exercise-card-component.html"></iframe> |
| | | </section> |
| | | <section> |
| | | <h3>Styling einer Web Component</h3> |
| | | <p>Ein <code>:host</code>-Block mit einem <code>display</code> gehört zum guten Ton!</p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | :host { |
| | | display: block; /* inline, inline-block, flex, grid etc... */ |
| | | |
| | | color: red; |
| | | |
| | | font-family: Arial, Helvetica, sans-serif; |
| | | } |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Innerhalb einer Komponente können beliebige Styles für verschiedene Selektoren verwendet werden</p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | <script type="text/template"> |
| | | :host { |
| | | display: block; |
| | | } |
| | | |
| | | .info { |
| | | background-color: lightblue; |
| | | } |
| | | |
| | | .warning { |
| | | background-color: yellow; |
| | | } |
| | | </script> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Welche Wechselwirkungen haben Styles zueinander, die innerhalb und außerhalb einer Komponente definiert sind? (Vorlage "web-component-styles.html")</p> |
| | | </section> |
| | | <section> |
| | | <p>Einige globale Styles werden dennoch vererbt:</p> |
| | | <ul> |
| | | <li>Sämtliche Styles, die standardmäßig einen Wert von <code>inherit</code> besitzen (z.B. <code>color</code> oder <code>font-family</code>)</li> |
| | | <li>Eigene CSS-Variablen wie z.B. <code>--my-custom-color</code></li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Passe das vorherige Ergebnis (Vorlage "web-component-styles.html") so an, dass ein beliebiger Style per CSS-Variable von außen gesetzt werden kann (z.B. <code>color</code> oder <code>background-color</code>)</p> |
| | | <p>Erinnerung CSS-Variablen:</p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | /* Definition */ |
| | | html { |
| | | --my-variable: #abcdef; |
| | | } |
| | | |
| | | /* Usage */ |
| | | .my-class { |
| | | background-color: var(--my-variable, white); |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>In der Anfangszeit von Web Components wurde oft folgendes Muster zur Individualisierung verwendet:</p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | /* Global Scope */ |
| | | html { |
| | | --my-border-color: #abcdef; |
| | | --my-border-width: 1px; |
| | | --my-margin-top: 1rem; |
| | | --my-margin-bottom: 1rem; |
| | | } |
| | | |
| | | /* Inside Web Component */ |
| | | .container { |
| | | border-color: var(--my-border-color, white); |
| | | border-width: var(--my-border-width, 1px); |
| | | margin-top: var(--my-margin-top, 0.5rem); |
| | | margin-bottom: var(--my-margin-bottom, 0.5rem); |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Eleganteres Stylen von Web Components mittels <code>part</code></p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | /* Global Scope */ |
| | | my-component::part(inner) { |
| | | border-color: yellow; |
| | | border-width: 2px; |
| | | } |
| | | |
| | | /* Inside Web Component */ |
| | | <div part="inner"></div> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Passe die <code>CardComponent</code> aus einer vorherigen Übung an (Vorlage "card-component.html"), sodass man Titel und Inhalt einfach von außen per <code>part</code>s stylen kann.</p> |
| | | </section> |
| | | <section> |
| | | <p><code>part</code>s von eingebetteten Web Components exportieren und verfügbar machen</p> |
| | | <pre> |
| | | <code class="html" data-trim data-line-numbers> |
| | | <!-- Inner Component Html --> |
| | | <div part="inner">Inner Text</div> |
| | | |
| | | <!-- Outer Component Html --> |
| | | <inner-component exportparts="inner"></inner-component> |
| | | |
| | | <!-- Global Html --> |
| | | <outer-component></outer-component> |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Passe das Beispiel (Vorlage "exportparts.html") entsprechend an, sodass von außen sowohl der <code>Inner Text</code> als auch der <code>Outer Text</code> stylebar sind</p> |
| | | </section> |
| | | <section> |
| | | <h3>Stylen von LightDOM-Kindern</h3> |
| | | <p>Web Components können direkte Kinder, die via <code>slot</code> eingefügt werden, stylen. Hierfür gibt es den Pseudo-Elemente-Selektor <code>::slotted([selector])</code>, welcher Elemente selektiert, die in einem Slot stecken:</p> |
| | | <pre> |
| | | <code class="css" data-trim data-line-numbers> |
| | | /* Inside Web Component */ |
| | | ::slotted(*) { /* Selectors like elements, #id or .class */ |
| | | font-style: italic; |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Definiere mit dem Pseudo-Elemente-Selektor <code>::slotted()</code> Styles, sodass per <code>slot</code> eingefügte Elemente mit der Klasse <code>red</code> einen roten und Elemente mit der Klasse <code>blue</code> einen blauen Hintergrund haben. Die Schriftart soll jeweils weiß sein (Vorlage "slotted.html")</p> |
| | | </section> |
| | | <section> |
| | | <h3>Events in Web Components</h3> |
| | | <img data-src="/assets/images/web-components-event-bubbling-shadow-dom.png"> |
| | | <ul> |
| | | <li>Events können an einer Web Component selbst oder innerhalb des Shadow Dom <code>dispatcht</code> werden</li> |
| | | <li>Das Shadow Root der Komponente stellt eine Art "gläserne Decke" dar</li> |
| | | <li>Standard-Events der UI wie <code>click</code> bubbeln durch das Shadow Dom nach oben durch</li> |
| | | <li>Ein <code>CustomEvent</code> muss entsprechend konfiguriert werden</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | button.dispatchEvent(new CustomEvent( |
| | | "tabactivated", // Name des Events, frei wählbar |
| | | { |
| | | detail: "any data", // beliebige Daten wie Strings, Zahlen, Booleans usw. |
| | | bubbles: true, // bestimmt, ob das Event durch alle Eltern nach oben steigt |
| | | cancelable: true, // Abbrechbar per event.preventDefault() |
| | | composed: true // durch eventuelle Shadow Root steigen, falls vorhanden. |
| | | } |
| | | )); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Aufgabe</h3> |
| | | <p>Entwickle eine Tab-Komponente (Vorlage "tab-componenmt.html"), welche beim Click auf einen Registerreite / Tab ein Event feuert, welches die Außenwelt über das gerade aktivierte Tab (tab-id) informiert</p> |
| | | </section> |
| | | <section> |
| | | <h3>Lösung</h3> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | this.root.innerHTML = ` |
| | | <style> |
| | | ::slotted(div) { |
| | | display: none; |
| | | } |
| | | |
| | | ::slotted(div.active) { |
| | | display: block; |
| | | } |
| | | </style> |
| | | |
| | | <div><slot name="tab"></slot></div> |
| | | <div><slot name="content"></slot></div> |
| | | `; |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | connectedCallback() { |
| | | const firstContent = this.querySelector("div"); |
| | | |
| | | firstContent.classList.add("active"); |
| | | |
| | | this.addEventListener("click", (e) => { |
| | | if (e.target.tagName === "SPAN" && e.target.hasAttribute("tab-id")) { |
| | | const oldContent = this.querySelector("div.active"); |
| | | const newContent = this.querySelector(`[tab-id="${e.target.getAttribute("tab-id")}"]`); |
| | | |
| | | oldContent.classList.remove("active"); |
| | | newContent.classList.add("active"); |
| | | |
| | | this.dispatchEvent(new CustomEvent("tabclick", {composed: true, bubbles: true, detail: e.target.getAttribute("tab-id"), cancelable: true})); |
| | | } |
| | | }); |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | </section> |
| | | <section> |
| | | <section> |
| | | <h2>NodeJS</h2> |
| | | </section> |
| | | <section> |
| | | <h3>Was ist NodeJS?</h3> |
| | | <ul> |
| | | <li class="fragment">"JavaScript auf dem Server"</li> |
| | | <li class="fragment">basiert auf der V8-Engine von Google</li> |
| | | <li class="fragment">Laufzeitumgebung für JavaScript mit eigener API</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <img data-src="/assets/images/node-js.svg"> |
| | | </section> |
| | | <section> |
| | | <h3>Überblick über die Node-API</h3> |
| | | <p>Die API ist in viele verschiedene Module aufgeteilt</p> |
| | | <table> |
| | | <tr><td>fs</td><td>Dateisystem</td></tr> |
| | | <tr><td>crypto</td><td>Kryptografie</td></tr> |
| | | <tr><td>zlib</td><td>Datenkompression</td></tr> |
| | | <tr><td>http</td><td>Webserver</td></tr> |
| | | <tr><td>net</td><td>Netzwerk (TCP)</td></tr> |
| | | <tr><td>tls</td><td>Verschlüsseltes Netzwerk (TCP)</td></tr> |
| | | </table> |
| | | </section> |
| | | <section> |
| | | <p>Aufruf eines Programmes in der Kommandozeile</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | node [Node-Parameter] path/to/script.js [Anwendungsparameter] |
| | | </code> |
| | | </pre> |
| | | <p>z.B.</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | node --inspect index.js --port 3000 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Einbinden von Modulen</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const fs = require("fs"); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufbau eines Moduls</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | module.exports = { |
| | | foo: 5, |
| | | sayHello: function(name) { |
| | | return "Hello " + name + "!"; |
| | | } |
| | | } |
| | | </code> |
| | | </pre> |
| | | <p>oder</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | exports.foo = 5; |
| | | exports.sayHello = function(name) { |
| | | return "Hello " + name + "!"; |
| | | }; |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Schreibe ein Modul, welches eine <code>Square</code>-Klasse zur Verfügung stellt, mit welchem man den Umfang und den Flächeninhalt eines Rechtecks berechnen kann:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const Square = require("./square"); |
| | | |
| | | const square = new Square(2, 3); |
| | | |
| | | console.log(square.getArea()); // => 6 |
| | | console.log(square.getPerimeter()); // => 10 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Module für die Kommandozeile</h3> |
| | | <p>package.json</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm init -y |
| | | </code> |
| | | </pre> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | { |
| | | ... |
| | | "bin": { |
| | | "my-command": "./index.js" |
| | | } |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>index.js</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | #! /usr/bin/env node |
| | | |
| | | console.log("Hello world from the command line!"); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Installation</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm install -g path/to/project |
| | | </code> |
| | | </pre> |
| | | z.B. |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm install -g . |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Ausführung</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | my-command |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Kommandozeilenparameter</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | console.log(process.argv); |
| | | </code> |
| | | </pre> |
| | | <p>angelehnt an die Parameter aus der Programmiersprache <code>c</code>:</p> |
| | | <pre> |
| | | <code class="c" data-trim data-line-numbers> |
| | | int main(int argc, char *argv[]) |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Mache das vorherige Programm für die Konsole aufrufbar, sodass die Länge und Breite für das Rechteck per Konsolenargumente übergeben werden können und der Flächeninhalt ausgegeben wird:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | my-command 2 3 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Buffer</h3> |
| | | <p>Array-artige Struktur für rohe Bytes</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const b1 = Buffer.from("abc"); // bevorzugt |
| | | const b2 = Buffer.alloc(100, 0); // Länge mit initialen Daten |
| | | const b3 = Buffer.allocUnsafe(100); // Länge ohne initiale Daten |
| | | const b4 = new Buffer("abc"); // offiziell nicht empfohlen |
| | | |
| | | console.log(b1); // => <Buffer 61 62 63> |
| | | console.log(b1.toString("hex")); // => "616263" |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Mit Buffern arbeiten:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const a = Buffer.from("a") + Buffer.from("b"); |
| | | const b = Buffer.concat([Buffer.from("a"), Buffer.from([98])]); |
| | | const c = b.toString() |
| | | |
| | | console.log(a); // => ab |
| | | console.log(b); // => <Buffer 61 62> |
| | | console.log(c); // => ab |
| | | |
| | | for (const byte of a) { |
| | | console.log(byte); // => 97 98 |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Implementiere eine XOR-Verschlüsselung (<code>^</code>-Operator), sodass jedes Byte einer Original-Nachricht der Reihe nach mit dem entsprechenden Byte eines Schlüsselwortes verschlüsselt wird und gebe die verschlüsselte Nachricht als Text aus.</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Implementiere zur vorherigen Verschlüsselung eine Entschlüsselung.</p> |
| | | </section> |
| | | <section> |
| | | <p>Lösung</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const word = Buffer.from("abcdef"); |
| | | const key = Buffer.from("xyz"); |
| | | const encrypted = Buffer.alloc(word.length, 0); |
| | | |
| | | // encrypt |
| | | for (let i = 0; i < word.length; i++) { |
| | | encrypted[i] = word[i] ^ key[i % key.length]; |
| | | } |
| | | |
| | | // decrypt |
| | | for (let i = 0; i < word.length; i++) { |
| | | word[i] = encrypted[i] ^ key[i % key.length]; |
| | | } |
| | | |
| | | console.log(word.toString()); // => "abcdef" |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Streams</h3> |
| | | <p>Konstanter Fluss von (meist) großen Datenmengen in kleinen Paketen</p> |
| | | </section> |
| | | <section> |
| | | <p>Gängige Typen von Streams</p> |
| | | <ul> |
| | | <li>Standard In / Out</li> |
| | | <li>HTTP-Requests / -Responses</li> |
| | | <li>TCP-Sockets</li> |
| | | <li>Lese- und Schreib-Streams auf Dateien</li> |
| | | <li>Große Datenbank-Ergebnisse mittels Cursor</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const readableStream = fs.createReadStream("./source.txt"); |
| | | const writableStream = fs.createWriteStream("./target.txt"); |
| | | |
| | | readableStream.on("data", (data /* Buffer */) => { |
| | | // process data here |
| | | writableStream.write(data) |
| | | }); |
| | | |
| | | readableStream.on("end", (data) => { |
| | | // stream has finished, e.g. EOF (end of file) |
| | | }); |
| | | |
| | | // alternatively pipe content |
| | | readableStream.pipe(writableStream); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Lese eine beliebige Datei als Readable Stream, verschlüssele den Inhalt mit der XOR-Methode und schreibe die verschlüsselten Bytes in eine Zieldatei</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Ändere das Programm, sodass auf Konsolenebene ein Pipe (|) ermöglicht wird:</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | echo "Hello World!" | node index.js |
| | | </code> |
| | | </pre> |
| | | <p>Nutze dafür die implizit vorhandenen Streams von Standard In / Out:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | process.stdin; // Readable Stream |
| | | process.stdout; // Writable Stream |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Kryptographie</h3> |
| | | </section> |
| | | <section> |
| | | Mithilfe des nativen <code>crypto</code>-Moduls ist es möglich, moderne sichere kryptografische Verfahren zu verwenden: |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const crypto = require("crypto"); |
| | | </code> |
| | | </pre> |
| | | Hierzu nutzt NodeJS selber die weit verbreitete OpenSSL-Bibliothek |
| | | </section> |
| | | <section> |
| | | Einen MD5-Hash erzeugen: |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const crypto = require("crypto"); |
| | | |
| | | const hash = crypto.createHash("md5"); |
| | | const md5 = hash.update("abcdefg") |
| | | .digest(); // Buffer (default) |
| | | // .digest("hex") // Hexadezimal |
| | | // .digest("utf8") // Bytes forciert als Chars |
| | | |
| | | console.log(md5); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Erzeuge jeweils den md5- und den sha256-Hash als Hex-Wert der beiden Texte in der Datei <code>text-examples.txt</code> und vergleiche jeweils ihren md5- und sha256-Hash miteinander</p> |
| | | </section> |
| | | <section> |
| | | <h3>Zippen</h3> |
| | | </section> |
| | | <section> |
| | | Mit dem nativen Modul <code>zlib</code> ist es möglich, Kompression in seinen Anwendungen zu nutzen. |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const zlib = require("zlib"); |
| | | |
| | | // Komprimieren |
| | | zlib.deflate("abc" /* buffer | string */, (err, compressed) => {}); |
| | | |
| | | // Dekomprimieren |
| | | zlib.inflate(compressed /* buffer */, (err, decompressed) => {}); |
| | | |
| | | // Als Stream |
| | | const zipper = zlib.createDeflate(); |
| | | const unzipper = zlib.createInflate(); |
| | | |
| | | source.pipe(zipper).pipe(target); |
| | | source.pipe(unzipper).pipe(target); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Nutze die einfachen Varianten <code>zlib.deflate</code> und <code>zlib.inflate</code>, um einen beliebigen Text zu komprimieren und anschließend wieder zu dekomprimieren</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Nutze die Stream-Varianten <code>zlib.createDeflate</code> und <code>zlib.createInflate</code>, um jeweils ein getrenntes Programm für die Kompression und Dekompression mit <code>process.stdin</code> und <code>process.stdout</code> zu machen, sodass in der Konsole folgende Nutzung möglich ist:</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | echo "abc" | node compress.js | node decompress.js |
| | | cat file.txt | node compress.js > file.zipped |
| | | cat file.zipped | node decompress.js > file.txt |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>TCP-Verbindungen</h3> |
| | | </section> |
| | | <section> |
| | | <ul> |
| | | <li>Bidirektionale Verbindung</li> |
| | | <li>Duplexfähig</li> |
| | | <li>Besteht immer aus Server- und Client-Socket</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | Mit dem nativen Modul <code>net</code> ist es möglich, eine TCP-Verbindung aufzubauen |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | /* Server */ |
| | | const net = require("net"); |
| | | |
| | | const server = net.createServer((socket) => { |
| | | |
| | | // new incoming connection |
| | | socket.on("data", (data) => { |
| | | // data is a buffer |
| | | }); |
| | | |
| | | socket.write("Hello World!"); |
| | | }); |
| | | |
| | | server.listen(3000); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | /* Client */ |
| | | const net = require("net"); |
| | | |
| | | const socket = net.connect(3000, () => { |
| | | // 'connect' listener |
| | | }); |
| | | |
| | | socket.on("data", (data) => { |
| | | // data is a buffer |
| | | }); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Implementiere die Clientlogik in der <code>tcp-client.js</code>, um dem Server ein <code>"Hello Server!"</code> zurück zu senden</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Sende eine festgelegte Datei vom Server zum Client. Der Server liest die Datei ein und sendet die Inhalte zum Client, welcher diese wiederum in eine Zieldatei schreibt. Eventuell darf auch noch eine Kompression mittels Zip / Deflate implementiert werden.</p> |
| | | </section> |
| | | </section> |
| | | </div> |
| | | </div> |