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') }
]
})
}
댓글
댓글 쓰기