Was ist neu bei Vue.js 3?

Seit der Veröffentlichung von Vue.js 2 ist die Vue.js-Community schnell gewachsen und erhält von den Usern großartige Bewertungen. Neben Angular und React zählt es zu den beliebtesten JavaScript-Frameworks. Nun steht Vue.js 3 kurz vor der Veröffentlichung und hat in den letzten Monaten für einen ziemlichen Hype in der Welt der Webentwicklung gesorgt.

Verbesserungen

Die neue Version von Vue.js ist auf Geschwindigkeit ausgelegt. Obwohl Vue 2 schon relativ schnell war, hat Vue 3 eine bis zu 100% bessere Update-Leistung und bis zu 200% schnellere serverseitige Renderings. Auch die Komponenten-Initialisierung ist jetzt effizienter, sogar mit Compiler-informierten schnellen Pfaden zur Ausführung. Das virtuelle DOM wurde ebenfalls vollständig neu geschrieben und übertrifft in Sachen Geschwindigkeit seine alte Version deutlich.

Auch von der Größe her gibt es nun Verbesserungen. Vue 2 war mit einem Gewicht von etwa 20 kB gzipped bereits verdammt klein. Mit einer konstanten Gundliniengröße von < 10 kB gzipped wiegt Vue 3 nur noch halb so viel. Wie? Größtenteils durch Eliminieren nicht genutzter Bibliotheken (Tree Shaking). Wenn du also ein Element nicht verwendest, ist es nicht enthalten.

Vue 3 wird TypeScript unterstützen. Zusätzlich werden die Pakete entkoppelt, wodurch alles modularer und einfacher zu warten sein wird.

Das Erstellen nativer Anwendungen mit Vue ist gar nicht so schwierig, aber mit Vue 3 ist der Laufzeit-Kern plattformunabhängig, wodurch es noch einfacher wird, diese Technologie mit jeder Art von Plattform zu verwenden.

Wie du siehst, lohnt es sich mal anzuschauen, welche Neuerungen es bei Vue geben wird. Werfen wir nun einen Blick auf die wichtigsten Features von Vue 3.

Composition API

Bisher griffen wir beim Erstellen neuer Komponenten auf die Options API zurück. Diese API zwang uns, den Code der Komponente durch Optionen zu trennen, was bedeutete, dass wir alle reaktiven Daten an einer Stelle (Data), alle berechneten Eigenschaften an einer Stelle (Computed), alle Methoden an einer Stelle (Methods) usw. haben mussten.

Obwohl es zwar für kleinere Komponenten handlich und lesbar ist, wird es jedoch schmerzhaft, wenn die Komponente komplexer wird und mit mehreren Funktionalitäten zu tun hat. Gewöhnlich enthält die Logik, die sich auf eine bestimmte Funktionalität bezieht, einige reaktive Daten, berechnete Eigenschaften, eine Methode oder einige wenige davon; manchmal beinhaltet sie auch die Verwendung von Component-Lifecylcle-Hooks. Das führt dazu, dass man bei der Arbeit an einem einzigen logischen Anliegen ständig zwischen verschiedenen Optionen im Code hin- und herspringen muss.

Das andere Problem, auf das man bei der Arbeit mit Vue gestoßen sein könnte, ist die Frage, wie man eine gemeinsame Logik extrahieren kann, die von mehreren Komponenten wiederverwendet werden kann. Vue hat bereits wenige Optionen, um dies zu tun, aber alle haben ihre eigenen Nachteile (z.B. Mixins und Scoped-Slots).

Die Lösung von Vue 3 hierfür ist eine setup()-Methode, die es uns ermöglicht, die Kompositionssyntax zu verwenden. Jedes Stück Logik ist außerhalb dieser Methode als Kompositionsfunktion definiert.

Codebeispiel aus einem Color-Picking-Spiel unter Nutzung der Komposition API:

<script>
import Vue from "vue";
import ColorChoiceButton from "@/components/ColorChoiceButton";
import GameControlHeader from "@/components/GameControlHeader";
import { reactive } from "vue";
import { getRandomColor } from "@/utils/getRandomColor";
 
export default {
  components: {
    GameControlHeader,
    ColorChoiceButton
  },
  setup() {
    const score = reactive({
      lost: 0,
      won: 0
    });
 
    const colors = reactive({
      picked: "",
      right: "",
      choices: ["", "", ""]
    });
 
    function pickColor(color = "") {
      colors.picked = color;
      if (colors.picked === colors.right) {
        //round won
        score.won++;
      } else {
        //round lost
        score.lost++;
      }
    }
    function setFreshColors() {
      colors.choices[0] = getRandomColor();
      colors.choices[1] = getRandomColor();
      colors.choices[2] = getRandomColor();
 
      // we should detect the rare case that we get the same color twice and generate colors again
 
      const randomChoice = Math.round(Math.random() * 2);
      colors.right = colors.choices[randomChoice];
      colors.picked = "";
    }
    function resetGame() {
      score.won = 0;
      score.lost = 0;
      setFreshColors();
    } //new colors, score = 0
 
    resetGame();
 
    return { pickColor, resetGame, setFreshColors, score, colors };
  }
};
</script>

Kurz gesagt, es handelt sich lediglich nur um eine Funktion, die Properties und Funktionen an das Template zurückgibt. Wir deklarieren hier alle reaktiven Properties, computed Properties, Watchers und Lifecycle-Hooks und geben sie dann zurück, damit sie im Template verwendet werden können.

Die Composition API ist eine großartige Möglichkeit, Code lesbarer und wartbarer zu machen. Sie wird dazu beitragen, dass größere Vue-Projekte modularer und wiederverwendbarer sind; ein sehr vielversprechendes Zeichen für die entwicklerfreundlichen Änderungen, die das Vue-Team vornimmt. Es scheint, als hätten sie viele Schmerzpunkte der Entwicklerteams entdeckt und versucht, praktikable Lösungen anzubieten, ohne dabei extrem drastische Änderungen vorzunehmen.

Mehrere Root Elemente

In Vue 2 kann das Template-Tag nur ein Wurzelelement aufnehmen. Selbst wenn wir nur zwei <p>-Tags hätten, müssten wir sie in ein <div>-Tag einschließen, damit es funktioniert. Aus diesem Grund mussten wir auch den CSS-Code in der übergeordneten Komponente so ändern, dass er wie erwartet aussah.

In Vue 3 wird diese Einschränkung aufgehoben. Es wird kein Root-Element mehr benötigt.

Wir können eine beliebige Anzahl von Tags direkt innerhalb der Sektion <template></template> verwenden:

<template>
  <p> Count: {{ count }} </p>
  <button @click="increment"> Increment </button>
  <button @click="decrement"> Decrement</button>
</template>

Suspense

Suspense ist eine Komponente, die während des Lazy-Loadings benötigt wird, das im Wesentlichen dazu dient,  Lazy-Komponenten zu umhüllen. Mehrere Lazy-Komponenten können mit der Suspense-Komponente umwickelt werden. In der Version 3 von Vue JS wird Suspense eingeführt, um auf verschachtelte asynchrone Abhängigkeiten in einem verschachtelten Baum zu warten, und es funktioniert gut mit asynchronen Komponenten. Anstelle unserer Komponente wird ein Fallback-Content angezeigt, bis eine gewünschte Bedingung erfüllt ist. Die Bedingung ist in der Regel ein asynchroner Vorgang, der innerhalb unserer Komponenten-Setup-Funktion stattfindet.

Beispiel:

<Suspense>
  <template >
    <suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

Portals

