how to build a slide


repo: https://github.com/abouthiroppy/slides

😋 me 😙

profile

Yuta Hiroto

A JS engineer and Architect

Dwango, Inc

Working on Node.js, Babel

Node.js Japan User Group

contents


purpose


  • manage with git
  • share commonly used slides
  • slides should be written in Markdown or HTML
  • don't write JS as much as possible
  • have a presenter note(optional)

🔧Tools🔨

Slide Framework


bespoke.js DIY Presentation Micro-Framework https://github.com/bespokejs/bespoke

JavaScript

css

build flow⏳

Markdown -> HTML -> React


🤔


In this case, the loader chain is ...
markdown-loader -> html-loader


// webpack.config.js 

{
  test: /\.md$/,
  use: [
    'html-loader',
    'markdown-loader'
  ]
}

fetch slides


The slide file name is 0-title.md, 01-context.md, 02-dir/0-title.md, etc...

  // fetch all slides 
  const context = require.context('./slides', true, /(md|html)$/);
  const res = {
    id    : context.id,
    slides: context
      .keys()
      .sort() // sort by file name
      .map((e) => context(e))
  };

require.context is provided by webpack.
The content acquired by the path is expanded as a string by require.context.

abouthiroppy/slides/blob/master/src/lib/fetch-slides.js

send to a react component

Pass slides to React using dangerouslySetInnerHTML
after slides are converted to HTML.

const Base = (props) => (
  <article>
    {
      props.slides.map((slide, i) => (
        <section
          key={slide.meta.id}
          className={slide.meta.className}
          data-bespoke-backdrop={slide.meta.background}
          dangerouslySetInnerHTML={{ __html: slide.context }}
        />
      ))
    }
  </article>
);

production🚀

Service Worker

Introduce offline-plugin to use Service Worker.
Save scripts and images preferentially.


// webpack.prod.config.js

module.exports = {
  plugins: [
    new OfflinePlugin(),
    ...
  ]
};

// offline.js

import offlineRuntime from 'offline-plugin/runtime';

offlineRuntime.install();

Service Worker


sw-network

dynamic import

Don't bundle with 1 file.
Presenter Mode's codes are separated from the core code.

// AppContainer.js

constructor(props) {
  if (mode === 'view') {
    import(/* webpackChunkName: 'presenter.view' */ './ContentView/View')
      .then((e) => {
        ...
      });
  }
  else if (mode === 'host') {
    import(/* webpackChunkName: 'presenter.host' */ './ContentView/Host')
      .then((e) => {
        ...
      });
  }
}

https://github.com/abouthiroppy/slides/blob/master/src/lib/AppContainer.js

dynamic import

Dynamic Import is currently in stage 3 of the TC39 process.

They are divided by webpack.

Hash: 08e7000cd507fa241759
Time: 77409ms
                    Asset       Size  Chunks                    Chunk Names
0.08e7000cd507fa241759.js    14.4 kB       0  [emitted]
1.08e7000cd507fa241759.js    3.65 kB       1  [emitted]         presenter.host
2.08e7000cd507fa241759.js    1.45 kB       2  [emitted]         presenter.view
  08e7000cd507fa241759.js    1.27 MB       3  [emitted]  [big]  main

reduce image size

Use image-webpack-loader, it uses imagemin.


image-webpack-loader -> file-loader


// webpack.config.js

{
  test: /\.(png|jpg|gif|svg?)$/,
  use: [
    'file-loader',
    'image-webpack-loader'
  ]
}

Presenter Mode 🎬

Presenter Mode

Presenter Mode is very simple, it has only a next slide view and note.


how to share page information

Use localStorage to talk in real time between Host and View.

// host.js
window.slide.bespoke.on('activate', (e) => {
  localStorage.setItem('page', JSON.stringify({
    date: Date.now(),
    page: e.index
  }));
});

// view.js
window.addEventListener('storage', (e) => {
  if (e.key === 'page') {
    const page = JSON.parse(e.newValue).page;

    window.slide.bespoke.slide(page);
  }
});

https://github.com/abouthiroppy/slides/tree/master/src/lib/ContentView

INFORMATION 💁

NODE FESTIVAL IN TOKYO🗼

largest Node.js and JavaScript conference in Japan!!!

http://nodefest.jp/2017/

the end


repo: https://github.com/abouthiroppy/slides