De Opkomst van de Butt-minder-Website

0
75

Het lijkt alsof alle coole kids hebben verdeeld zich in twee cliques: de Onthoofde CMS menigte aan de ene kant en de Static Site Generator menigte op de andere. Terwijl ik toegeven dat zijn pretty cool team namen, ik vond mezelf niet in staat om een kant kiezen. Te parafraseren Groucho Marx, “ik geef niet om bij een club die mij als lid.”

Voor mijn eigen eenvoudige blog (dat is beschamend leeg op het moment), een static site generator kan een goede pasvorm. Systemen zoals Hugo en Jekyll beide zijn sterk aanbevolen door de ontwikkelaars heb ik liefde en vertrouwen en ziet er geweldig uit op het eerste gezicht, maar ik raakte struikelblokken toen ik wilde om het thema of het opzetten van meer complexe JavaScript en interactie tussen pagina ‘ s. Er zijn manieren voor het oplossen van deze problemen, maar dat is niet de soort van het weekend wil ik hebben.

Naast dat, ik hou van om te experimenteren, nieuwe dingen doen, en ik heb een grote crush op Vue op het moment. Het hebben van een Onthoofde CMS setup met een front-end die is ontkoppeld van de back-end kan een goede combinatie voor mij, maar na 7 jaar van PHP gooien met WordPress, de hele setup voelt als overkill voor mijn basisbehoeften.

Wat ik echt wil is een static site generator die me het schrijven van een blog als een onderdeel van een grotere interne pagina ‘ app, zodat ik ruimte om nieuwe dingen te proberen en nog steeds de volledige controle over de styling, zonder de noodzaak voor een database of een soort van back-end. Dit is een lange weg om je te vertellen dat ik vond mijn eigen club te sluiten, met een uitgesproken vn-coole naam.

Maak je klaar voor het…

De Butt-minder-Website

Omdat er geen back-end, het krijgen? 😶

Het duurt een paar stappen te gaan butt-minder:

  1. Het instellen van een één-pagina-app met Vue
  2. Het genereren van elke route bij het bouwen
  3. Maak een blog en artikel componenten
  4. Integreren Webpack parseren van Markdown inhoud
  5. De functionaliteit uitbreiden met plugins
  6. Winst!

Dat laatste punt is een deel van elk voorstel, toch?

Ik weet het lijkt een hoop werk, maar dit is niet zo moeilijk als het lijkt. Laten we breken de stappen samen.

Het instellen van een één-pagina-app met Vue

Laten we Vue up and running. We moeten Webpack om dat te doen.

Ik snap het, Webpack is behoorlijk intimiderend zelfs als je weet wat er aan de hand is. Het is waarschijnlijk het beste laat iemand anders doet het echt hard werken, dus we gebruiken de Vue Progressieve Web App Bewaarplaats als onze stichting en maak een paar tweaks.

We konden gebruik maken van de standaard setup van de repo, maar zelfs ik, tijdens het schrijven van dit artikel waren er wijzigingen worden gemaakt. In het belang van het niet hebben van dit alles pauze op ons, zullen we gebruik maken van een repo die ik heb gemaakt voor demonstratie doeleinden. De repo is een tak voor elke stap die we zullen behandelen in deze post mee te volgen.

Bekijk op GitHub

Het klonen van de repo en check de stap-1 tak:

$ git clone https://github.com/evanfuture/vue-yes-blog.git stap-1
$ cd vue-ja-blog
$ npm installeren
$ npm uitvoeren dev

Een van mijn favoriete onderdelen van de moderne ontwikkeling is dat het duurt slechts dertig seconden om een progressieve web app up and running!

Daarna laten we de zaken compliceren.

Het genereren van elke route bij het bouwen

Uit de doos, enkele pagina apps alleen een single entry point. In andere woorden, het leven te leven op een enkele URL. Dit is zinvol in sommige gevallen, maar we willen onze app om te voelen als een normale website.

We zullen gebruik moeten maken van de geschiedenis van de mode in de Vue Router bestand om dat te doen. Eerste, we zetten dat op door het toevoegen van stand: geschiedenis van de Router eigenschappen van het object als volgt:

// src/router/index.js
Vue.gebruik(Router);