Die <teleport to=”target”>-Komponente verschiebt alle Children zum ersten durch target beschriebenen Element im DOM-Tree. Dabei funktioniert target wie ein herkömmlicher Query-Selektor, das heißt, ein Element kann über die ID (#target), die Klasse (.target) oder den Tag-Namen (z.B. main) angewählt werden.

Mit Teleports können sich vor den Content stellende Elemente wie Modals, (Cookie) Banners, Pop Ups etc. von überall aus in der App aufgerufen werden, rendern aber stets an derselben Stellen im DOM-Tree.

App.vue

<Component>
  <teleport to="#target">
    <div>...</div>
  </teleport>
</Component>

index.html

<body>
  ...
  <div id="app"></div>
  <div id="portal-target"></div>
</body>

Multiple V-Models

Wenn du Vue verwendest, weißt du bereits, dass v-models für die bidirektionale Datenbindung von Vue-Komponenten verwendet werden. In Vue 2 erhältst du ein v-model für eine Komponente, aber in dieser neuen Version gibt es tolle Neuigkeiten!

Du kannst jetzt so viele v-model-Deklarationen und -Bindungen pro Komponente haben wie du willst und die einzelnen v-models auch benennen.

So etwas ist jetzt möglich:

<InviteeForm
   v-model:name="inviteeName"
   v-model:email="inviteeEmail"
/>

Global mounting/configuration API Änderung

Wir können eine weitere wichtige Änderung in der Art und Weise finden, wie wir unsere Anwendung instanziieren und konfigurieren. Derzeit verwenden wir das globale Vue-Objekt, um jede beliebige Konfiguration bereitzustellen und neue Vue-Instanzen zu erstellen. Jede am Vue-Objekt vorgenommene Änderung wirkt sich auf jede Vue-Instanz und Komponente aus.

Schauen wir uns nun an, wie es in Vue 3 funktionieren wird:

import { createApp } from 'vue'
import App from './App.vue'
 
const app = createApp(App)
 
app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
 
app.mount('#app')

Fazit

Abgesehen von der Composition API, welche die größte neue API in Vue 3 ist, können wir auch eine Menge kleinerer Verbesserungen finden. Wir können sehen, dass sich Vue in Richtung einer besseren Erfahrung für Entwickler und einfachere, intuitivere APIs bewegt. Es ist auch großartig zu sehen, dass das Vue-Team beschlossen hat, viele Ideen, die derzeit nur über Bibliotheken von Drittanbietern verfügbar sind, in den Kern des Frameworks zu übernehmen.

How to: End-To-End-Testing mit Cypress

Jeder der mal mit komponentenbasiertem Programmieren angefangen hat, kennt die Situation. Man schreibt eine Komponente, die auf den ersten Blick funktioniert, dann aber beim Aufruf unerwartete Fehler aufweist. In der Entwicklungsphase ist dies ganz natürlich, aber wenn die Anwendung dem Kunden übergeben bzw. Endnutzer zur Verfügung gestellt wird und es zum Knall kommt, ist es katastrophal für die Reputation der eigenen Firma.

Jetzt sitzt du da und musst mehrere 1000 Elemente testen, die bei jeder Änderung sich gegenseitig beeinflussen. Testvorgänge mehrmals manuell auszuführen ist zeit- und nervenraubend.

Abhilfe schafft das End-to-End Test-Framework Cypress, welches speziell für das Testing von Web-Applikationen entwickelt wurde.

Das Besondere an Cypress ist, dass alles automatisiert abläuft. Manuelles hin her klicken gehört der Vergangenheit an, wenn das Testsetup ordentlich umgesetzt ist.

Eine ausführliche Featurebeschreibung könnt ihr unter: https://docs.cypress.io/guides/overview/why-cypress.html#Our-mission nachlesen

Mit Cypress starten

Cypress installieren

Zuallererst navigieren wir im Terminal in unseren Projektordner und installieren dort anschließend alle nötigen NPM Pakete:

$ cd/your/project/path

Mit npm:

$ npm install cypress --save-dev

Mit yarn:

$ yarn add cypress --dev

Cypress öffnen

Mit npm:

$ npx cypress open

Mit yarn:

$ yarn run cypress open

Cypress Benutzeroberfläche

Das Hauptmenü sollte in etwa so aussehen:

Wie du sehen kannst, liefert das Tool bereits viele fertig geschriebene Beispiele. Um zu prüfen, ob alles funktioniert, kannst du der Einfachheit halber auf ein Bespieltest klicken und es öffnet sich ein neues Fenster, wo der gewählte Test ausgeführt wird.

Der Test-Runner von Cypress öffnet sich und sollte in etwa so aussehen:

Die Ersten Schritte

In diesem Abschnitt werde ich euch eine kleine Zusammenfassung geben, was ihr für den Start wissen solltet.

1. Testdatei erstellen

  • Alle Test-Files kommen in dem Ordner: cypress/intergration
  • Namenkonvention: fileName_spec.js oder fileName.spec.js
  • describe(), it(), context() und specify() beinhalten die auszuführende Testbefehle und ihr könnt damit auch euer Test strukturieren und ein Name geben.

Dateistruktur an einem Bespiel:

// -- Start: Our Application Code --
function add (a, b) {
  return a + b
}
// -- End: Our Application Code --

// -- Start: Our Cypress Tests --
describe('Unit test our math functions', function() {
  context('math', function() {
    it('can add numbers', function() {
      expect(add(1, 2)).to.eq(3)
    })
})
// -- End: Our Cypress Tests --

2. Test schreiben

Sobald ihr die Test-File angelegt habt, aktualisiert Cypress automatisch die Testliste und euer Test sollte im Menü auswählbar sein.

In diesem Abschnitt werden die grundlegenden Funktionen gezeigt.

Test Website besuchen: cy.visit()

Diese Funktion überprüft den Link und gibt ein true zurück, falls ein 2XX Status wie 200 zurückkommt. Anderenfalls wird ein false zurückgegeben und der Test schlägt fehl.

describe('My First Test', function() {
  it('Test PHMU Website', function() {
    cy.visit('https://www.phmu.de/')
  })
})
  • Wie ihr sehen könnt werden die Befehle in dem Commando Log als Großbuchstaben angezeigt. Hier in unseren Beispiel VISIT.
  • Ein Klick darauf, wird das angesprochene Element in Funktion visuell angezeigt.
  • Alle Test werden als eine Timeline dargestellt und ihr könnt dadurch die Zeit “zurückspringen” und genau schauen, wann und wo der Test fehlgeschlagen hat.

Elemente abfragen

Im Cypress können wir ein Element aus unsere Test-Website abfragen und herauspicken (Query).

Die gängigen Funktionen dafür sind:

contains(content) – Gibt das DOM-Element zurück, dass den entsprechenden innerText enthält.

describe('My First Test', function() {
  it('Test PHMU Website', function() {
    cy.visit('https://www.phmu.de/')
  })
  it('Test contain PHMU slogan', function() {
    cy.contains('Unser Herz schlägt digital')
  })
})

get(selector) – Gibt das das DOM-Element zurück, dass mit dem Selektor ausgewählt wurde.

describe('My First Test', function() {
  it('Test PHMU Website', function() {
    cy.visit('https://www.phmu.de/')
  })
  it('Test contain PHMU slogan', function() {
    cy.get('img[class=image]')
    //Sucht ein img element mit der CSS-Klasse image
  })
})

Mit Elementen interagieren

Nachdem ihr im vorherigen Schritt die Elemente abgefragt haben, ist es auch möglich mit den Elementen zu interagieren. Falls eine Interaktion mit einem Element nicht möglich ist, wird der Test fehlgeschlagen.

type()

describe('My First Test', function() {
  it('Test contact formular', function() {
    cy.visit('https://www.phmu.de/workshops')
  })
  it('Test firstname input', function() {
    cy.get('input[id="firstname"]').type('Tung')
  })
})

click()

describe('My First Test', function() {
  it('Test contact formular', function() {
    cy.visit('https://www.phmu.de/workshops')
  })
  it('Test firstname input', function() {
    cy.get('button[id="submit"]').click()
  })
})

select()

describe('My First Test', function() {
  it('Test contact formular', function() {
    cy.visit('https://www.phmu.de/workshops')
  })
  it('Test firstname input', function() {
    cy.get('select[id="title"]').select("Doctor")
  })
})

Assertion

Mit should() kannst du überprüfen ob ein Element das ist, welches du erwartest. Als Beispiel möchten wir sicherstellen, dass die aktuelle URL die erwartete URL ist.

describe('My First Test', function() {
  it('Test PHMU Website', function() {
    cy.visit('https://www.phmu.de/')
  })
  it('Test workshop link', function() {
    cy.get(a[href="/workshop"]).click
    cy.url().should('include', '/workshop')
  })
})

In diesem Blog habe ich euch das nötigste mitgegeben, um euch auf Cypress aufmerksam zu machen. Es ist auch möglich Serveranfragen zu testen und zu validieren oder das Terminal auszulesen und nach Fehlern zu testen. Cypress ist sehr umfangreich und bietet viele Möglichkeit, sein Web-Interface auf Herz und Nieren zu testen..

Wie ich bereits erwähnt habe, empfehle ich euch wärmsten die Dokumentation. Diese beinhaltet neben der guten Dokumentation auch Best Practices für ein effektives Testing von Web-Applikation.

Links

Tailwind CSS mit gulp.js in ein WordPress Theme einbinden

Tailwind CSS ermöglicht als Utility Framework einen unglaublich effizienten Workflow in der Frontend-Entwicklung. Um Tailwind in deinem WordPress Theme nutzen zu können, reicht es zunächst, dieses via CDN einzubinden. Willst du jedoch über die minimale Funktionalität hinaus, so kommst du um ein lokales Setup nicht herum. Wie du das mit WordPress unter einen Hut bringen kannst, beschreiben wir dir in diesem Beitrag.

WP-Gulp und Gulp-PostCSS

Um Tailwind in den WordPress Theme zu integrieren, musst du zuerst WPGulp installieren. WPGulp ist ein Plugin, das die Integration von gulp.js in WordPress erleichtert. Zusätzlich brauchst du das Gulp-Plugin von PostCSS, um später Tailwind verarbeiten zu können:

npx wpgulp
npm install -D gulp-postcss

Nun hast du ein komplettes Gulp-Setup installiert, das aus SCSS-Dateien kompakte CSS-Dateien generiert. In der wpgulp.config.js musst du jetzt die Pfadvariablen an dein Projekt anpassen. Bestimme, wo deine SCSS-Dateien liegen und wo das CSS generiert wird. Da dein Projekt jetzt mit SCSS arbeitet, solltest du jetzt deine Stylesheets anpassen.

Installation von Tailwind CSS

Jetzt sind wir bereit, Tailwind in unser Theme zu integrieren:

npm install tailwindcss

Füge dann Tailwind deiner primären SCSS-Datei hinzu:

@tailwind base;
@tailwind components;
@tailwind utilities;

Wenn du Tailwind individuell anpassen und erweitern möchtest, kannst du eine Konfigurationsdatei generieren lassen:

npx tailwind init

In der dadurch erstellten tailwind.config.js kannst du Tailwind jetzt nach Belieben anpassen. Mehr dazu auf  https://tailwindcss.com/docs/configuration.

Integration von Tailwind CSS in gulp

Jetzt haben wir alles, was wir brauchen, um Tailwind in Gulp zu konfigurieren. Dazu gehen wir in die gulpfile.babel.js. Hier fügen wir zuerst PostCSS und Tailwind den CSS-Plugins hinzu:

// CSS related plugins
// ...
const postcss = require( ‘gulp-postcss’ );
const tailwindcss = require( ‘tailwindcss’ );
// ...

Die beiden Plugins sind jetzt in unserer Gulp-Datei als Konstanten importiert. Nun wollen wir, dass Gulp diese auch verwendet. Die Gulp-Tasks styles und stylesRTL generieren die finalen CSS-Dateien und RTL-Stylesheets (Right To Left). Hier fügen wir PostCSS und Tailwind ein:

gulp.task( ‘styles’. () => {
  return gulp
    .src( config.stlyeSRC, { allowEmpty: true })
    // ...
    .pipe( postcss([
      tailwindcss( ‘./path/to/your/tailwind.config.js’ ),
      require( ‘autoprefixer’ )
    ]) )
    // ...
});
// ...
gulp.task( ‘stylesRTL’. () => {
  return gulp
    .src( config.stlyeSRC, { allowEmpty: true })
    // ...
    .pipe( postcss([
      tailwindcss( ‘./path/to/your/tailwind.config.js’ ),
      require( ‘autoprefixer’ )
    ]) )
    // ...
});
// ...

Ist das erledigt, können wir testen, ob unser Setup funktioniert. Dazu kannst du einfach in einer deiner PHP-Dateien einem HTML-Element eine beliebige Tailwind-Klasse wie bg-red-500 hinzufügen.

Führe nun npm start aus. Wurden deine Styles erfolgreich generiert, kannst du auf deiner Seite schauen, ob die Tailwind-Styles angezeigt werden. Funktioniert? Glückwunsch, du hast erfolgreich Tailwind CSS deinem WordPress-Theme hinzugefügt! Jetzt kannst du damit anfangen, deine tailwind.config.js individuell anzupassen.

Links:

Mit Netlify Functions Emails versenden

Sagen wir du hast deine erste moderne statische Website (JAMStack) gebaut und willst sie dynamischer gestalten wie z.B durch das Versenden von E-Mails mit dynamischen Inhalten und Dateianhang. Dafür willst du aber keinen eigenen Server aufsetzen bzw. nutzen. Wie das gelingt, erklären wir in diesem Post.

Was benötigt man?

1. Mailing Service

Als allererstes brauchst du einen Mailing Service, der deine Emails versendet. Wichtig ist, dass du Emails nicht von deinem eigenen Emailserver senden solltest, sondern immer auf einen größeren Dienstleister zurückgreifen solltest um beispielsweise der Gefahr zu entgehen, dass du als Spamversender eingestuft wirst. Um eine Mail zu versenden, sprichst du die Service API mit einem API Key an und übergibst alle Daten, die in der Mail enthalten sein sollen und der Mailing Service verschickt sie dann an die Zieladresse.

Es gibt viele Services zur Auswahl und du solltest jenen nehmen, der von den Features und dem Preis her am besten zu dir passt. Da ich bei meinem letzten Projekt gerne mit POSTMARK gearbeitet habe, werde ich in diesem Post vorstellen, wie man diesen Dienst benutzt.

2. Netlify

Als Nächstes brauchen wir eine kleine Menge serverseitigen Code, um dem API Key beim API Request zu schützen. Dafür eignen sich Lambda Funktionen bestens. Man ruft einen URL Endpoint auf und beim Aufruf wird ein vorgegebener Code ausgeführt. Netlify unterstützt dies, also wäre es eine gute Idee deine Seite darauf zu hosten.

Schritt 1: Mail Service einrichten

POSTMARK einrichten

Als Erstes melden wir uns bei Postmark an und gehen alle Schritte durch, bis wir zum Dashboard kommen und Zugriff auf den API Token haben.

API Key für Netlify zur Verfügung stellen

Um den API Key zu schützen, sollte der Key nur beim Ausführen serverseitigen Codes zur Verfügung gestellt werden. Im Netlify müssen wir deshalb unter “Environment” den Key einer Variable zuweisen.

Schritt 2: Functions schreiben

Wie erstellen wir eine Netlify Function? Ganz einfach. In deinem Projekt Ordner legst du ein Ordner mit dem Namen “functions” an. In diesem legst du dann alle JavaScript Dateien an, die du benötigst. In unserem Fall legen wir die Datei send-email.js an.

/project
   ... your entire website or whatever ...
   /functions/
         send-email.js

Postmark Node Library benutzen

Postmark stellt eine eigene Node.js Bibliothek zur Verfügung und mit einem Blick auf die Dokumentation entdecken wir gleich am Anfang ein einfaches Codebeispiel zum Testen.

const serverToken = "xxxx-xxxxx-xxxx-xxxxx-xxxxxx" //API Key
let postmark = require("postmark")
let client = new postmark.ServerClient(serverToken);

exports.handler = (event, context, callback) => {
client.sendEmail(
    {
        From: "from@example.com", //Deine Emailadresse
        To: "to@example.com", //Ziel Emailadresse
        Subject: "test email",
        HtmlBody: "test",
        TextBody: "test"
    }
).then(response => {
    console.log(response.To);
    console.log(response.SubmittedAt);
    console.log(response.Message);
    console.log(response.MessageID);
    console.log(response.ErrorCode);
});
}

Wenn du mit der AWS Lambda Funktionen noch nicht so vertraut bist oder nicht kennst – No Problem! Ich gebe dir einen kleinen Überblick über die einzelnen Parameter, die unser handler erhält.

  • event is an object that contains data on the request
  • context contains the user information when using Identity for user authentication
  • callback is a function we can use to create a response

Mit der callback() Methode können wir testen, ob unsere Anfrage bei der Mailing Service API angekommen ist.

  callback(null, {
    statusCode: 200,
    body: 'I am on my way !'
  })
}

Schritt 3: Testen

Netlify Dev

Nun haben wir unsere erste Netlify Funktion geschrieben und möchten unser Werk testen. Dafür gibt es ein kleines Tool namens Netlify Dev. Damit können wir einen dev Server simulieren, der uns eine lokale URL ausgibt mit denen wir unseren Funktionen in der Entwicklungsumgebung ausführen und testen können.

Nach der Installation und Ausführung sollte es ungefähr so aussehen:

Testen der Funktion

Um unsere Lambda Funktion aufzurufen, müssen wir sie mit eine URL, wie die unten ausgeführte, ansprechen:

http://localhost:8888/.netlify/functions/send-email

Wenn alles geklappt hat, wird sich der Empfänger über deine erste E-Mail freuen 🙂

Schritt 4: Email mit Daten befüllen

Glückwunsch, nun hast du deine erste E-Mail mit der Netlify Function verschickt. Leider ist der Inhalt noch statisch und langweilig. Um deiner Mail mehr Leben einzuhauchen, müssen wir den Code dynamischer machen.

Sagen wir du willst mehrere Glückwünsche per E-Mail an Freunde versenden und eine Textdatei mit einer persönliche Nachricht als Anhang hinzufügen.

Alle Daten befüllst du mit einem Formular und diese werden als ein Objekt gespeichert z.B.:

const congratulation = {
  targetEmail: "test@gmail.com", 
  reason: "Geburtstag",
  firstname: "Peter",
  message: "Alles Gute zum Geburtstag. Es freut mich das du erwachsen 
           wirst"
}

Nun müssen diese Daten beim Versenden an die URL der send-mail.js Funktion übergeben werden. Dazu nutzen wir eine HTTP Request mit der Funktion URL als Zieladresse mit den Formulardaten als payload.

axios({
          method: 'POST',
          headers: { 'content-type': 'application/json' },
          url: '/.netlify/functions/send-email',
          data: congratulation, //payload
      })

Als nächstes müssen wir in unserer send-mail.js Funktion noch den Inhalt der E-Mail mit den Daten aus dem payload befüllen. Es empfiehlt sich beim Entwickeln console.log(event) zu benutzen, um sich alle Daten aus dem Request im Terminal anzeigen zulassen

Um einen Anhang hinzuzufügen, braucht man nur einen Blick auf die Postmark Node.js Dokumentation zu werfen.

const attachment = new 
                    postmark.Models.Attachment("Glückwunschtext.txt", 
                    Buffer.from("payload.message").toString("base64"), 
                    "text");

client.sendEmail(
    {
        From: "from@example.com", //Deine Emailadresse
        To: "to@example.com", //Ziel Emailadresse
        Subject: "test email",
        HtmlBody: "test",
        TextBody: "test"
        Attachments: [attachment],
    })

Die fertig angepasste Funktion sieht dann am Ende so aus.

const serverToken = "xxxx-xxxxx-xxxx-xxxxx-xxxxxx" //API Key
let postmark = require("postmark")
let client = new postmark.ServerClient(serverToken);

exports.handler = (event, callback) => {
console.log(event) 
const payload = JSON.parse(event.body)

 const attachment = new 
                    postmark.Models.Attachment("Glückwunschtext.txt", 
                    Buffer.from("payload.message").toString("base64"), 
                    "text");

client.sendEmail(
    {
        From: "tung@phmu.de", //Deine Emailadresse
        To: payload.targetEmail, //Ziel Emailadresse
        Subject: payload.reason, 
        TextBody: `Liebe ${payload.firstname}, 
                  du hast eine Glückwunschemail erhalten`,
        Attachments: [attachment],
    })
} 

Der Inhalt der verschickten Emails sollte dann so aussehen:

Von: tung@phmu.de
An: test@gmail.com
Betreff: Geburtstag

Anhang: Glückwunschtext.txt

Nachricht: 
Liebe Peter, 
du hast eine Glückwunschemail erhalten             

Wie ihr seht, ist das Versenden von E-Mails mit Netlify Functions gar nicht so kompliziert. Man könnte jetzt die E-Mails noch fancier machen wie z. B. HTML Code hinzufügen und… Aber das werde ich dir überlassen 😉

Dark Mode: Warum Websites das neue Feature unterstützen sollten

Der Dark Mode für digitale Interfaces ist ein ziemlich heißes Thema. Erst kürzlich stellte Microsoft Office seine Suite auf Dark Mode Kompatibilität um. Sogar ein eigener Trailer wurde dafür produziert. Mit den neuen Betriebssystem iOS und iPad OS Versionen von Apple hält der Dark Mode auch Einzug auf die mobilen Endgeräte der Apple Nutzer. Android kann dies bereits etwas länger. Der Verbreitung des Features wird es jedoch neuen Schub verleihen.

Durch die größere Verbreitung wird es Zeit über die Anpassung der eigenen Website und Webapps nachzudenken.

Website Nutzer im Dark Modus nicht mit Licht fluten

Aus eigener Erfahrung kann ich sagen, wie schnell man sich an den Dark Mode beim Mac OS gewöhnen kann. Ich selbst nutze One Switch – Fireball Studio als App, die den Dark Mode mit Sonnenuntergang aktiviert. So ist das Arbeiten in lichtärmeren Umgebungen für die Augen angenehmer.

Viele native Programme wie Mail, Kalender und auch Code Editoren unterstützen den Dark Mode bereits komplett. Im Web sieht das Bild jedoch anders aus. Sobald man in den Browser wechselt, werden Nutzer relativ schnell wieder mit hellen Sites geflutet. Das Erlebnis kann dann schnell anstrengend werden. Es gibt bereits einige Browser Plugins, die versuchen für alle Websites einen Dark Mode zu generieren. Jedoch haben Unternehmen hier keinen Einfluss auf die Farbgestaltungen und das Corporate Design. Besser wäre es daher von Haus aus, die Unterstützung des Dark Modes einzubauen, um die volle Kontrolle der Darstellung zu haben.

Aktuelle Browser Versionen wie Chrome, Safari und Firefox unterstützen den Dark Mode bereits und können auch an Websites, die Info zum Licht abschalten weitergeben. Jedoch muss der Support auch innerhalb der Website implementiert sein.

Wir haben mit PHMU.de den Dark Mode implementiert und erhoffen uns so ein angenehmeres Surfen bei Nutzern und die Schonung der Augen.

Wie du Tailwind CSS im Vue.js-Projekt nutzen kannst

Du hast erfolgreich ein Vue-Projekt gestartet (wie das geht haben wir hier aufgeschrieben) und möchtest nun TailwindCSS als Utility-Framework zur Entwicklung nutzen? Dann zeigen wir dir im folgenden Beitrag, wie du das relativ einfach hin bekommst.

Zuerst musst du die notwendigen Packages installieren:

npm install tailwindcss autoprefixer
-- ODER --
yarn add tailwindcss autoprefixer

Dann kannst du mit dem folgenden Command das tailwindcss.config.js file in deinem Projekt erstellen:

npx tailwind init

Ist das erledigt, kannst du unter assets einen neuen Ordner /css erstellen und dort die Datei tailwind.css anlegen. Hier fügst du folgende Imports hinzu:

@tailwind base;
@tailwind components;
@tailwind utilities;

Nun müssen wir der Vue-App mitteilen, dass die CSS-Styles genutzt werden sollen. Dafür importieren wir unsere neue tailwind.css in die App.vue.

import Vue from 'vue';
import App from './App.vue';

import './assets/styles/tailwind.css';

Im letzten Schritt passen wir unser postcss.config.js an. So geben wir an, dass wir beide Plugins nutzen wollen.

module.exports = {
  plugins: [
    require('tailwindcss'),
    require('autoprefixer'),
  ]
}

Ist das erledigt, können wir prüfen, ob alles funktioniert. Füge am besten eine Tailwind-CSS-Klasse wie bg-red-500 einem div in App.vue hinzu und schaue ob du die Änderung nach dem Livereload siehst. Passt alles? Perfekt, dann hast du erfolgreich TailwindCSS zu deinem Projekt hinzugefügt.

Bundle-Größe optimieren

Tailwind vergrößert dein CSS immens. Wenn du also nun npm run build ausführst solltest du die Veränderung sehen. Über 679 KiB ist das CSS nun groß. Das bekommen wir jedoch relativ einfach reduziert.

File                                      Size             Gzipped

dist/js/chunk-vendors.9080b304.js         119.90 KiB       41.68 KiB
dist/js/app.d8b6098b.js                   7.19 KiB         2.58 KiB
dist/service-worker.js                    0.96 KiB         0.54 KiB
dist/js/about.edf670de.js                 0.44 KiB         0.31 KiB
dist/css/app.6504396d.css                 679.39 KiB       85.10 KiB

Um nicht genutzte Tailwind-CSS-Klassen aus deinem Bundle zu entfernen, nutzen wir PurgeCSS in Verbindung mit PostCSS. Dazu füge zuerst das notwendige Package dem Projekt hinzu:

npm install purgecss  @fullhuman/postcss-purgecss

Ist die Installation abgeschlossen, müssen wir in der PostCSS Config folgende Änderung vornehmen:

const purgecss = require(“@fullhuman/postcss-purgecss”);

module.exports = {
    plugins: [
      require(‘tailwindcss’),
      require(‘autoprefixer’),

    // Only add purgecss in production
    process.env.NODE_ENV === 'production'
      ? purgecss({
          content: ['./src/**/*.html', './src/**/*.vue'],
        })
      : '',
  ],
}

Wenn wir nun npm run build erneut starten, sollten wir bereits drastische Verbesserungen bei der CSS-Datei erkennen.

File                                      Size             Gzipped

dist/js/chunk-vendors.9080b304.js         119.90 KiB       41.68 KiB
dist/js/app.d8b6098b.js                   7.19 KiB         2.58 KiB
dist/service-worker.js                    0.96 KiB         0.55 KiB
dist/js/about.edf670de.js                 0.44 KiB         0.31 KiB
dist/css/app.69044861.css                 2.14 KiB         0.81 KiB

2.14 KiB damit lässt sich starten!

Links:

Setup eines VueJS Projektes mit der Vue CLI

Um ein neues VueJS Projekt zu starten gibt es verschiedene Wege. Der einfachste und angenehmste Weg ist das Setup mithilfe der Vue CLI. Um die CLI nutzen zu können, müssen zuerst die notwendigen NPM Module global installiert werden. Dazu kann folgender Command im Terminal ausgeführt werden:

npm install -g @vue/cli

#oder 

yarn global add @vue/cli 

Läuft die Installation erfolgreich durch, steht danach ein neuer Command im Terminal zur Verfügung. Mit vue -v lässt sich beispielsweise prüfen, welchen Version der Vue CLI nun installiert ist.

Das Projekt Setup über die CLI

Um ein Projektsetup zu starten kann man nun mithilfe von vue create + den gewünschten Projektname den Setup-Prozess über das Terminal starten.

vue create vue-setup

Nach dem Command startet ein kleiner Wizard, der durch das Setup führt. Zunächst wird man gefragt, ob man die Konfiguration manuell durchführen oder das Standard-Setup das nur Babel und ESLint beinhaltet, nutzen möchte. Wir wählen zunächst manuell mit den Pfeiltasten und anschließender Leertaste um zu sehen, welche Möglichkeiten angeboten werden.

Im manuellen Modus erscheinen nun alle Optionen und Packages die genutzt werden können. Beispielsweise kann ich relativ schnell und simpel den Vue Router meinem Projekt dadurch hinzufügen. Aber auch das Setup für Unit- und End-to-End Testing lässt sich anwählen.

Nachdem die gewünschte Auswahl vorgenommen wurde, lässt sich die Installation des Projektes und der nötigen Packages starten.

Mithilfe der CLI UI ein visuelles Projektsetup

Sollte man nicht zu den Terminal-Liebhabern gehören, lässt sich mithilfe eines modernen Interfaces ebenso ein Projektsetup durchführen. Dazu startet man im Terminal zunächst die UI mit:

vue ui

Dadurch öffnet sich auf Port 8000 das Vue UI Interface. Über Project Create startet man den bereits aus der CLI gewohnten Prozess beim Initialisieren eines neuen Projektes.

Zunächst lässt sich über der Speicherort aussuchen und der Projektname angeben. Ebenso kann zwischen npm oder yarn als Standard Package Manager gewählt werden. Eine Festlegung ist aber kein Muss. Sind die Einstellung getroffen, wird auch hier der Wizard Prozess gestartet.

Auch über die UI kann ein manuelles Setup durchgeführt werden, wenn zum Beispiel schon während des Setups der Vue Router oder Vuex hinzugefügt werden sollen.

Nach dem erfolgreichen Setup ist man startklar und kann sein Vue Projekt weiterentwickeln.

Real-Time Preview für Static Sites mit Gatsby und sanity.io

Moderne statische Webseiten, auch JAMStack Webseiten genannt, bieten eine Reihe von Vorteilen in den Bereichen Sicherheit, Geschwindigkeit und Zuverlässigkeit. Wo sie aber einem voll integriertem CMS wie WordPress noch nachstehen, ist die Möglichkeit für Autoren, eine Vorschau von Inhalten zu sehen bevor sie diese veröffentlichen. Da die Webseite nach jeder Änderung neu gebaut werden muss, wird der wertvolle schnelle Feedback Zyklus gebrochen.

Wir haben in verschieden Projekten gute Erfahrungen mit Gatsby als Framework für statische Webseiten gemacht. Bei der Arbeit mit Gatsby ist es für Entwickler möglich, mit schnellen Feedback Zyklen zu arbeiten, da Gatsby einen Development Server bereitstellt, der Hot Module Reloading (-> laufender Code wird aktualisiert ohne das die Seite neu geladen wird) unterstützt. In einem aktuellen Projekt haben wir es geschafft, den gleichen Workflow auch für Autoren zu ermöglichen.

Basis dafür ist das Headless CMS sanity.io, dass Contentupdates in Echtzeit über Websockets zur Verfügung stellt. Das Team von sanity.io hat ein Gatsby Source Plugin entwickelt, das den Gatsby Development Server um einen Listener erweitert der auf neue Contentupdates lauscht und diese ohne neu zu laden direkt in die Seite integriert. Damit Autoren keinen lokalen Development Server auszuführen müssen, kann dieser in der Cloud laufen. Dadurch wird nur ein Browser benötigt, um CMS und Preview zu nutzen.


Diese Preview geht durchaus weiter als es z.B. mit WordPress möglich ist:

  • Alle Eingaben im Sanity Studio werden in Echtzeit angezeigt, ohne das ein Speichern nötig ist. Das gilt auch für andere Datentypen, wie z.B. die Auswahl von Farben mit einem Color Picker.
  • Die Vorschau ist für die gesamte Webseite. So können nicht nur einzelne Draft Posts angezeigt werden, sondern auch Menüs oder Übersichtsseiten, die die bearbeiteten Inhalte enthalten. Außerdem können globale Änderungen wie z.B. Akzentfarben ausprobiert werden

Und so gehts:

Du brauchst:

1. Gatsby + Sanity

Damit Gatsby auf Content in deinem Sanity Projekt zugreifen kann, musst du das gatsby-source-sanity Paket in deinem Projekt installieren und einrichten. Füge dafür folgende Sektion in das Plugin Array in deiner gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-sanity',
      options: {
          // zu finden auf manage.sanity.io/projects
        projectId: sanityProjectID,
        // wahrscheinlich 'production' oder 'development'
        dataset: datasetID,
        // zu finden auf der Projektseite auf manage.sanity.io unter Settings > API
        token: YOUR_SANITY_TOKEN,
        watchMode: true,
        overlayDrafts: true,
      },
    },
  ],
}

