Version | Erscheinung | Elemente ca. |
---|---|---|
HTML 1.0 | 1993 | 20 |
HTML 2.0 | 1995 | 50 |
HTML 3.2 | 1997 | 70 |
HTML 4.01 | 1999 | 90 |
HTML 5.0 | 2014 | 110 |
selector [, selector, selector, ...] {
property: value;
}
html {
background-color: red;
font-size: 30px;
}
#my-id /* ID */
html, body /* Mehrfachselektion */
div > p /* p die direkt unterhalb eines div sind */
div p /* p die irgendwo unterhalb eines div sind */
a:visited /* besuchte Links */
div:hover /* divs über die man mit der Maus fährt */
span.important /* span mit der Klasse "important" */
Es gibt zwei grundlegende Blocktypen:
block
inline
Beansprucht eine ganze Zeile und verursacht Zeilenumbrüche
display: block;
Kann Breite und Höhe haben
display: block;
width: 20px;
height: 20px;
Wie einfacher Text, der mit anderen in der gleichen Zeile stehen kann
display: inline;
Festlegen von Breite und Höhe sind wirkungslos
display: inline;
width: 20px; /* kein Effekt */
height: 20px; /* kein Effekt */
Mischform von inline
und block
display: inline-block;
width: 20px;
height: 20px;
Regeln können andere Regeln überschreiben
#id:hover { display: none }
#id { display: block }
.class.clazz { display: inline }
div { display: inline-block }
Es gibt zwei Varianten des Modells:
content-box
(default)
border-box
box-sizing: content-box;
box-sizing: border-box;
div {
border: 3px solid red;
padding: 10px;
width: 100px;
}
.border-box {
box-sizing: border-box;
}
.content-box {
box-sizing: content-box;
}
/* default, da wo es in der Seite steht */
position: static;
/* relativ zu seiner eigentlichen Position */
position: relative;
/* relativ zu seinem nächsten non-static parent o. window */
position: absolute;
position: relative;
position: absolute;
erlauben Verschiebung mittels
top: 10px;
left: 10px;
right: 10px;
bottom: 10px;
.menu-item {
display: inline-block;
position: relative;
}
.menu-item .children {
display: none;
position: absolute;
}
.menu-item:hover .children {
display: block;
}
Version | ECMA Standard | Jahr |
---|---|---|
ES1 | ECMAScript 1 | 1997 |
ES2 | ECMAScript 2 | 1998 |
ES3 | ECMAScript 3 | 1999 |
ES5 | ECMAScript 5 | 2009 |
ES6 | ECMAScript 2015 | 2015 |
... | ||
ECMAScript 2020 | 2020 |
Code wird zwischen script
-Tags geschrieben
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
</head>
<body>
<script>// code</script>
</body>
</html>
DOM-API
// findet das erste Element
const parent = document.querySelector("#id");
// findet alle Elemente
const elements = document.querySelectorAll("#id");
// Element erstellen
const newElement = document.createElement("div");
newElement.addEventListener("click", (event) => {
// Click-Event
});
// Element anhängen
parent.appendChild(newElement);
newElement.remove();
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
Parsen eines HTML-Dokuments
Verschiedene Varianten, JavaScript erst am Ende auszuführen
Damals mit jQuery:
<!DOCTYPE html>
<html>
<head>
<script>
$(document).ready((event) => {
const element = document.querySelector("#hello");
});
</script>
</head>
<body>
Hello World!
</body>
</html>
script
-Tag am Ende
<!DOCTYPE html>
<html>
<head>
</head>
<body>
Hello World!
...
<script src="index.js"></script>
</body>
</html>
Event-basiert, z.B. per DOMContentLoaded
<!DOCTYPE html>
<html>
<head>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
// wird ausgeführt, sobald das Dokument geparst wurde
const element = document.querySelector("#hello");
});
</script>
</head>
<body>
Hello World!
</body>
</html>
Moderne Variante
<script src="index.js" defer></script>
document
-Operationen
document.open();
document.write("Booo!");
document.close();
<body>
<script>document.write("Dies ist ein Text.
")</script>
<script>
const button = document.querySelector("button");
button.addEventListener("click", () => {
document.write("Und pfutsch!");
});
</script>
</body>
Version | Jahr |
---|---|
0.9 | 1991 |
1.0 | 1996 |
1.1 | 1999 |
2.0 | 2015 |
3.0 | 2022 |
Request an www.google.de
GET / HTTP/1.1
Host: www.google.de
User-Agent: curl/7.88.1
Accept: */*
Response von www.google.de
HTTP/1.1 200 OK
Date: Tue, 04 Apr 2023 15:45:26 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Content-Security-Policy-Report-Only: [...]
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: [...]
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
<!doctype html><html>...</html>
Einfach mal testen:
curl -v --http1.0 http://www.google.de
curl -v --http1.1 http://www.google.de
curl -v --http2 http://www.google.de
curl -v --http2-prior-knowledge http://www.google.de
curl -v --http3 http://www.google.de
Verschiedene "Verben" bzw. "Methoden"
Beispiel:
git clone https://git.furnco.de/r/public/web-development/examples.git
cd examples/templates/005-http-rest
node index.js
curl -X GET -is http://localhost:3000/todo
curl -X POST -H "Content-Type: application/json" -d '{"name": "Einkaufen"}' -is http://localhost:3000/todo
curl -X POST -H "Content-Type: application/json" -d '{"name": "Putzen"}' -is http://localhost:3000/todo
curl -X PUT -H "Content-Type: application/json" -d '{"name": "Schlafen"}' -is http://localhost:3000/todo/1
curl -X DELETE -is http://localhost:3000/todo/0
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 13
ETag: W/"d-KMmUcQ1kigqtwzZnU+jVDkYP3Co"
Date: Wed, 05 Apr 2023 12:05:05 GMT
Connection: keep-alive
Keep-Alive: timeout=5
["Einkaufen"]
app.get("/todo", (req, res) => {
return res.json(todos);
});
// [...]
app.post("/todo", (req, res) => {
// [...]
return res.json(todos);
// [...]
});
GET
und POST
auf klassischen Webseiten
Beispiel:
cd examples/templates/006-http-web
node index.js
"
) gesetzt werdenDefinition eines einfachen Objekts in JS
const o = {
x: 1,
y: 2
};
Entsprechung in JSON
{
"x": 1,
"y": 2
}
Mögliche Typen
Der äußere Container kann ein Array oder ein Object sein
{
"key": "value",
}
[
"foo",
"bar"
]
c / c++ | Java | C# | JS | |
---|---|---|---|---|
Formale Spezifikation | ja | |||
Übersetzung | Nativ kompiliert | Bytecode | Interpretiert / kompiliert (JIT) | |
Typisierung | statisch | dynamisch | ||
Speicher-Management | Manuell | Garbage Collector |
function add(x, y) {
return x + y;
}
const a = 5;
const b = add(a, 6);
const c = [];
# im Ordner 'examples/'
. hooks/post-checkout
# node [Script-Datei], z.B.
node index.js
console.log("Hello World!");
// < es6
var x = 1;
// >= es6
let y = 2;
const z = 3;
var yes = false;
if (yes) {
var a = 1;
}
console.log(a);
undefined
var yes = false;
if (yes) {
let a = 1;
}
console.log(a);
ReferenceError: a is not defined
function foo() {
var yes = false;
if (yes) {
var a = 1;
}
}
foo();
console.log(a);
ReferenceError: a is not defined
var
function
-Scope
var yes = false;
var a;
if (yes) {
a = 1;
}
console.log(a);
let
, const
let x = 5; // Number (Integer)
let f = 1.25; // Number (Float)
let b = true; // Boolean
let s = "hallo"; // String
let a = []; // Array
let o = {}; // Objekt
let nan = NaN; // Not a Number
let n = null; // Null
let u = undefined; // Undefiniert
if (x == 1) {
// Code
}
else if (x == 2) {
// alternativer Code
}
else {
// Fallback
}
for (let i = 0; i < 5; i++) {
// Code
}
const a = [1, 2, 3];
for (const element of a) {
// Code
}
while (i == 1) {
// Code
}
do {
// Code
} while (i == 1)
let a = [1, 2, 3];
a.push(4);
a.push(5);
a.pop();
console.log(a);
[1, 2, 3, 4]
let o = {x: 1, y: 2};
console.log(o.z);
console.log(o);
o.z = 3;
console.log(o);
undefined
{ x: 1, y: 2 }
{ x: 1, y: 2, z: 3 }
let o = {};
o.a = 1;
o["a"] = 1; // alternativ
o["$ !"] = 2; // so sind auch komplexe Schlüssel möglich
// o."$ !" = 2; => Syntaxfehler
// o.$ ! = 2; => Syntaxfehler
o[true] = 3;
console.log(Object.keys(o));
[ 'a', '$ !', 'true' ]
function foo() {
console.log("foo");
}
function bar(a) {
return a;
}
foo();
const a = bar(1);
console.log(a);
foo
1
let sum = function(a, b) {
return a + b;
}
let subtract = function() {
console.log(arguments);
return arguments[0] - arguments[1];
}
console.log(sum(1, 2));
console.log(subtract(1, 2));
3
[Arguments] { '0': 1, '1': 2 }
-1
function Rectangle(a, b) {
this.a = a;
this.b = b;
}
const r = new Rectangle(2, 3);
console.log(r.a);
2
function Rectangle(a, b) {
this.a = a;
this.b = b;
}
// jede Funktion besitzt einen Prototypen
Rectangle.prototype.getArea = function() {
return this.a * this.b;
}
const r1 = new Rectangle(2, 3);
const r2 = new Rectangle(4, 5);
console.log(r1.getArea());
console.log(r2.getArea());
6
20
function Rectangle(a, b) {
this.a = a;
this.b = b;
}
const r = new Rectangle(2, 3);
console.log(r.getArea);
Rectangle.prototype.getArea = function() {
return this.a * this.b;
}
console.log(r.getArea);
undefined
[Function (anonymous)]
const a = [1, 2, 3];
console.log(a.sum());
6
Array.prototype.sum = function() {
let sum = 0;
for (let number of this) {
sum += number;
}
return sum;
}
// Oberklasse
function Shape() {
}
Shape.prototype.getType = function() {
return "Shape";
}
// Unterklasse
function Rectangle(a, b) {
this.a = a;
this.b = b;
}
// Prototype der Oberklasse in die Unterklasse kopieren
Rectangle.prototype = Object.create(Shape.prototype);
// Konstruktor der Oberklasse am Prototypen durch den eigenen ersetzen
Rectangle.prototype.constructor = Rectangle;
const r = new Rectangle(2, 3);
console.log(r.getType());
// .getType() selber implementieren
Rectangle.prototype.getType = function() {
return "Rectangle";
}
console.log(r.getType());
Shape
Rectangle
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
class Rectangle {
constructor(a, b) {
this.a = a;
this.b = b;
}
getArea() {
return this.a * this.b;
}
}
const r = new Rectangle(2, 3);
console.log(r.getArea());
6
// Oberklasse
class Shape {
getType() {
return "Shape";
}
}
// Unterklasse erbt von Oberklasse
class Rectangle extends Shape {
constructor(a, b) {
super();
this.a = a;
this.b = b;
}
}
const r = new Rectangle(2, 3);
console.log(r.getType());
// Implementierung per Prototype weiterhin möglich, falls man so will
Rectangle.prototype.getType = function() {
return "Rectangle";
}
console.log(r.getType());
Shape
Rectangle
Asynchron: Es wird nicht auf die Beendigung des Befehls gewartet, sondern mit dem Programm fortgefahren. Die Vollendung des asynchronen Befehls tritt irgendwann in der Zukunft ein.
setTimeout(callback, time); // Ausführung nach [time] Millisekunden
setInterval(callback, interval); // Ausführung alle [interval] Millisekunden
setTimeout(function() {
// Code, der nach 2 Sekunden ausgeführt wird
console.log("zweiter");
}, 2000);
console.log("erster");
erster
zweiter
Aufgabe: Schreibe ein Programm, das zwei weitere beliebige Texte mit jeweils 2 Sekunden Verzögerung von einander ausgibt, d.h.
"erster" -> 2s -> "zweiter" -> 2s -> "Text A" -> 2s -> "Text B"
setTimeout(function() {
console.log("zweiter");
// weiterer Code
}, 2000);
console.log("erster");
setTimeout(function() {
console.log("zweiter");
setTimeout(function() {
console.log("Text A");
setTimeout(function() {
console.log("Text B");
}, 2000);
}, 2000);
}, 2000);
console.log("erster");
new Promise(function (resolve, reject) {
// ich bin fertig
resolve();
// es ist ein Fehler aufgetreten
reject();
});
const p = new Promise(function (resolve, reject) {...});
const p2 = new Promise(function (resolve, reject) {...});
const p3 = new Promise(function (resolve, reject) {...});
const p4 = new Promise(function (resolve, reject) {...});
p
.then(function() { return p2; })
.then(function() { return p3; })
.then(function() { return p4; });
console.log("erster");
delay(2000)
.then(function () {
console.log("zweiter");
return delay(2000);
})
.then(function() {
console.log("Text A");
return delay(2000);
})
.then(function() {
console.log("Text B");
})
const delay = function() {
// implementieren
}
console.log("erster");
delay(2000)
.then(function () {
console.log("zweiter");
return delay(2000);
})
.then(function() {
console.log("Text A");
return delay(2000);
})
.then(function() {
console.log("Text B");
})
const delay = function(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, ms);
});
}
console.log("erster");
delay(2000)
.then(function () {
console.log("zweiter");
return delay(2000);
})
.then(function() {
console.log("Text A");
return delay(2000);
})
.then(function() {
console.log("Text B");
})
const p = new Promise(function(resolve, reject) {
resolve("ok");
});
p.then(function(value) {
console.log(value);
});
ok
const p = new Promise(function(resolve, reject) {
reject(new Error("Fehler"));
});
p
.then(function() {
console.log("Ich werde nicht aufgerufen")
})
.catch(function(e) {
console.log(e.message);
});
Fehler
const fs = require("fs");
fs.readFile("./hello.txt", {encoding: "utf8"}, function(error, data) {
console.log(data);
});
Hello World!
const fs = require("fs");
function readFile() {
// implementieren
}
readFile("./hello.txt").then(function(content) {
console.log(content);
});
Hello World!
const fs = require("fs");
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, {encoding: "utf8"}, function(error, data) {
resolve(data);
});
});
}
readFile("./hello.txt").then(function(content) {
console.log(content);
});
Hello World!
Hello World!
Hallo Welt!
const fs = require("fs");
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, {encoding: "utf8"}, function(error, data) {
resolve(data);
});
});
}
readFile("./hello.txt")
.then(function(content) {
console.log(content);
return readFile("./hallo.txt");
})
.then(function(content) {
console.log(content);
});
Hello World!
Hallo Welt!
await
nur im Kontext einer async
-Funktion möglich
async function wrapper() {
const p = new Promise(function(resolve, reject) {
resolve("Hello from Promise!");
});
const result = await p;
console.log(result);
}
wrapper();
Hello from Promise!
const fs = require("fs");
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, {encoding: "utf8"}, function(error, data) {
resolve(data);
});
});
}
async function wrapper() {
const content1 = await readFile("./hello.txt");
const content2 = await readFile("./hallo.txt");
console.log(content1);
console.log(content2);
}
wrapper();
Hello World!
Hallo Welt!
Auflösen von komplexen Strukturen
// Objekt destrukturieren (per Attribut-Namen)
const { a, b } = { a: 1, b: 2 };
console.log(a);
console.log(b);
// Array destrukturieren (per Position)
const [ c, d ] = [3, 4];
console.log(c);
console.log(d);
1
2
3
4
// Bei Objekten Elemente umbenennen oder auch auslassen
const { a: x, b: y } = { a: 1, b: 2, c: 3 };
console.log(x);
console.log(y);
// Bei Arrays Elemente auslassen
const [ , a, b, , c] = [3, 4, 5, 6, 7, 8];
console.log(a);
console.log(b);
console.log(c);
1
2
4
5
7
// Verschachteltes Destructuring
const {a: [, x, ] } = { a: [0, 1, 2]};
console.log(x);
1
const o = { a: [0, {b: [0, 5]}, 2]};
const { /* Destructuring-Ausdruck */ } = o;
console.log(x);
5
const o = { a: [0, { b: [0, 5] }, 2]};
const { a: [, { b: [, x] }]} = o;
console.log(x);
5
const o = { a: [0, {b: [0, { c: [{ x: 5 }, { d: 3}] }]}, 2]};
const { /* Destructuring-Ausdruck */ } = o;
console.log(x);
3
const o = { a: [0, {b: [0, { c: [{ x: 5 }, { d: 3}] }]}, 2]};
const { a: [, {b: [, { c: [, { d: x}] }] }] } = o;
console.log(x);
3
Destructuring zum parallelen Ausführen von mehreren Promises
const fs = require("fs");
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, {encoding: "utf8"}, function(error, data) {
resolve(data);
});
});
}
async function wrapper() {
const [ content1, content2 ] = await Promise.all([
readFile("./hello.txt"),
readFile("./hallo.txt")
]);
console.log(content1);
console.log(content2);
}
wrapper();
function throwError() {
try {
throw new Error("Fehler!");
return "Hello World!";
}
catch (e) {
return e.message;
}
finally {
console.log("Finally wird immer ausgeführt");
return "Finally!";
}
}
const result = throwError();
console.log(result);
Finally wird immer ausgeführt
Finally!
Anwendungsbeispiel für Try-Catch-Finally
function readFromDatabase() {
try {
db.connect();
return db.read();
}
catch (e) {
console.log(e.stack);
return "";
}
finally {
if (db.open) {
db.close();
}
}
}
function a() {
const x = 5;
return function() {
return x * x;
};
}
const squareX = a();
const result = squareX();
console.log(result);
25
class MyClass {
constructor(a) {
this.a = a;
}
getA() {
return this.a;
}
}
const x = new MyClass(5);
const y = new MyClass(10);
console.log(x.getA());
console.log(y.getA());
5
10
global.a = 5;
function foo() {
this.a += 10;
}
foo();
console.log(a);
15
class MyClass {
/* ... */
setA() {
function f() {
this.a += 100;
}
f();
}
}
const x = new MyClass(5);
x.setA();
console.log(x.getA());
TypeError: Cannot read property 'a' of undefined
class MyClass {
/* ... */
setA() {
const me = this;
function f() {
me.a += 100;
}
f();
}
}
const x = new MyClass(5);
x.setA();
console.log(x.getA());
105
return
nicht notwendig
// Einzeiler
const f1 = _ => 1;
const f2 = () => 1;
const f3 = (x) => x * x;
const f4 = (x, y) => x + y;
// Mehrzeiler
const f5 = (x, y) => {
return x * y;
};
class MyClass {
/* ... */
setA() {
const f = () => {
this.a += 100;
}
f();
}
}
const x = new MyClass(5);
x.setA();
console.log(x.getA());
105
function* generator() {
let i = 0;
while(true) {
yield i;
i++;
}
}
const g = generator();
console.log(g.next().value);
console.log(g.next().value);
console.log(g.next().value);
1
2
3
function* generator() {
yield 1;
yield 2;
yield 3;
}
const g = generator();
for (const i of g) {
console.log(i);
}
1
2
3
const revenues = [
{month: 0, revenue: 1000},
{month: 1, revenue: 2000},
{month: 2, revenue: 3000},
{month: 3, revenue: 4000},
{month: 4, revenue: 5000},
{month: 5, revenue: 6000},
{month: 6, revenue: 6000},
{month: 7, revenue: 5000},
{month: 8, revenue: 4000},
{month: 9, revenue: 3000},
{month: 10, revenue: 2000},
{month: 11, revenue: 1000}
];
let totalRevenue = 0;
/* ... */
console.log(totalRevenue);
totalRevenue = revenues
.filter((revenue) => revenue.month < 6)
.map((revenue) => revenue.amount)
.reduce((sum, amount) => sum + amount, 0);
21000
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
a.filter((e) => e > 2); // Filterung
a.map((e) => e * 2); // Umformung
a.reduce((p, e) => p + e, 0); // Zusammenrechnung / Reduzierung
a.some((e) => e > 5); // true wenn min. ein Element die Bedingung erfüllt
a.every((e) => e > 5); // true wenn alle Elemente die Bedingung erfüllen
const m = new Map();
m.set("a", 0);
m.set("a", 1);
m.set("b", 2);
m.has("c"); // => false
m.delete("b");
console.log(m.get("a")); // => 1
m.forEach((k, v) => console.log(k, v)) // => "a" 1 ...
for (const entry of m.entries()) {
console.log(entry); // => ["a", 1] ...
}
for (const key of m.keys()) {
console.log(key); // => "a" ...
}
for (const value of m.values()) {
console.log(value); // => "1" ...
}
const s = new Set();
s.add("a");
s.add("a");
s.has("c"); // => false
s.delete("a");
console.log(s.size); // => 0
s.forEach((k, v) => console.log(k, v)) // => "a" "a" ...
for (const entry of m.entries()) {
console.log(entry); // => ["a", "a"] ...
}
for (const key of m.keys()) {
console.log(key); // => "a"...
}
for (const value of s.values()) {
console.log(value); // => "a" ...
}
function doSomething(e) {
console.log("capture");
}
function click(e) {
console.log("bubble");
}
div.addEventListener("click", doSomething, true);
button.addEventListener("click", click, false /* default */);
capturing
bubble
Aufgabe: Erweitere das vorherige Beispiel so, dass das div-Element einen weiteren Click-Handler bekommt, der den Text "bubble2" anzeigt, nachdem "bubble" in der Konsole geloggt wurde.
Erwartete Ausgabe:
capturing
bubble
bubble2
Lösung:
function doSomething(e) {
console.log("capture");
}
// neu
function doSomething2(e) {
console.log("bubble2");
}
function click(e) {
console.log("bubble");
}
div.addEventListener("click", doSomething, true);
div.addEventListener("click", doSomething2, false); // neu
button.addEventListener("click", click, false /* default */);
function click(e) {
e.stopPropagation()
}
Aufgabe: Ändere das Ergebnis des vorherigen Beispiels, sodass die Bubble-Phase auf Ebene des button-Elements gestoppt wird. Was ändert sich in der Ausgabe?
Lösung:
function doSomething(e) {
console.log("capture");
}
function doSomething2(e) {
console.log("bubble2");
}
function click(e) {
e.stopPropagation();
console.log("bubble");
}
div.addEventListener("click", doSomething, true);
div.addEventListener("click", doSomething2, false);
button.addEventListener("click", click, false /* default */);
button.dispatchEvent(new Event("click"));
doSomethingElse();
button.dispatchEvent(new Event("click"));
doAnotherThing(); // wird erst ausgeführt, wenn zuvor alle Event-Handler fertig sind
const empty1 = "";
const empty1 = '';
const s1 = 'abc';
const s2 = "abc";
const s3 = 'a"b"c';
const s4 = "a\"b\"c";
const s5 = 'a\'b\'c';
const s6 = "a'b'c";
const s7 = `a'b'"c"`;
const age = 10;
// Konkatenation
const s = "My age is " + number + "!";
// Interpolation
const s = `My age is ${number}!`;
// => "My age is 10!"
function sayHello() {
return "Hello";
}
let interpolation = `${sayHello()}`;
// => "Hello"
interpolation = `5 + 5 = ${5+5}`;
// => "5 + 5 = 10"
return html`hello world!`
function myTag() {
console.log(arguments);
// return 5;
// return true;
return "abc";
}
let interpolation = myTag`a ${5} b ${6} c`;
// => "abc"
Arguments(3) [Array(3), 5, 6]
0 : (3) ['a ', ' b ', ' c', raw: Array(3)]
1 : 5
2 : 6
Schreibe ein Tag, welches die Zahlen aus den dynamischen Ausdrücken zusammenrechnet (strings.js
)
function add(parts, ...numbers) {
return numbers.reduce((p, c) => p + c, 0);
}
let sum = add`a ${5} b ${6} c`;
console.log(sum);
// => 11
function add(...numbers) {
return numbers.reduce((p, c) => p + c, 0);
}
add(1, 2, 3); // => 6
const o1 = {a: 1, b: 2};
const o2 = {x: 5, y: 6, ...o1};
const a1 = [4, 5, 6];
const a2 = [1, 2, 3, ...a1];
window.innerWidth; // innere Auflösung des sichtbaren Fensters
window.innerHeight;
window.outerWidth; // äußere Auflösung des Browserfensters inklusive Adresszeile usw.
window.outerHeight;
window.addEventListener(); // Events an das äußerste "Dach"-Element hängen
location.href; // aktuelle URL
location.host; // aktuelle Domain, z.B. "www.google.de"
location.protocol; // aktuelles Protokoll, z.B. "https:"
navigator.language; // bevorzugte Browsersprache, z.B. "de"
navigator.userAgent; // z.B. "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) [...]"
navigator
.mediaDevices
.getDisplayMedia(); // Fordere Erlaubis zum Streamen von Anwendungen oder Bildschirmen an
document.body; // <body>-Element der Seite
document.activeElement; // aktives Element der Seite, welche gerade den Fokus besitzt
document.cookie; // für die aktuelle Seite gesetzte Cookies
document.execCommand("copy"); // Texte in die Zwischenablage zu schreiben
document.createElement("div"); // erzeuge neues DOM-Element mit angegebenen Tag
// auf 5MB Daten pro Domain begrenzt
localStorage.getItem(key); // Element holen
localStorage.setItem(key, value); // Element setzen
localStorage.removeItem(key); // Element entfernen
selector [, selector2, selector3, ...] {
property1: value;
property2: value;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
}
/* Elemente */
html, body {...}
/* IDs */
#my-id {...}
/* Klassen */
.my-class {...}
/* Pseudo-Klassen */
div:hover {...}
/* Attribute */
div[my-attribut="abc"] {...}
/* Kombinationen */
div.my-class#my-id {...}
html {
--my-background-color: #abcdef;
}
html div {
background-color: var(--my-background-color, white);
}
display
-Attributblock
width
und height
möglichinline
Custom Elements
width
und height
nicht möglichinline-block
block
und inline
width
und height
möglichnone
position
-Attributstatic
relative
static
-Position, z.B. mittels left: 20px
absolut
static
-Parent (sonst window
)Implementiere folgendes Beispiel-Menü
.menu-item {
display: inline-block;
position: relative;
}
.menu-item .children {
display: none;
position: absolute;
}
.menu-item:hover .children {
display: block;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"></meta>
<meta
name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
>
</head>
<body>
...
</body>
</html>
Ein Muss für alle responsiven Seiten
npm install -g http-server
cd pfad/zum/projekt
http-server --port 8080
div {
width: 1px; /* Pixel */
width: 1pt; /* Points */
width: 1em; /* Breite des "M" in der Schriftgröße des Elements */
width: 1rem; /* Breite des "M" in der Schriftgröße des HTML-Elements */
width: 1vw; /* Prozent der View Width */
width: 1%; /* Prozent des Elternelements */
width: 5fr; /* Fraction (nur im CSS Grid verfügbar) */
}
Größeneinheiten in einer HTML-Datei ausprobieren
.container {
display: flex; /* soll ein flexibler Container sein */
display: inline-flex;
flex-direction: column; /* Kinder spaltenweise anordnen */
flex-direction: row; /* Kinder zeilenweise anordnen */
align-items: center;
justify-content: center;
gap: 20px; /* Abstände zwischen den Achsen */
}
.child {
flex-grow: 1; /* Anteiliger Platz, wenn mehr als nötig da ist */
flex-shrink: 1; /* Anteiliger Platz, wenn weniger als nötig da ist */
flex-basis: 20px; /* Grundlage zur Berechnung der finalen Größe */
order: 1; /* Erlaubt die Darstellung in anderer Reihenfolge als durch das HTML vorgegeben */
}
Mit CSS Flexbox rumspielen
.grid {
display: grid; /* inline-grid; */
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 5px 10px;
}
<div class="grid">
<span>A</span>
<span>B</span>
<span>C</span>
<span>D</span>
<span>E</span>
<span>F</span>
</div>
.grid {
grid-template-columns: [left] 1fr [column1] 1fr [column2] 1fr [right];
grid-template-rows: [top] 1fr [row1] 1fr [bottom];
}
.a {
grid-column: 2 / span 2;
grid-row: 1;
}
.b {
grid-column: left / column2;
grid-row: 2;
}
<div class="grid">
<span class="a">A</span>
<span class="b">B</span>
</div>
.grid {
grid-template-areas:
"header header header"
"content content content";
}
.a {
grid-area: header;
}
.b {
grid-area: 2 / 1 / 2 / 3; /* start-row / start-column / end-row / end-column */
/* alternativ auch Namen der Lines möglich */
}
Implementiere folgendes Design:
.grid {
display: grid;
grid-template-rows: 1fr 5fr 1fr;
gap: 2px;
grid-template-areas:
"navigation header header header"
"navigation content content content"
"footer footer footer footer"
}
.navigation {
grid-area: navigation;
}
.header {
grid-area: header;
}
.content {
grid-area: content;
}
.footer {
grid-area: footer;
}
<div class="grid">
<div class="area navigation">Navigation</div>
<div class="area header">Header</div>
<div class="area content">Content</div>
<div class="area footer">Footer</div>
</div>
.grid {
align-items: center;
justify-items: center;
}
@media screen and (min-width: 640px) and (max-width: 1024px) {
.navigation {
display: none;
}
}
Bedeutung: Wenn die Seite auf einem Display angezeigt wird, welches zwischen 640px und 1024px in der Breite anzeigen kann, dann blende die Navigation aus
Entwerfe eine kleine Seite mit @media und zeige ein beliebiges Element ab einer bestimmten Höhe oder Breite an (die Größe des Displays kann dynamisch mit den Entwicklertools im Gerätesimulator geändert werden)
Passe die Vorlage "media-queries-responsive-layout.html" mit @media-Queries so an, dass das Menü links ab einer maximalen Breite von 480 Pixel und kleiner nach links oben in die Ecke minimiert wird und nur per Hover wieder 50% Breite und 100% Höhe bekommt
@media screen and (max-width: 480px) {
.menu {
grid-area: unset;
position: absolute;
}
.menu:hover {
height: 100%;
width: 50%;
}
.grid {
grid-template-columns: 0fr 5fr;
}
}
.foo {
transform: translateX(20px) translateY(20px);
transform-origin: left;
}
Die meisten besitzen zusätzlich spezifischere X-, Y- oder 3D-Versionen
Probiere die genannten Transform-Effekte in einer beliebigen Seite aus
width
, height
, transform
, color
und viele weiteredisplay
lässt sich jedoch nicht animieren
.foo {
width: 100%;
background-color: red;
/* transition: [Attribut] [Dauer] [Zeitfunktion] */
transition: width 1s ease-out;
}
.foo:hover {
width: 50%;
}
Keyframes
.foo {
animation-duration: 3s;
animation-name: slidein;
}
@keyframes slidein {
from {
margin-left: 100%;
width: 300%;
}
to {
margin-left: 0%;
width: 100%;
}
}
.foo {
/* ... */
}
@keyframes slidein {
1% {
margin-left: 100%;
}
100% {
margin-left: 0%;
}
}
.container {
perspective: 1000px; /* "Entfernung" des Betrachters */
transform-style: preserve-3d; /* oder "flat" */
}
.element {
backface-visibility: hidden; /* oder "visible" */
}
Implementiere einen drehenden Würfel (Vorlage "templates/008-css/css-3d-cube.html")
Was sind Web Components?
Vorteile
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!)
(/examples/templates/web-components/hello-sayer.html)
<slot>
<slot>
s
Entwerfe eine CardComponent
(Vorlage "card-component.html") in folgendem Stil und nutze dabei sinnvolle <slot>
s:
Ein :host
-Block mit einem display
gehört zum guten Ton!
Innerhalb einer Komponente können beliebige Styles für verschiedene Selektoren verwendet werden
Welche Wechselwirkungen haben Styles zueinander, die innerhalb und außerhalb einer Komponente definiert sind? (Vorlage "web-component-styles.html")
Einige globale Styles werden dennoch vererbt:
inherit
besitzen (z.B. color
oder font-family
)--my-custom-color
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. color
oder background-color
)
Erinnerung CSS-Variablen:
/* Definition */
html {
--my-variable: #abcdef;
}
/* Usage */
.my-class {
background-color: var(--my-variable, white);
}
In der Anfangszeit von Web Components wurde oft folgendes Muster zur Individualisierung verwendet:
/* 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);
}
Eleganteres Stylen von Web Components mittels part
/* Global Scope */
my-component::part(inner) {
border-color: yellow;
border-width: 2px;
}
/* Inside Web Component */
Passe die CardComponent
aus einer vorherigen Übung an (Vorlage "card-component.html"), sodass man Titel und Inhalt einfach von außen per part
s stylen kann.
part
s von eingebetteten Web Components exportieren und verfügbar machen
Inner Text
Passe das Beispiel (Vorlage "exportparts.html") entsprechend an, sodass von außen sowohl der Inner Text
als auch der Outer Text
stylebar sind
Web Components können direkte Kinder, die via slot
eingefügt werden, stylen. Hierfür gibt es den Pseudo-Elemente-Selektor ::slotted([selector])
, welcher Elemente selektiert, die in einem Slot stecken:
/* Inside Web Component */
::slotted(*) { /* Selectors like elements, #id or .class */
font-style: italic;
}
Definiere mit dem Pseudo-Elemente-Selektor ::slotted()
Styles, sodass per slot
eingefügte Elemente mit der Klasse red
einen roten und Elemente mit der Klasse blue
einen blauen Hintergrund haben. Die Schriftart soll jeweils weiß sein (Vorlage "slotted.html")
dispatcht
werdenclick
bubbeln durch das Shadow Dom nach oben durchCustomEvent
muss entsprechend konfiguriert werden
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.
}
));
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
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>
`;
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}));
}
});
}
Die API ist in viele verschiedene Module aufgeteilt
fs | Dateisystem |
crypto | Kryptografie |
zlib | Datenkompression |
http | Webserver |
net | Netzwerk (TCP) |
tls | Verschlüsseltes Netzwerk (TCP) |
Aufruf eines Programmes in der Kommandozeile
node [Node-Parameter] path/to/script.js [Anwendungsparameter]
z.B.
node --inspect index.js --port 3000
Einbinden von Modulen
const fs = require("fs");
Aufbau eines Moduls
module.exports = {
foo: 5,
sayHello: function(name) {
return "Hello " + name + "!";
}
}
oder
exports.foo = 5;
exports.sayHello = function(name) {
return "Hello " + name + "!";
};
Aufgabe
Schreibe ein Modul, welches eine Square
-Klasse zur Verfügung stellt, mit welchem man den Umfang und den Flächeninhalt eines Rechtecks berechnen kann:
const Square = require("./square");
const square = new Square(2, 3);
console.log(square.getArea()); // => 6
console.log(square.getPerimeter()); // => 10
package.json
npm init -y
{
...
"bin": {
"my-command": "./index.js"
}
}
index.js
#! /usr/bin/env node
console.log("Hello world from the command line!");
Installation
npm install -g path/to/project
z.B.
npm install -g .
Ausführung
my-command
Kommandozeilenparameter
console.log(process.argv);
angelehnt an die Parameter aus der Programmiersprache c
:
int main(int argc, char *argv[])
Aufgabe
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:
my-command 2 3
Array-artige Struktur für rohe Bytes
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"
Mit Buffern arbeiten:
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
}
Aufgabe
Implementiere eine XOR-Verschlüsselung (^
-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.
Aufgabe
Implementiere zur vorherigen Verschlüsselung eine Entschlüsselung.
Lösung
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"
Konstanter Fluss von (meist) großen Datenmengen in kleinen Paketen
Gängige Typen von Streams
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);
Aufgabe
Lese eine beliebige Datei als Readable Stream, verschlüssele den Inhalt mit der XOR-Methode und schreibe die verschlüsselten Bytes in eine Zieldatei
Aufgabe
Ändere das Programm, sodass auf Konsolenebene ein Pipe (|) ermöglicht wird:
echo "Hello World!" | node index.js
Nutze dafür die implizit vorhandenen Streams von Standard In / Out:
process.stdin; // Readable Stream
process.stdout; // Writable Stream
crypto
-Moduls ist es möglich, moderne sichere kryptografische Verfahren zu verwenden:
const crypto = require("crypto");
Hierzu nutzt NodeJS selber die weit verbreitete OpenSSL-Bibliothek
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);
Aufgabe
Erzeuge jeweils den md5- und den sha256-Hash als Hex-Wert der beiden Texte in der Datei text-examples.txt
und vergleiche jeweils ihren md5- und sha256-Hash miteinander
zlib
ist es möglich, Kompression in seinen Anwendungen zu nutzen.
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);
Aufgabe
Nutze die einfachen Varianten zlib.deflate
und zlib.inflate
, um einen beliebigen Text zu komprimieren und anschließend wieder zu dekomprimieren
Aufgabe
Nutze die Stream-Varianten zlib.createDeflate
und zlib.createInflate
, um jeweils ein getrenntes Programm für die Kompression und Dekompression mit process.stdin
und process.stdout
zu machen, sodass in der Konsole folgende Nutzung möglich ist:
echo "abc" | node compress.js | node decompress.js
cat file.txt | node compress.js > file.zipped
cat file.zipped | node decompress.js > file.txt
net
ist es möglich, eine TCP-Verbindung aufzubauen
/* 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);
/* Client */
const net = require("net");
const socket = net.connect(3000, () => {
// 'connect' listener
});
socket.on("data", (data) => {
// data is a buffer
});
Aufgabe
Implementiere die Clientlogik in der tcp-client.js
, um dem Server ein "Hello Server!"
zurück zu senden
Aufgabe
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.
Kurze Wiederholung des HTTP-Protokolls
Request
GET / HTTP/1.1
Host: localhost:3456
Response
HTTP/1.1 200
Content-Type: text/plain
Content-Length: 12
Hello World!
Aufgabe
Implementiere mithilfe des TCP-Sockets einen minimalen HTTP-Server, welcher Hello <name>
als Text zurück gibt, wobei <name>
aus dem Query-Parameter gelesen werden soll
const http = require("http");
const server = http.createServer((req, res) => {
res.writeHeader(200, {
"Content-Type": "text/plain", /* Put Content-Type here */
});
console.log(req.rawHeaders);
console.log(req.method);
console.log(req.url);
res.write(/* response content */);
res.end(); /* important if no Content-Length is specified */
});
server.listen(3456);
Aufgabe
Experimentiert mit verschiedenen Möglichkeiten von Content-Length
und dem Vorhandensein von res.end()
, mit längerer oder kürzerer Content-Length
als der tatsächliche Body oder teilt den Body in zwei res.write()
und verzögert künstlich mit einem setTimeout() den zweiten Teil und schaut euch im Browser bei den Entwickler-Tools die Timings der Response an.
Was sind Worker Threads?
Eine bidirektionale nachrichten-basierte Implementierung von Multithreading in NodeJS
Beispiel:
// Main Thread
const { Worker } = require('worker_threads');
const data = 'some data';
const worker = new Worker("path/to/worker.js", { workerData: data });
worker.on('message', message => console.log('Reply from Thread:', message));
// Worker Thread
const { workerData, parentPort } = require('worker_threads');
const result = workerData.toUpperCase();
parentPort.postMessage(result);
Weitere nützliche Funktionen:
// Main Thread
worker.on('message', message => processMessage(message));
worker.on('error', error => console.error(error));
worker.postMessage({a: 1}); // send any data to the worker
worker.terminate(); // manually terminate worker thread
// Worker Thread
parentPort.on('message', message => doSomething(message)); // receive messages from the main thread
process.exit(0); // terminate self, optionally with exit code
Aufgabe
Nutze die in den examples
vorhandene Funktion isPrime
(010-nodejs/mt-main.js und 010-nodejs/mt-worker.js) und implementiere mit Hilfe der Worker Threads
die Suche nach Primzahlen.
Die gewünschte maximale Zahl, bis zu der die Primzahlen gesucht werden sollen als auch die Anzahl der Threads, mit der gleichzeitig gesucht werden soll, sollen möglichst einfach parametrisierbar sein (einfache Variable im Code genügt).
Probiert verschiedene Thread-Anzahlen aus und vergleicht einmal die benötigten Zeiten.
Hinweise:
Promise.all
)workerData
können auch Objekte übergeben werdenIm Gegensatz zu einer SPA ("Single Page Application") teilt sich eine Multi Page Application in mehrere Unterseiten auf, die sich per URL z.B. im Browser adressieren lassen.
In einem neuen Ordner / Projekt:
npm init -y
npm install express pug
mkdir public
// index.js (s. examples 010-nodejs/mpa.js)
const express = require("express");
const app = express();
app.use(express.static("public"));
app.listen(3002);
Fülle das Verzeichnis "public" mit jeweis einer HTML-Datei für eine "Home"- und eine "About Us"-Seite, die beide eine einfache Navigations-Leiste enthalten (simple span
mit Link (href)), um zwischen diesen beiden Seiten navigieren zu können.
Unterscheiden sollen sich diese beiden Seiten durch ihren "Inhalt", indem dort einfach "Home" und "About Us" enthalten ist.
Template-Engine "pug"
html
head
body
div This is a div with some text
#main.my-class This is also a div with short-hands for id and class
a(href="/") This is a link with href
Conditionals
html
head
body
if name
div Hello #{name}!
else
div Hello Anonymous"
Extension / Inheritance
// layout.pug
html
head
body
div Some static content
block content
// page.pug
extends layout.pug
block content
div This is my home content
Pug in Express
mkdir views
// ...
app.set('view engine', 'pug');
app.get('/', (req, res) => {
// file name without .pug and some data passed to the template
res.render('index', { title: 'Hey', message: 'Hello there!' })
});
app.listen(3002);
// views/index.pug
html
head
title #{title}
body #{message}
Implementiere die Seiten aus dem vorherigen Beispiel mit Pug (nutze Vererbung, um die Navigationsleiste nicht doppelt implementieren zu müssen) und baue einen Gruß im Seitentext mit dynamischen Namen ein, den man z.B. in der URL als Query-Parameter "?name=Joe" angibt und der im Server ausgewertet wird.
Wichtige Aspekte beim produktiven Betrieb von Anwendungen
Typische Werkzeuge beim Betrieb
Vorbereitungen
Eckdaten für die VM
Nach der Installation in der VM mit den bekannten Credentials anmelden und die IP in Erfahrung bringen:
ip a
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP [...]
link/ether 08:00:27:43:e5:2c brd ff:ff:ff:ff:ff:ff
inet 10.0.0.166/16 metric 100 brd 10.0.255.255 scope global dynamic enp0s3
valid_lft 863191sec preferred_lft 863191sec
Nach der Installation am Server anmelden
ssh user@domain [-p 12345]
Beispiele für eine Domain: localhost, www.example.com, 127.0.0.1, [0:0:0:0:0:0:0:1], [::1]
Es kann auch der bei der Installation des Servers angegebene Host-Name verwendet werden, z.B.
# "vm-ubuntu-server"
ssh user@vm-ubuntu-server
# Paketlisten updaten
sudo apt update
# Pakete updaten
sudo apt upgrade
sonstige Werkzeuge installieren
sudo apt install vim git curl build-essential
# In einen Ordner gehen
cd [Ordner]
# Zum vorherigen Ordner gehen
cd -
# Längere Ordnerpfade sind auch möglich
# realtiv
cd a/b/c/d
# absolut
cd /var/www/html
# Einen Ordner erstellen
mkdir my-folder
# Mehrere Hierarchien
mkdir -p a/b/c/d
# Mehrere Ordner
mkdir a b c
# Ordner rekrusiv löschen
rm -rf [Ordner]
# Ordner kopieren
cp -r quelle ziel
# Datei kopieren
cp quelle ziel
# Ordner und Dateien verschieben
mv quelle ziel
# User anlegen
# -m => Home Ordner anlegen
# -G => zur angegeben Gruppe hinzufügen
# -s => Pfad zur Shell, in diesem Fall /bin/bash
useradd [user] -m -G sudo -s /bin/bash
# Passwort setzen
passwd [user]
# Schlüsselpaar lokal erzeugen, falls nicht vorhanden
ssh-keygen
# Inhalt aus Datei kopieren:
cat ~/.ssh/id_rsa.pub
# Auf dem Server in folgenden Datei einfügen (Public Key pro Zeile):
~/.ssh/authorized_keys
anschließend wird kein Passwort mehr beim Login benötigt
sudo apt install apache2
Einrichtung überprüfen
# Oder im Browser aufrufen:
curl http://vm-ubuntu-server
Aufbau der Apache-Konfiguration
# Hauptdatei, selten gebraucht
/etc/apache2/apache2.conf
# Verfügbare vhosts
/etc/apache2/sites-available/*.conf
# Aktivierte vhosts, Symlinks auf obige verfügbare
/etc/apache2/sites-enabled/*.conf
cd /etc/apache/sites-available
# 000-default.conf als Vorlage nutzen
# vm-ubuntu-server durch eigenen Host ersetzen
sudo cp 000-default.conf www.vm-ubuntu-server
# 'ServerName' einkommentieren und mit eigenem Host anpassen
# Weitere Optionen je nach Fall anpassen, z.B. 'DocumentRoot'
sudo vim www.vm-ubuntu-server
# vhost / Site aktivieren
sudo a2ensite www.vm-ubuntu-server.conf
Domains lokal auf dem Entwicklungssystem bekannt machen:
# Windows
# z.B. mit Notepadd++ editieren. Benötigt Administrator-Rechte
C:\Windows\System32\drivers\etc\hosts
# Unix
# z.B. mit vim editieren. Benötigt Administrator-Rechte
/etc/hosts
# unten hinzufügen
[IP des Servers] www.vm-ubuntu-server
# scp [Quelle] [Ziel], z.B.
scp index.html user@domain:~/html
Richte die Sub-Domain www.vm-ubuntu-server
ein und liefere unter dieser Adresse eine beliebige HTML-Seite aus. Dies kann eine minimale selbstgeschriebene oder eine beliebige komplexere aus den vergangenen Veranstaltungen sein, z.B. aus den CSS-Themen (jedoch keine NodeJS-Projekte).