Vue SSR 잘라먹기 - 라우팅과 코드 나누기


vue-router로 라우팅하기


vue-router를 쓰는 것을 추천한다(라고 쓰고 강제한다.).  각 요청마다 새로운 인스턴스를 제공해야 한다.

// router.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export function createRouter () {
  return new Router({
    mode: 'history',
    routes: [
      // ...
    ]
  })
}
// app.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'

export function createApp () {
  // create router instance
  const router = createRouter()

  const app = new Vue({
    // inject router into root Vue instance
    router,
    render: h => h(App)
  })

  // return both the app and the router
  return { app, router }
}
// entry-server.js
import { createApp } from './app'

export default context => {
  // since there could potentially be asynchronous route hooks or components,
  // we will be returning a Promise so that the server can wait until
  // everything is ready before rendering.
  // 한국어로 하자면 다 로딩되길 기다려주기 위해 promise/비동기로 한다
  return new Promise((resolve, reject) => {
    const { app, router } = createApp()

    // set server-side router's location
    router.push(context.url)

    // wait until router has resolved possible async components and hooks
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      // no matched routes, reject with 404
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      // the Promise should resolve to the app instance so it can be rendered
      resolve(app)
    }, reject)
  })
}
// 서버 번들이 빌트됐다고 가정하고, 아마 서버에 대한 코드는 아래와 같을 것이다.
// server.js
const createApp = require('/path/to/built-server-bundle.js')

server.get('*', (req, res) => {
  const context = { url: req.url }

  createApp(context).then(app => {
    renderer.renderToString(app, (err, html) => {
      if (err) {
        if (err.code === 404) {
          res.status(404).end('Page not found')
        } else {
          res.status(500).end('Internal Server Error')
        }
      } else {
        res.end(html)
      }
    })
  })
})




코드 나누기


코드 나누기 == 게으른 로딩 == 초기 렌더링 다운로드 자산 감소 == 높은 TTI(time-to-interactive). 키는 초기화면에서 내가 필요한 것만 로딩 시키는 것.
뷰는 비동기 컴포넌트를 1급(first-class)의 개념으로 제공한다. 이 때 웹팩2의 코드 분할 포인트와 같은 동적 import 지원를 결합한다.
// changing this...
import Foo from './Foo.vue'

// to this:
const Foo = () => import('./Foo.vue')
앱을 출력하거나 마운트하기 전에 router.onReady가 서버와 클라이언트 모두 필요하다. 왜냐하면 라우터는 훅에의해 invoke되는 순서대로 비동기 라우트 컴포넌트를 resolve해야 하기 때문이다. 우리는 이미 서버엔트리에서는 해보았다. 이제 클라이언트 엔트리를 update해본다.
// entry-client.js

import { createApp } from './app'

const { app, router } = createApp()

router.onReady(() => {
  app.$mount('#app')
})


비동기 루트 컴포넌트를 포함한 예시 루트 config
// router.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export function createRouter () {
  return new Router({
    mode: 'history',
    routes: [
      { path: '/', component: () => import('./components/Home.vue') },
      { path: '/item/:id', component: () => import('./components/Item.vue') }
    ]
  })
}














댓글

이 블로그의 인기 게시물

서버에 파일 저장하기 - blob

Nuxt를 사용해야하는 10가지 이유 - 번역

후지필름 XC 50-230mm f4.5-6.7 OIS II