Die interessanten Optionen sind hier watchMode, welche den Listener im Development Server aktiviert, und overlayDrafts, welche Content der noch nicht veröffentlicht ist anzeigt.

2. gatsby develop in der Cloud

Damit auch Nutzer ohne Programmiererfahrung die Preview nutzen können, wollen wir den gatsby develop Prozess, der üblicherweise lokal ausgeführt wird, auf einen Webserver laufen lassen. Dafür muss er mit speziellen Optionen gestartet werden, die den Zugriff von außen ermöglichen. Füge dafür folgendes Script zu deiner package.json hinzu:

"serve-preview": "NODE_ENV=development gatsby develop --host 0.0.0.0 --p $PORT"

Ersetze dabei $PORT mit einem Port deiner Wahl, z.B. 80, wenn du nicht eine Plattform wie Heroku oder Render benutzen willst, die den Port als Variable zur Verfügung stellt.

Um die Preview auszuführen, kannst du einen beliebigen Server mit Node.js benutzen. Kopiere dafür dein Projekt auf den Server, installiere es mit yarn und starte es mit yarn serve-preview.

Eine einfache Alternative ohne Server Setup ist eine PaaS Plattform wie Heroku oder Render. Dafür musst du nur ein Projekt mit deinem Git Repo anlegen und über die UI “Build” (yarn) und “Start” (yarn serve-preview) Kommandos angeben.

