Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Frontend Development 2019 – What's in your stack?

Frontend Development 2019 – What's in your stack?

stefan judis

June 13, 2018
Tweet

More Decks by stefan judis

Other Decks in Technology

Transcript

  1. “ Nobody loves what prettier does to their syntax. Everyone

    loves what prettier does to their coworkers' syntax. "Grensley" on Reddit
  2. @font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal;

    src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour font-display: auto; }
  3. @font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal;

    src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available font-display: swap; }
  4. @font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal;

    src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available // font-display: swap; // invisible until custom font is loaded (FOIT deluxe) font-display: block; }
  5. @font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal;

    src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available // font-display: swap; // invisible until custom font is loaded (FOIT deluxe) // font-display: block; // invisible very short then fallback font // -> custom font when available font-display: fallback; }
  6. <nav class="modal"> <ul> <li><a href="#">One</a></li> <li> <span>Two</span> <ul class="dropdown"> <li>

    <label> <span>Sub-1</span> <input type="text"> </label> </li> <li> <label> <span>Sub-2</span> <input type="text"> </label> </li> </ul> </li> <li><a href="#">Three</a></li> </ul> </nav> li:focus-within { background: #ddd; }
  7. const options = { threshold: 0 }; const intersectionObserver =

    new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options);
  8. const options = { threshold: 0 }; const intersectionObserver =

    new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options); [...document.querySelectorAll('.lazyload')] .forEach(elem => intersectionObserver.observe(elem));
  9. const options = { threshold: 0 }; const intersectionObserver =

    new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options); [...document.querySelectorAll('.lazyload')] .forEach(elem => intersectionObserver.observe(elem)); Congratulations, that's lazy loading!
  10. <html> <head> <title>ES6 modules tryout</title> <!-- in case ES6 modules

    are supported --> <script src="app/index.js" type="module"></script> <!-- in case ES6 modules aren't supported --> <script src="dist/bundle.js" defer nomodule></script> </head> <body> <!-- ... --> </body> </html>
  11. <script src="app/index.js" type="module"></script> 'use strict'; // strict mode by default

    console.log(this); // undefined const a = 1; // local to the script
  12. <script src="app/index.js" type="module"></script> 'use strict'; // strict mode by default

    console.log(this); // undefined const a = 1; // local to the script import main from './main.js';
  13. // loaded asynchronous // executed after finished HTML parsing <script

    src="app/index.js" type="module"></script> 'use strict'; // strict mode by default console.log(this); // undefined const a = 1; // local to the script import main from './main.js';
  14. export async function migration (migrationCreator: Function): Promise<Intent[]> { // ...

    } The async function migration accepts the argument migrationCreator of type Function and returns a Promise that resolves with a collection of type Intent.
  15. export async function migration (migrationCreator: Function, makeRequest: Function): Promise<Intent[]> {

    // ... } test/unit/bin/lib/config.spec.ts test/unit/lib/content-types-in-chunks.spec.ts test/unit/lib/deleted-ct-entries.spec.ts test/unit/lib/fetcher.spec.ts test/unit/lib/intent-list/intent-list.spec.ts src/lib/migration-steps/index.ts ...
  16. self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit

    - return response if (response) { return response; } return fetch(event.request); } ) ); });
  17. “ Backend Engineer: Hmm. So you’re saying this “GraphQL” will

    allow any web or native engineer to arbitrarily query basically any field in any backend service, recursively, however they want, without any backend engineers involved? Frontend Engineer: Yeah, right? It’s amazing! […silence…] Backend Engineer: Guards, seize this person. medium.com/airbnb-engineering/reconciling-graphql-and-thrift-at-airbnb-a97e8d290712
  18. POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title

    slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } }
  19. POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title

    slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } } One request for everything
  20. API

  21. { "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react":

    "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "3.0.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, }
  22. { "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react":

    "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "3.0.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, }
  23. { "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react":

    "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "1.1.4" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, } A single dependency
  24. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); await browser.close(); })();
  25. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { }); await browser.close(); })();
  26. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();
  27. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();
  28. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();
  29. const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() =>

    { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); await browser.close(); })();
  30. const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL

    = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { return new Promise(async resolve => { const {status} = await fetch(href); resolve({ url: href, status }); }) })); await browser.close(); })();
  31. const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL

    = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); await browser.close(); })();
  32. const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL

    = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); // filter invalid URLs const invalidLinks = urls .filter(href => href.status !== 200) .map(({url}) => url) await browser.close(); })();
  33. const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL

    = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); // filter invalid URLs const invalidLinks = urls .filter(href => href.status !== 200) .map(({url}) => url) if (invalidLinks.length) { console.error(`Found invalid links: \n ${invalidLinks.join('\n')}`); } else { console.log('All cool!'); } await browser.close(); })();
  34. “ You can take your front-end skills and do things

    that typically only a back-end can do. Chris Coyier
  35. Lambda exports.sayHello = async (event) => { return 'Hello from

    Lambda!'; }; API Gateway GET /.../eu-central-1.amazonaws.com/prod/ POST /.../eu-central-1.amazonaws.com/prod/ PUT /.../eu-central-1.amazonaws.com/prod/ ...
  36. Lambda API Gateway GET /.../eu-central-1.amazonaws.com/prod/ POST /.../eu-central-1.amazonaws.com/prod/ PUT /.../eu-central-1.amazonaws.com/prod/ ...

    exports.sayHello = async (event) => { return { statusCode: 200, body: JSON.stringify({ "msg": "Hello from Lambda!" }) }; };
  37. service: say-hello provider: name: aws runtime: nodejs8.10 functions: sayHello: handler:

    handler.sayHello events: - http: path: / method: get handler.js exports.sayHello = async (event) => { return { statusCode: 200, body: JSON.stringify({ "msg": "Hello from Lambda!" }) }; }; serverless.yml
  38. module.exports.redirect = async ({ pathParameters }, context) => { try

    { } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };
  39. module.exports.redirect = async ({ pathParameters }, context) => { try

    { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };
  40. const got = require('got'); module.exports.redirect = async ({ pathParameters },

    context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };
  41. const got = require('got'); module.exports.redirect = async ({ pathParameters },

    context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } }; www.my-links.online/ devday
  42. const got = require('got'); module.exports.redirect = async ({ pathParameters },

    context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } }; www.my-links.online/ devday (yes, I bought this domain)
  43. ? ?

  44. ? ? module.exports.handleDiscourse = (event, context, callback) => { const

    post = JSON.parse(event.body).post; if (post && event.headers['X-Discourse-Event'] !== 'post_edited') { const { name, topic_title, topic_slug, topic_id, post_number } = post; } };
  45. ? ? module.exports.handleDiscourse = (event, context, callback) => { const

    post = JSON.parse(event.body).post; if (post && event.headers['X-Discourse-Event'] !== 'post_edited') { const { name, topic_title, topic_slug, topic_id, post_number } = post; // send it to Slack // ... // ... } };
  46. ? ?

  47. ? ?

  48. require('dotenv').config(); const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, CONTACT_NUMBERS, BOT_NUMBER, BOT_MESSAGE } =

    process.env; const client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN); exports.handler = function(event, context, callback) { Promise.all( CONTACT_NUMBERS.split(';').map(num => { return client.messages.create({ from: BOT_NUMBER, to: num, body: BOT_MESSAGE }); }) ) .then(() => callback(null, { statusCode: 200, body: 'Created' })) .catch(e => callback(e)); };