export standaard nieuwe Router({
mode: ‘de geschiedenis’,
routes: [
// …

Onze starter app heeft twee routes. Naast Hallo, wij hebben een tweede onderdeel genaamd Banana dat leeft op de route /banaan. Zonder geschiedenis van de mode, de URL voor die pagina zou worden http://localhost:1982/#/banana. Geschiedenis van de mode, reinigt, die tot http://localhost:1982/banana. Veel eleganter!

Dit alles werkt vrij goed in ontwikkeling modus (npm uitvoeren dev), maar laten we een kijkje nemen op hoe het eruit zou zien in de productie. Hier is hoe we alles compileert:

$ npm uitvoeren bouwen

Die opdracht zal het genereren van uw Vue site in de ./dist map. Om het te zien live, is er een handig commando voor het opstarten van een super eenvoudige HTTP-server op uw Mac:

$ cd dist
$ python -m SimpleHTTPServer

Sorry Windows mensen, ik weet niet gelijkwaardig!

Ga nu naar localhost:8000 in uw browser. U ziet uw site er uit zal zien in een productie-omgeving. Klik op de Banaan link, en alles is goed.

Vernieuw de pagina. Oeps! Dit blijkt onze eerste probleem met één pagina-apps: er is slechts één HTML-bestand dat wordt gegenereerd bij het bouwen, dus er is geen manier voor de browser om te weten dat /banaan zou zich moeten richten op de belangrijkste app-pagina en het laden van de route zonder de fancy Apache-stijl omleidingen!

Natuurlijk, er is een app voor. Of, op zijn minst een plugin. De basisfunctionaliteit is opgemerkt in de Vue Progressieve Web App Bewaarplaats documentatie. Hier is hoe je het zegt kunnen we de rotatie van de plugin:

$ npm installeren -D prerender-spa-plugin

Laten we onze routes naar de Webpack productie configuratie bestand:

// ./build/webpack.prod.conf.js
// …
const SWPrecacheWebpackPlugin = (‘sw-precache-webpack-plugin’)
const PrerenderSpaPlugin = (‘prerender-spa-plugin’)
const loadMinified = require(‘./load-minified’)
// …
const webpackConfig = merge(baseWebpackConfig, {
// …
plugins: [
// …
nieuwe SWPrecacheWebpackPlugin({
// …
kleineren: true,
stripPrefix: ‘dist/’
}),
// prerender app
nieuwe PrerenderSpaPlugin(
// Pad naar gecompileerde applicatie
pad.join(__dirname, ‘../dist’),
// Lijst van eindpunten die u wilt prerender
[ ‘/’, ‘/banaan’ ]
)
]
})

Dat is het. Nu, wanneer u een nieuwe bouwen, elke route in de array zal weergegeven worden als een nieuwe toegangspoort tot de app. Gefeliciteerd, we hebben eigenlijk gewoon ingeschakeld statische site generatie!

Maak een blog en artikel componenten

Als je overslaan, vooruit, we zijn nu aan het stap-2 tak van mijn demo-repo. Ga je gang en probeer het uit:

$ git checkout stap-2

Deze stap is vrij eenvoudig. We maken twee nieuwe onderdelen, en elkaar koppelen.

Blog Component

Laten we registreren de blog component. We maken een nieuw bestand met de naam YesBlog.vue in de /src/onderdelen directory en neerzetten in de markup voor het weergeven van:

// ./src/onderdelen/YesBlog.vue
<sjabloon>
<div class=”blog”>
<h1>Blog</h1>
<router-link=”/”>Home</router-link>
<hr/>
<artikel v-for=”artikel in de artikelen” :key=”artikel.slug” class=”artikel”>
<router-link class=”artikel__link” :te=”`/blog/${ artikel.slug }`”>
<h2 class=”artikel__title”> {{- artikel.titel }}</h2>
<p class=”artikel__description”>{{- artikel.beschrijving}}</p>
</router-link>
</article>
</div>
</template>

<script>
export standaard {
naam: ‘blog’,
berekend: {
artikelen() {
terug [
{
slug: ‘eerste-artikel’,
titel: ‘Artikel’,
beschrijving: ‘Dit is een artikel’s beschrijving’,
},
{
slug: ‘tweede-artikel’,
titel: ‘Artikel’,
beschrijving: ‘Dit is een artikel van twee’s beschrijving’,
},
];
},
},
};
</script>

Alles wat we doen, hier is het creëren van een tijdelijke array (artikelen) die zal worden gevuld met artikel objecten. Deze matrix maakt ons artikel lijst en maakt gebruik van de slug parameter als de bericht-ID. De titel en beschrijving parameters invullen van de post details. Voor nu, het is alle hard-coded krijgen terwijl we de rest van onze code in de plaats.

Artikel Component

Het artikel onderdeel is van een soortgelijk proces. We maken een nieuw bestand met de naam YesArticle.vue en het vaststellen van de markup voor het weergeven van:

// ./src/onderdelen/YesArticle.vue
<sjabloon>
<div class=”artikel”>
<h1 class=”blog – __title”>{{- artikel.titel}}</h1>
<router-link=”/blog”>Terug</router-link>
<hr/>
<div class=”artikel__lichaam” v-html=”artikel.lichaam”></div>
</div>
</template>

<script>
export standaard {
naam: ‘YesArticle’,
rekwisieten: {
id: {
type: String,
nodig: true,
},
},
gegevens() {
terug {
artikel: {
titel: dit.id,
lichaam: ‘<h2>Test</h2><p>Ok, laat’s meer doen nu!</p>’,
},
};
},
};
</script>

We gebruiken de rekwisieten doorgegeven door de router om te weten wat artikel ID we werken met. Voor nu zullen we alleen gebruiken als de titel van de post, en hardcode het lichaam.

Routing

We kunnen niet vooruit gaan, totdat wij ons nieuwe uitzicht op de router. Dit zal ervoor zorgen dat onze Url ‘ s zijn ongeldig en kunnen onze navigatie om goed te functioneren. Hier is het geheel van de router bestand:

// ./src/router/index.js
importeren Router van ‘vue-router’;
importeren Hello from ‘@/onderdelen/Hallo’;
importeren Banaan van ‘@/onderdelen/Banaan’;
importeren YesBlog van ‘@/onderdelen/YesBlog’;
importeren YesArticle van ‘@/onderdelen/YesArticle’;

Vue.gebruik(Router);

export standaard nieuwe Router({
mode: ‘de geschiedenis’,
routes: [
{
pad: ‘/’,
naam: ‘Hallo’,
onderdeel: Hallo,
},
{
pad: ‘/banaan’,
naam: ‘Banaan’,
onderdeel: Banaan,
},
{
pad: ‘/blog’,
naam: ‘YesBlog’,
onderdeel: YesBlog,
},
{
pad: ‘/blog/:id’,
naam: ‘YesArticle’,
rekwisieten: true,
onderdeel: YesArticle,
},
],
});

Merk op dat we hebben toegevoegd /:id van de YesArtcle onderdeel pad en de rekwisieten op true. Deze zijn cruciaal omdat ze het vaststellen van de dynamische routering in het onderdeel rekwisieten array in het onderdeel bestand.

Tot slot kunnen we u een link naar onze homepage die verwijst naar de blog. Dit is wat we neerzetten in de Hallo.vue-bestand te gaan:

<router-link=”/blog”>Blog</router-link>
Pre-rendering

We hebben veel werk gedaan tot nu toe, maar niets van dat alles zal blijven totdat we vooraf maken van onze routes. Pre-rendering is een mooie manier om te zeggen dat we de toepassing vertellen wat routes bestaan en voor het dumpen van de juiste markering op de juiste route. We hebben nog een Webpack-plug-in voor deze eerder, dus hier is wat we kunnen toevoegen aan onze Webpack productie configuratie bestand:

// ./build/webpack.prod.conf.js
// …
// Lijst van eindpunten die u wilt prerender
[ ‘/’, ‘/banaan’, ‘/blog’, ‘/blog/eerste-artikel’, ‘/blog/tweede-artikel’ ]
// …

Ik moet toegeven, dit proces kan lastig en vervelend. Ik bedoel, wie wil aanraken meerdere bestanden om een URL te maken?! Gelukkig, we kunnen automatiseren, die we zullen behandelen verder naar beneden.

Integreren Webpack parseren van Markdown inhoud

We zijn nu aan het stap-3-tak. Check it out als je het volgende in de code:

$ git checkout stap-3
De Berichten

We worden met behulp van Markdown te schrijven op onze posten, met wat FrontMatter te maken van meta-data functionaliteit.

Start een nieuw bestand aan in de berichten directory te maken van onze allereerste post:

// ./src/posts/eerste-artikel.md

titel: Artikel Één van MD
beschrijving: waarin de held begint fris
gemaakt: 2017-10-01T08:01:50+02
bijgewerkt:
status: publiceren

Hier is de tekst van het artikel. Het is vrij groot is, is het niet?

// ./src/berichten/tweede-artikel.md

titel: Artikel Twee van MD
beschrijving: Dit is een ander artikel
gemaakt: 2017-10-01T08:01:50+02
bijgewerkt:
status: publiceren

## Laten we beginnen met een H2
En dan nog wat tekst
En dan nog wat code:
“html
<div class=”container”>
<div class=”main”>
<div class=”artikel invoegen-wp-tags-here”>
<h1>Titel</h1>
<div class=”article-content”>
<p class=”intro”>Intro Tekst</p>
<p></p>
</div>
<div class=”article-meta”></div>
</div>
</div>
</div>

Dynamische Routering

Een vervelende zaak op het moment dat we moeten hardcode voor onze routes voor de pre-rendering plug-in. Gelukkig is het niet ingewikkeld om te maken deze dynamische met een beetje Knooppunt magie. Eerst maken we een helper in ons bestand om de bestanden te vinden:

// ./build/utils.js
// …
const ExtractTextPlugin = (‘extract-tekst-webpack-plugin’)
const fs = (‘fs’)

de export.filesToRoutes = function (directory, de uitbreiding, de routePrefix = “) {
functie findFilesInDir(startPath, filter){
laat resultaten = []
if (!fs.existsSync(startPath)) {
console.log(“dir “, startPath)
terug
}
const bestanden = fs.readdirSync(startPath)
voor (i = 0; i < – bestanden.lengte; i++) {
const filename = pad.join(startPath, bestanden[i])
const stat = fs.lstatSync(filename)
als (stat.isDirectory()) {
resultaat = resultaat.concat(findFilesInDir(filename, filter)) //recurse
} else if (bestandsnaam.indexOf(filter) >= 0) {
de resultaten.push(filename)
}
}
resultaten
}

terug findFilesInDir(pad.join(__dirname, directory), uitbreiding)
.kaart((bestandsnaam) => {
terug bestandsnaam
.vervangen(pad.join(__dirname, directory), routePrefix)
.vervangen(extensie “)
})
}

de export.assetsPath = function (_path) {
// …

Dit kan eigenlijk alleen maar worden gekopieerd en geplakt, maar wat we hier hebben gedaan is het maken van een utility-methode genoemd filesToRoutes() in een map, de uitbreiding, en een optionele routePrefix, en retourneert een matrix van de routes op basis van een recursieve functie bestand zoeken binnen die map.

Alles wat we moeten doen om onze blog routes dynamisch is het samenvoegen van deze nieuwe array in onze PrerenderSpaPlugin routes. De kracht van de ES6 maakt dit heel eenvoudig:

// ./build/webpack.prod.conf.js
// …
nieuwe PrerenderSpaPlugin(
// Pad naar gecompileerde applicatie
pad.join(__dirname, ‘../dist’),
// Lijst van eindpunten die u wilt prerender
[
‘/’,
‘/banaan’,
‘/blog’,
…utils.filesToRoutes(‘../src/berichten’, ‘.md’, ‘/blog’)
]
)

Want we hebben reeds geïmporteerde utils aan de top van het bestand voor andere doeleinden te gebruiken, kunnen we gewoon gebruik maken van de verspreiding van de operator … om samen de nieuwe dynamische routes array in deze, en zijn we klaar. Nu onze pre-rendering is volledig dynamisch, alleen afhankelijk van ons een nieuw bestand toevoegt!

Webpack-Laders

We zijn nu aan het stap-4 tak:

$ git checkout stap-4

Om daadwerkelijk onze Markdown-bestanden in parse-inhoud, moeten we een aantal Webpack-laders. Weer iemand anders heeft al het werk gedaan voor ons, dus hebben we alleen maar te installeren en voeg ze toe aan onze config.

$ npm installeren -D json-loader markdown-het-front-materie-loader markdown-het highlight.js yaml-front-zaak

We zullen alleen het aanroepen van de json-loader en markdown-het-front-materie-loader van onze Webpack config, maar de laatste heeft peer afhankelijkheden van markdown-en highlight.js dus we installeren die op dezelfde tijd. Ook, niets waarschuwt ons over dit, maar yaml-front-zaak is ook nodig, zodat de bovenstaande commando voegt er aan toe dat ook.

Gebruik te maken van deze mooie nieuwe laders, we gaan het toevoegen van een blok aan ons Webpack basis config:

// ./build/webpack.base.conf.js
// …
de module.export = {
// …
module: {
regels: [
// …
{
test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
loader: ‘url-loader’
opties: {
limiet: 10000,
naam: utils.assetsPath(‘fonts/[naam].[hash:7].[ext]’)
}
},
{
test: /.md$/,
laders: [‘json-loader’, ‘markdown-het-front-materie-loader’],
},
]
}
}

Nu, enige tijd Webpack ontmoetingen vereisen een verklaring met een .md-extensie, het zal gebruik maken van de front-materie-loader (die juist de metadata blok van onze artikelen en de code blokken), en de uitvoer JSON en uitvoeren van het door de json-loader. Op deze manier, we weten dat we eindigen met een object voor elk artikel dat eruit ziet als dit:

// eerste-artikel.md [Object]
{
lichaam: “<p>Hier is de tekst van het artikel. Het is vrij groot is, is het niet?</p>n”
gemaakt: “2017-10-01T06:01:50.000 Z”
beschrijving: “waarin de held begint verse”
raw: “nnhier is de tekst van het artikel. Het is vrij groot is, is het niet?n”
slak: “het eerste artikel”
status: “publiceren”
titel: “Artikel Één van MD”
bijgewerkt: null
}

Dit is precies wat we nodig hebben en het is vrij makkelijk uit te breiden met andere metadata als je het nodig hebt. Maar tot dusver is dit niet iets te doen! We moeten eisen dat deze in één van onze onderdelen, zodat Webpack kunnen vinden en laden.

We kunnen schrijven:

require(‘../posts/eerste-artikel.md’)

…maar dan zouden we te maken hebben dat voor elk artikel die we maken, en dat het niet leuk als onze blog groeit. We hebben behoefte aan een manier om dynamisch te vragen van al onze Markdown-bestanden.

Dynamische Eigen

Gelukkig, Webpack doet dit! Het was niet makkelijk om documentatie te vinden voor deze, maar hier is het. Er is een methode genaamd nodig.context() die we kunnen gebruiken om te doen wat we nodig hebben. Wij zullen het toevoegen aan het script sectie van onze YesBlog onderdeel:

// ./src/onderdelen/YesBlog.vue
// …
<script>
const posten = {};
const req = nodig.context(‘../posts/’, false, /.md$/);
req.sleutels().forEach((key) => {
posts[key] = req(sleutel);
});

export standaard {
naam: ‘blog’,
berekend: {
artikelen() {
const articleArray = [];
– Object.toetsen(berichten).forEach((key) => {
const artikel = posts[key];
– artikel.slug = – toets.vervangen(‘./’, “).vervangen(‘.md’, “);
articleArray.push(artikel);
});
terug articleArray;
},
},
};
</script>
// …

Wat gebeurt hier? We maken een posts object dat we eerst vullen met artikelen, gebruik dan later in het onderdeel. Aangezien we de pre-rendering al onze inhoud, van dit object zal onmiddellijk beschikbaar zijn.

Het vereisen.context() methode accepteert drie argumenten.

  • de directory waar het zal zoeken
  • het wel of niet inclusief submappen
  • een regex filter om terug te keren bestanden

In ons geval, wij willen alleen Markdown-bestanden in de berichten directory, dus:

vereisen.context(‘../posts/’, false, /.md$/);

Dit geeft ons een soort van vreemde nieuwe functie/object dat we moeten ontleden om te gebruiken. Dat is waar req.sleutels() geeft ons een array van de relatieve paden naar elk bestand. Als we req(key), dit zal de terugkeer van de artikel-object dat we willen hebben, dus we kunnen toewijzen aan dat de waarde van een bijpassende sleutel in onze berichten object.

Ten slotte, in de berekende artikelen() methode, zullen we automatisch onze slug door het toevoegen van een slak sleutel tot elke post, met een waarde van de bestandsnaam zonder pad of uitbreidingen. Als we willen, dit kan veranderd worden om ons in staat te stellen de slug in de Markdown zelf, en alleen terug te vallen op auto-generatie. Op hetzelfde moment, duwen we het artikel van objecten in een array, dus we hebben iets gemakkelijk te itereren over in onze component.

Extra Credit

Er zijn twee dingen die je waarschijnlijk wilt doen meteen als je deze methode gebruiken. De eerste is om te sorteren door de datum en de tweede is om op te filteren artikel status (ontwerp en gepubliceerd). Want wij hebben een matrix, dit kan gedaan worden in één lijn toegevoegd net voor het retourneren articleArray:

articleArray.filter(post => post.status === ‘publiceren’).sorteer (a, b) => een.gemaakt < b.het is gemaakt);
Laatste Stap

Een laatste ding om te doen nu, en dat is te instrueren onze YesArticle component gebruik van de nieuwe data die we ontvangen, samen met de route wijzigen:

// ./src/onderdelen/YesArticle.vue
// …
gegevens() {
terug {
artikel: require(`../posts/${dit.id}.md`), // eslint-uitschakelen-line global-eisen, import/geen-dynamic-eisen
};
},

Omdat we weten dat onze component zal vooraf worden berekend, kunnen we uitschakelen van de ESLint regels die niet toestaan van dynamische en wereldwijde vereist, en vragen om het pad naar de post die overeenkomt met de parameter id. Deze triggers onze Webpack Markdown laders, en we hebben het allemaal gedaan!

OMG!

Ga je gang en test dit uit:

$ npm uitvoeren van bouwen && cd dist && python -m SimpleHTTPServer

Bezoek localhost:8000, navigeren en het vernieuwen van de pagina ‘ s te laden de hele app van de nieuwe toegangspoort. Het werkt!

Ik wil benadrukken hoe cool dit is. We hebben draaide een map van Markdown-bestanden in een array van objecten die we kunnen gebruiken als we willen, ergens op onze website. De sky is de limit!

Als u wilt zien hoe het allemaal werkt, kunt u check out de laatste tak:

$ git checkout stap-compleet

De functionaliteit uitbreiden met plugins

Mijn favoriete deel over deze techniek is dat alles is uitbreidbaar en vervangbaar.

Heeft iemand een beter Markdown processor? Geweldig, wisselen van de lader! Moet controle over uw site het SEO? Er is een plugin voor. Nodig voor het toevoegen van een commentaar systeem? Voeg die plugin ook.

Ik houd een oogje op deze twee repositories voor ideeën en inspiratie:

  • Geweldig Vue.js
  • Geweldig Markdown

Winst!

U dacht dat deze stap een grap was?

Het laatste wat wij willen doen is nu de winst van de eenvoud die we hebben gemaakt, en nab sommige gratis hosting. Sinds de site is nu gegenereerd op je git repository, alles wat je hoeft te doen is druk uw veranderingen te Github, Bitbucket, Gitlab of welke code repository die u gebruikt. Ik koos voor Gitlab omdat eigen repo ‘ s zijn gratis en ik wilde niet dat mijn concepten publiek, zelfs in repo-vorm.

Na dat u op zoek moet naar een host. Wat je echt wilt is een host die heeft continuous integration en deployment, zodat het samenvoegen van je master branch triggers de npm uitvoeren van het bouwen van de opdracht en regenereert uw site.

Ik gebruikte Gitlab eigen CI-tools voor de eerste paar maanden na mijn set-up. Ik vond de setup te gemakkelijk zijn, maar het oplossen van problemen die te moeilijk zijn. Ik overgestapt naar Netlify, die heeft een uitstekende gratis plan en een aantal grote CLI tools ingebouwd.

In beide gevallen bent u in staat om uw eigen domeinnaam op eigen servers en het installeren van een SSL certificaat voor HTTPS-ondersteuning—dat laatste punt is belangrijk als je ooit willen experimenteren met dingen als de getUserMedia API, of maak een winkel om de verkoop te maken.

Met al deze set-up, je bent nu een lid van de Butt-minder Website van de club. Gefeliciteerd en welkom, vrienden! Hopelijk vindt u dit een eenvoudig alternatief voor complexe content management systemen voor uw eigen persoonlijke website en die het mogelijk maakt om te experimenteren met gemak. Laat het me weten in de comments als je vast komt te zitten langs de weg…of als het je lukt dan in je wildste dromen. 😉