3. Preview Links in Sanity

Damit Autoren schnell die Preview für eine bestimmte Seite öffnen können, bietet Sanity die Möglichkeit eine Funktion bereitzustellen die eine Preview URL zu einem Dokument zuordnet. Das kann zum Beispiel so aussehen:

// resolvePreviewURLs.js
const baseURL = 'https://your-preview.herokuapp.com'

export default function resolvePreviewURLs(document) {
  switch (document._type) {
    case 'page': return `${baseURL}/${document.slug.current}`;
    case 'post': return `${baseURL}/post/${document.id}`;
    case 'author': return `${baseURL}/info/people/${document.slug.current}`;
    default: return baseURL;
  }
}

Diese Funktion muss dann mit dem Parts System in sanity.json integriert werden:

{
  "parts": [
    {
      "implements": "part:@sanity/production-preview/resolve-production-url",
      "path": "./resolvePreviewURLs.js"
    }
  ]
}

Außerdem musst du noch das @sanity/production-preview Plugin mit folgendem Kommando installieren:

sanity install @sanity/production-preview

Danach steht im Sanity Studio das Kommando Open Preview (Ctrl + Alt + O) zur Verfügung.

Können wir Sie unterstützen?

Wenn sie auch ein Preview System für ihre statische Webseite brauchen, helfen wir gern weiter. Wir können sie im gesamten Prozess der Entwicklung und Einrichtung moderner JAMStack Webseiten mit komplexen Anforderungen unterstützen.

