DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on

SPA with history mode router in vanilla JS (with potential for SSG)

I don't know non-JavaScript-based Static Site Generators do it, but JS-based, like Gatsby, Nuxt, or Gridsome can prevent reloading and show transition between pages on location.pathname changes.

True SPA like Vue can do it too, but on first load, you can hasten JavaScript by using prerender-spa-plugin.

I have just create an SPA with history mode router in vanilla JS, but I do use a bundler (Rollup).

GitHub logo patarapolw / minimal-rollup-ts-pug-sass-template

Rollup + TypeScript + Pug + SASS template with no plan for JavaScript frameworks, whatsoever

minimal-rollup-ts-pug-sass-template

Rollup + TypeScript + Pug + SASS template with no plan for JavaScript frameworks, whatsoever

Usage

npx degit patarapolw/minimal-rollup-ts-pug-sass-template PATH_TO_YOUR_PROJECT_FOLDER_OR_OMIT
Enter fullscreen mode Exit fullscreen mode

Modes

  • ROUTER_MODE=history for SPA history mode
  • ELECTRON=1 for Electron mode (coupled with yarn build:electron and yarn watch:electron)

Caveats

Rollup currently have a problem of HTML not being watched. Serve in JavaScript / TypeScript to prevent this.




BTW, I cannot just use Parcel, because I want to enable it for Electron as well. (But Electron shouldn't use history mode.)

Why templating engine like Pug?

Because I feel like it will minify HTML by default. Also, Pug is defaulted to be very tidy, with no burden of the closing tag.

Of course, you can use other templating engines, like EJS as well.

Meta tags

Meta tags for Google, Facebook, and Twitter are as follow.

    meta(http-equiv="Content-Type", content="text/html;charset=UTF-8")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")

    meta(name="description", content=description data-meta="description")
    meta(name="keywords", content=keywords data-meta="keywords")

    meta(property="og:title" content=title data-meta="title")
    meta(property="og:description" content=description data-meta="description")
    meta(property="og:image" content=image data-meta="image")
    meta(property="og:url" content=url data-meta="url")

    meta(property="twitter:title" content=title data-meta="title")
    meta(property="twitter:description" content=description data-meta="description")
    meta(property="twitter:image" content=image data-meta="image")
    meta(property="twitter:card" content="summary_large_image")

    link(rel="shortcut icon", href=`${favicon || 'favicon.ico'}`, type="image/x-icon")

    title(data-title=title data-meta="title")= title
Enter fullscreen mode Exit fullscreen mode

I put data-meta and data-title, in case I need to edit it from JavaScript, so it is as simple as document.querySelectorAll('[data-meta=...]').

Module and nomodule

By default, Rollup (and Snowpack) specializes in the newer ES module, but nomodule fallbacks to SystemJS for lazy-loading.

    script(src="module/index.js" type="module")
    script(src="nomodule/index.js" nomodule)
Enter fullscreen mode Exit fullscreen mode

Forgot to mention that SPA router uses lazy loading for faster loading time, and load only what is needed.

Custom elements: <app-router> and <a is="router-link">

<a is="router-link"> is to provide convenience for creating a href that also have base URL and hash sign.

<app-router> might not need to be made custom element, because it is always singleton anyway; but I use it to make the element class-based.

popstate event

It is the event for page navigate that may have "state" in case HTML5 History is manipulated.

It can be superficially triggered by window.dispatchEvent(new PopStateEvent('popstate')), which is the basis of navigateTo function.

export function navigateTo (to: string) {
  if (ROUTER_MODE === 'history') {
    history.pushState({ to }, '', to)
    window.dispatchEvent(new PopStateEvent('popstate', { state: { to } }))
  } else {
    location.replace(to)
  }
}
Enter fullscreen mode Exit fullscreen mode

spa-rendered event and data-spa-rendered attribute

This is to indicate that the SPA has finished loading and is now ready to be scraped by Puppeteer to create a multiple-page website.

I will try using it extensively, to see if it is any good.

GitHub logo patarapolw / minimal-rollup-ts-pug-sass-template

Rollup + TypeScript + Pug + SASS template with no plan for JavaScript frameworks, whatsoever

Top comments (0)