Kontakt aufnehmen

How-to: Upload direkt aus dem Frontend einer Vue App in ein AWS Bucket mit SignedURLs

Will man in seiner Vue App einen Upload für Nutzer bereitstellen, geht dies meist über das eigene Backend und ein damit verbundenes Daten-Handling. Mithilfe eines AWS Buckets und den SignedURLs ist dies relativ simpel zu erreichen, ohne große Last auf das eigene Backend zu legen.

Ich berichte von meinen Erfahrungen und Learnings bei der Umsetzung dieser Lösung. Vielleicht hilft es dem einen oder anderen Leser etwas schneller ans Ziel zu kommen.

Der Blogpost zeigt, mit welchem Setup der Upload zu AWS eingerichtet werden kann, und man komplett auf einen Backend-Server hierbei verzichten kann. Die Lernkurve bei AWS ist etwas steil, aber sobald das Grundsetup eingerichtet ist, lässt der Upload schnell realisieren.

Das brauchst du zum Starten

Setup deiner AWS Konsole

Schritt 1: Account anlegen

Im ersten Schritt erstellt man ein AWS Konto. AWS fragt bei einem neuen Account immer die Kreditkartendaten ab, obwohl wir später das Setup so einstellen, dass vorerst keine Kosten anfallen. Nach der erfolgreichen Anmeldung, haben wir Zugriff auf die zum Konto gehörenden Keys. Die angezeigten Schlüssel sind die Root-Schlüssel, die wir vorerst aber nicht nutzen werden, da einen IAM User für den Upload erstellen werden. Solltest du später diese Schlüssel benutzen wollen, kannst du dir einfach jederzeit neue Schlüssel unter “My Security Credentials” generieren lassen.

Beachte: Deine Schlüssel solltest du niemals öffentlich teilen, da es wegen des “Pay as you go” Preismodells schnell teuer für dich werden könnte. Auf dem sichersten Weg bist du, wenn du dir die Schlüssel als Variablen in einer .env-Datei in deinem Projekt hinterlegst und diese in deinem Git-Repository ignorierst

Schritt 2: Erstelle einen Bucket

Ist der Account angelegt, kann es mit der Bucket-Erstellung weitergehen. Ein Bucket lässt sich relativ einfach erstellen, indem du einen Name vergibst und alle Genehmigung vorerst deaktivierst. Dies ist wichtig, da sich erst danach die Bucket Policy auf public setzen lässt, um die Bilder für alle erreichbar zu machen.

Erstelle ein öffentliches Bucket (letzte Genehmigung auch ausschalten)

So sollten deine Bucket Policy und die CORS Einstellungen aussehen:

 {
    "Version": "2012-10-17",
    "Id": "public policy example",
    "Statement": [
        {
            "Sid": "Allow get requests",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR_BUCKET/*"
        }
    ]
}
<?xml version="1.0" encoding="UTF-8"?><CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule>    <AllowedOrigin>*</AllowedOrigin>    <AllowedMethod>POST</AllowedMethod>    <AllowedMethod>GET</AllowedMethod>    <AllowedMethod>PUT</AllowedMethod>    <AllowedMethod>DELETE</AllowedMethod>    <AllowedMethod>HEAD</AllowedMethod>    
<AllowedHeader>*</AllowedHeader>
</CORSRule></CORSConfiguration>

Scritt 3: Erstelle einen neuen IAM Benutzer

Haben wir auch das Bucket angelegt, können wir uns nun der Erstellung eines IAM Benutzers widmen. Die richtig Seite für die Erstellung findest du am schnellsten über die Suche innerhalb der AWS Konsole.

Der neue Nutzer sollte nur Zugriff zum Get-, Put- und DeleteObject haben. Auch hier spielt die Sicherheit eine große Rolle. Sollten einmal Unbefugte Zugriff zu dem Schlüssel bekommen, haben diese dennoch keinen Vollzugriff auf das Bucket. Es empfiehlt sich deshalb eine neue Policy zu erstellen.

Hier ist ein Beispiel für eine Policy. Alle Schritte zum Erstellen eines neuen IAM Nutzers findest du in folgender Liste:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::YOUR_BUCKET/*"
        }
    ]
}
  • Navigiere zum User – der Link ist in der Sidebar, klicke auf Add user
  • Vergib einen Name eingeben und und wählen Programmatic access Dies erstellt einen eigenen Zugriffsschlüssel für den Nutzer, den wir später beim Upload nutzen.
  • Wähle “Attach existing policies directly” aus, um eine neue Policy unter “Create policy” erstellen zu können. Der Button wird einen neuen Tab öffnen.
  • Füge hier unter JSON einfach den Code von oben ein und klick “review Policy”. Notiz: Statt YOUR_BUCKET muss man natürlich den Name seines eigenen Buckets benutzen.
  • Vergib einen Namen für die Policy, und schließe den Tab mit “create policy”
  • Aktualisiere die Policies und wähle die neuerstellte Policy aus
  • Klicke “Next: Tags”, die du aber übergehen kannst, da wir Tags nicht brauchen
  • Nach der Erstellung des Users kannst du den Schlüssel einsehen. Man kann entweder den Text kopieren oder eine CSV Datei herunterladen. Diesen Schlüssel werden wir in der nächsten Schritt brauchen!

Das AWS JS SDK

Nach dem erfolgreichem Setup unserer AWS Konsole können wir endlich mit Coding anfangen. Ich habe die AWS Methoden aus Gründen der Wiederverwendbarkeit getrennt in einem aws.js File untergebracht.

Schritt 1: Erstellen einer S3 Instance

Zu allerst wollen wir die aws-sdk Bibliothek zu unserem Projekt hinzuzufügen, um die AWS Methode zu benutzenzu können. Zusätzlich für Requests nutzen wir auch das axios Package.

Installiere aws-sdk und axios in deinem Projekt yarn:

yarn add aws-sdk axios

Sobald die Installation erfolgreich war, können wir mit der Initialisierung einer S3-Instanz starten. Wie du siehst nutzen wir unsere Zugriffsschlüssel aus der .env-Datei sowie eine Regionsvariable. Mit new aws.S3() lässt sich eine S3-Instanz intialisieren. Wie du siehst habe ich auch die Option signatureVersion angeben, um Dateien zu dem Server hochladen zu können. Wenn du einen amerikanischen Server benutzt, kannst du dir diese Option sparen.

const aws = require('aws-sdk')

aws.config.update({
  secretAccessKey: process.env.VUE_APP_AWS_SECRET_ACCESS_KEY,
  accessKeyId: process.env.VUE_APP_AWS_ACCESS_KEY,
})

export const s3 = new aws.S3({
  signatureVersion: 'v4',
  region: process.env.VUE_APP_AWS_REGION,
})

Notiz: wenn du das Code kopierst, vergiss nicht in deinen .env Variablen VUE_APP_AWS_ACCESS_KEY, VUE_APP_AWS_SECRET_ACCESS_KEY und VUE_APP_AWS_REGION definieren.

Schritt 2: SignedURL

Wenn die Konfiguration abgeschlossen ist, können wir unsere singleUpload Methode mit signedURL anlegen. Ein kurzer Ausflug zum Thema signedURLs und warum es gut ist sie zu benutzen?

  • Die signedURL kann nur für einzelne Datei-Uploads benutzt werden – der Benutzer kann das Bucket nicht ungewollt weiter befüllen
  • Die signedURL verschlüsselt den Filename und Filetype – der Benutzer kann nur die jeweils angemeldete Datei hochladen
  • Die signedURL ist zeitlich beschränkt – dies schützt vor Exploits z.B.: Benutzer mit bösen Absichten, die versuchen, die signedURL von einem anderen Benutzer zu benutzen
  • Die signedURL ist von der Vue.js App generiert und lässt sich nicht selbst erstellen
  • Die signedURL funktioniert nur mit dem festgelegtem Bucket – Benutzer können kein andere Bucket sehen oder darauf zugreifen
export const singleUpload = (file, folder) => {
  const key = folder + '/' + Date.now() + '-' + file.name.replace(/\s/g, '-')
  const params = {
    Bucket: process.env.VUE_APP_AWS_BUCKET,
    Key: key,
    Expires: 10,
    ContentType: file.type,
  }
  const url = s3.getSignedUrl('putObject', params)
  return axios
    .put(url, file, {
      headers: {
        'Content-Type': file.type,
      },
    })
    .then(result => {
      const bucketUrl = decodeURIComponent(result.request.responseURL).split(
        key
      )[0]
      result.key = key
      result.fullPath = bucketUrl + key
      return result
    })
    .catch(err => {
      // TODO: error handling
      console.log(err)
    })
}

In Zeile 2 generieren wir einen spezifischen Dateinamen bei AWS als Key bezeichnet. Zusätzlich muss der Dateiname auch den Ordner enthalten in dem die Datei liegen soll, beispielsweise ein Album oder Team. Wir können den Ordnernamen mit Schrägstrich abgrenzen. Um einen einzigartigen Dateinamen zu generieren nutzen wir Date.now(). Die replace Methode ersetzt die Whitespaces gegen einen Bindestrich (-). Es wäre sogar möglich nur mit Date.now() zu arbeiten. Dies liegt bei dir, welche Struktur du in deinem Bucket aufbauen möchtest.

Wie ich oben schon erwähnt habe, beschränkt das “Expires” Attribut die URL zeitlich. Wenn du mehr über getSignedUrl erfahren willst, klicke auf dem Link.

Sobald die Datei hochgeladen ist, erhalten wir den Key und den Link zur Datei, diese geben wir zurück, um sie beispielsweise in unserer Datenbank mit abzulegen.

Schritt 3: Löschen die Datei

Das Löschen einer hochgeladenen Datei lässt sich ebenso einfach umsetzen. Man braucht nur das Bucket und den Name der Datei. Wenn man mehrere Buckets benutzt, dann speichert man lieber den Bucketnamen auch in der Datenbank mit ab. Beide Namen lassen sich dann aus der Datenbank ziehen. Nach dem erfolgreichen Löschen im Bucket, musst du natürlich die Datei auch aus deiner Datenbank löschen. 

export const deleteObjectByKey = key => {
  const params = {
    Bucket: process.env.VUE_APP_AWS_BUCKET,
    Key: key,
  }
  const data = s3.deleteObject(params).promise()

  return data
}

Upload Komponente in Vue mit Filepond

Wenn du deinen File-Upload nicht selbst stylen möchtest ist filepond sehr zu empfehlen. Mit der Bibliothek kannst in wenigen Minuten ein professionelles UI für den Upload implementieren.


Schritt 1: FilePond Komponente

Um die Bibliotheken nutzen zu können, fügen wir sie mit yarn wieder zu den Projekt Dependencies hinzu.

yarn add vue-filepont filepond-plugin-file-validate-type filepond-plugin-image-preview filepond-plugin-image-crop filepond-plugin-image-transform

Nach erfolgreichem Hinzufügen, kannst du vue-filepond in der gewünschten Vue-Komponente importieren.

import vueFilePond from 'vue-filepond'
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
import FilePondPluginImageCrop from 'filepond-plugin-image-crop'
import FilePondPluginImageTransform from 'filepond-plugin-image-transform'
  <FilePond
    ref="pond"
      :server="{
      process: (fieldName, file, metadata, load, error, progress, abort) => {
        uploadFile(file, metadata, load, error, progress, abort)
      },
    }"
    @removefile="onRemoveFile"
  />

Nun zur FilePond Komponente: Ref wird benötigt, um die Methode wie processFiles, addFile usw. mit den Komponente zu verbinden. Wenn die Datei bearbeitet wird, dann wird unsere uploadImages Methode mit den Parametern ausgeführt. Wichtig, die AWS Methoden müssen ebenso aus der aws.js importiert werden.

import { singleUpload, deleteObjectByKey } from '@/aws.js'

Schritt 2: So geht’s mit dem File-Upload

Der File-Upload in unserer Vue-App lässt sich nun recht einfach umsetzen. Wir rufen unsere uploadFile Methode mit der Datei und dem gewünschten Ordner als Parameter auf. Wenn der Upload erfolgreich war, erhalten wir eine Antwort mit dem Status 200.

async uploadFile(file, metadata, load, error, progress, abort){
      const result = await singleUpload(
        file,
        this.$route.params.teamSlug // folder of the file, you should change it to your variable or a string
      )
      if (result.status === 200) {
        // Handle storing it to your database here
        load(file) // Let FilePond know the processing is done
      } else {
        error() // Let FilePond know the upload was unsuccessful
      }
      return {
        abort: () => {
          // This function is entered if the user has tapped the cancel button
          // Let FilePond know the request has been cancelled
          abort()
        },
      }
},

Schritt 3: Rendern der Bilder

Um Dateien in seiner Vue-App später anzuzeigen, müssen spezifischen Datei-Daten wie Key und Url in der Datenbank gespeichert werden. Wenn man nur Bilder speichert, dann reicht auch nur der Key, da die URL in einem Computed Objekt generiert werden kann.

computed: {
  imgSrcArray: () => {
    return this.keys.map(url => 'https://s3.eu-central-1.amazonaws.com/vue-fileupload-example/' + url)
  },
},

Wichtig: Tausche eu-central-1 gegen deine Bucket-region und vue-fileupload-example gegen euren Bucket-name! Dann kannst du mir v-for beispielsweise eine Liste von Bilder rendern.

<img v-for="src in imgSrcArray" :src="src"/>

Schritt 4: Entfernung von Dateien

Im Schritt 1 habt ihr schon wahrscheinlich den v-on remove bemerkt. Jetzt zeige ich euch die Methode, die beim Löschen ausgeführt werden wird.

async onRemoveFile(event) {
      let url = this.$route.params.teamSlug + '/' + event.file.name // event.file.name has only the name after the slash / so the actual filename
      const res = await deleteObjectByKey(url)
      if (
        res.$response.httpResponse.statusCode >= 200 &&
        res.$response.httpResponse.statusCode < 300
      ){
        // here remove data from database too
      }
    },

Der StatusCode der Antwort zwischen 200 und 300 heißt, dass die Datei entweder gelöscht worden ist oder diese gar nicht existiert.

Resumé

Mithilfe eines AWS Buckets und der signedURL Funktion ist es relativ einfach einen Datei-Upload ohne große Einbindung des Backends zu realisieren. So wird das Backend nicht unnötig unter Last gesetzt. In Verbindung mit Vue und Filepond ist der gewünschte Upload über das Frontend einsatzbereit.

Was wir auf der VueJS Konferenz in London gelernt haben

Mit dem Ziel, die Vue.js Community besser kennenzulernen und Neuerung rund um das hippe JavaScript-Framework zu erfahren, haben wir uns auf den Weg nach London zur VueJS London gemacht. Für mich war es besonders außergewöhnlich, da ich früher weder geflogen bin noch in London war. London ist nicht nur wegen der Konferenz eine Reise wert. Die Stadt ist wirklich beeindruckend. Neuankömmlinge wie mich erkennt man relativ schnell, da sie beim Überqueren der Straße immer in die falsche Richtung schauen… *emoji*. 

Lufthansa brachte uns bequem nach London

Meetup der VueJS London Gruppe als Vorabevent

Als kleine Besonderheit vor der Konferenz hatte die lokale Vue.js Meetupgruppe ein Vorabevent organisiert. Bei spannenden Vorträgen von Chris Fritz (Mitglied des Vue Core Teams) und Jen Looper (Gründerin der Vue Vixen & senior Entwickler) und Chris DeMars zum Thema “accessibilty (A11Y)” gab es einen ersten Vorgeschmack auf die einen Tag später startende Konferenz. 

Der Vortrag von Chris, den Hauptverantwortlichen für die wirklich gut organisierte Vue.js Dokumentation, gab einen Überblick über neue Technologien und einige Tipps zum ‘best practices’. Die Neuerung bei den Vue Devtools unterstützen Entwickler immer besser dabei ihre Vue-Apps zu debuggen. Zudem hat er Weiterentwicklungen für die Vue CLI 3.0 damit verbundene Vue UI vorgestellt .

Jen Looper stellte vue-nativescript vor.

Jen Looper gab einen kleinen Talk zu den Grundlagen der mobilen Entwicklung mit Vue. In Verbindung mit NativeScript lassen sich in vertrauter Umgebung mobile Apps entwickeln. Jen hat ihre Eigenentwicklung vorgestellt: eine App die Schülern beim Sprachenlernen hilft. Die App bewertet die Genauigkeit der ausgesprochenen Wörter, sodass Aussprachefehler korrigiert werden können. 

Zweiter Tag – Tag der Workshops

Der offiziell erste Tag der Konferenz startet mit Tagesworkshops. Im Advanced Vue Workshop bei Chris Fritz gab es Tipps zum Vorgehen bei der Entwicklung größerer Applikationen. Der Workshop hat sich definitiv gelohnt. Wir haben die empfohlenen Grundsetups beleuchtet und mögliche Fallstricke besprochen, z. B.: den Unterschied zwischen scoped und module CSS, einige Wörter über ESlint und andere lints (z.B.: style). Chris hat auch sein entwickeltes Boilerplate für Enterprise Vue Apps vorgestellt.

Konferenztag mit Vorträgen

Der dritte Konferenztag war ein Feuerwerk an Vorträgen zum Framework. Auch war es irgendwie der Tag der Releases. Unter anderen wurde Nuxt 2.0 live auf der Bühne released.

Skype-Call mit Evan You

Der Kernentwickler Evan You ging in seinem Vortrag auf die Vorhaben bezüglich der Version 3 ein. Unter anderen soll die neue Version komplett in TypeScript entwickelt. Seine Ideen hat er auch nach dem Konferenz in einem Blogpost veröffentlicht. 

Jen Looper sprach über AI und ihre verschiedene Arten und wie sie lernen. Sie hatte eine mobile Beispiel-App mit NativeScript für Vue.js entwickelt Auch hier wurde auf der Bühne die Version 2 für nativescript-vue angekündigt..

Ein ziemlich beeindruckender Vortrag war die Präsentation der Neuerung der Codesandbox von Ives van Hoorne. Es ist nun möglich auch serverseitige Template innerhalb einer Sandbox zu nutzen. Somit können auch ziemlich schnell prototypische Nuxt.js Apps aufgesetzt werden.

Daniel Rossenwasser hat die Neuerung rund um die Arbeit mit TypesScript und Vue vorgestellt. Der Hinweis darauf, dass Vue 3 in TypeScript entwickelt wird, zeigt welche Richtung das Kernteam einschlägt.

VueJS und London waren definitiv eine Reise wert

Diese Reise hat sich auf jeden Fall gelohnt. Trotz zahlreicher Möglichkeiten sich im Web über VueJS auf dem Laufenden zu halten, war die VueJS London ein inspirierendes Erlebnis.

Alle Neuerung und Entwicklungen greifen wir auch in unserem Vue.js Workshop auf.