怎么在vue中使用ssr+koa2构建服务端渲染

这篇文章给大家介绍怎么在vue中使用ssr+koa2构建服务端渲染,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

创新互联公司专注于吉水企业网站建设,成都响应式网站建设,商城网站制作。吉水网站建设公司,为吉水等地区提供建站服务。全流程按需策划,专业设计,全程项目跟踪,创新互联公司专业和态度为您提供的服务

vue是什么

Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与其它大型框架的区别是,使用Vue可以自底向上逐层应用,其核心库只关注视图层,方便与第三方库和项目整合,且使用Vue可以采用单文件组件和Vue生态系统支持的库开发复杂的单页应用。

 服务端渲染

 服务端渲染和单页面渲染区别

查看下面两张图,可以看到如果是服务端渲染,那么在浏览器中拿到的直接是完整的 html 结构。而单页面是一些 script 标签引入的js文件,最终将虚拟dom去挂在到 #app 容器上。

怎么在vue中使用ssr+koa2构建服务端渲染 

怎么在vue中使用ssr+koa2构建服务端渲染 

@vue/cli 4 来构建项目结构

下面代码使用最精简的实例完整代码会放到 github 上

step1 安装最新的脚手架初始化项目

yarn global add @vue/cli

step2 添加服务端文件

启动一个 web 服务下方代码中 http://localhost:9000 就是我们最终要访问到地址

const Koa = require('koa')
const path = require('path')

const resolve = file => path.resolve(__dirname, file)
const app = new Koa()
const router = require('./router')
const port = 9000
app.listen(port, () => {
 console.log(`server started at localhost:${port}`)
})
module.exports = app

这里只是启动了服务,我们需要在去读取服务端和客户端到文件,下面代码就是服务端渲染的关键步骤

const fs = require('fs')
const path = require('path')
const send = require('koa-send')
const Router = require('koa-router')
const router = new Router()
// 获取当前文件的绝对路径
const resolve = file => path.resolve(__dirname, file)
const { createBundleRenderer } = require('vue-server-renderer')
const bundle = require('../dist/vue-ssr-server-bundle.json')
const clientManifest = require('../dist/vue-ssr-client-manifest.json')
// 创建一个 BunleRender 实例用于 renderer.renderToString 将 bundle 渲染为字符串
const renderer = createBundleRenderer(bundle, {
 runInNewContext: false,
 template: fs.readFileSync(resolve('../src/index.temp.html'), 'utf-8'),
 clientManifest: clientManifest
})

const handleRequest = async ctx => {
 ctx.res.setHeader('Content-Type', 'text/html')
 // 在 2.5.0+ 版本中,此 callback 回调函数是可选项。在不传递 callback 时,此方法返回一个 Promise 对象,在其 resolve 后返回最终渲染的 HTML。
 ctx.body = await renderer.renderToString(Object.assign({}, ctx.state.deliver, { url }))
}
router.get('/home',handleRequest)
module.exports = router

vue-server-render 提供一个名为 createBundleRenderer 的 API 使用方法如下

const { createBundleRenderer } = require('vue-server-renderer')

const renderer = createBundleRenderer(serverBundle, {
 runInNewContext: false, // 推荐
 template, // (可选)页面模板
 clientManifest // (可选)客户端构建 manifest
})

通过上面的 createBundleRenderer 方法生产 render 对象最终将 bunlde 渲染为字符串,将最终的 html 返回给客户端。

bundleRenderer.renderToString([context, callback]): ?Promise

step3 添加 entry-client.js,entry-server.js 入口文件

在 src 中除了这两个入口文件,其他的文件都是在客户端和服务端公用的。来看下这两个入口文件中分别干了什么。

大体的流程就是:服务端创建 vue 实例,将页面中的异步请求的数据拿到存储在容器中 --> 客户端接收到服务端发送的 html 以激活模式进行挂载,自动给根元素 #app 上添加 data-server-rendered="true" 特殊属性

main.js

import Vue from 'vue'
import App from './App.vue'
...
export function createApp() {
 // ...
 const app = new Vue({
 router,
 store,
 render: h => h(App)
 })
 return { app, router, store }
}

entry-server.js

import { createApp } from './main.js'
export default context => {
 // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
 // 以便服务器能够等待所有的内容在渲染前,
 // 就已经准备就绪。
 return new Promise((resolve, reject) => {
 const { app, router, store } = createApp()
 // 设置服务器端 router 的位置
 router.push(context.url)

 // 等到 router 将可能的异步组件和钩子函数解析完
 router.onReady(() => {
  const matchedComponents = router.getMatchedComponents()

  // 匹配不到的路由,执行 reject 函数,并返回 404
  if (!matchedComponents.length) {
  return reject({ code: 404 })
  }
  Promise.all(
  matchedComponents.map(component => {
   if (component.asyncData) {
   return component.asyncData({
    store,
    context,
    route: router.currentRoute
   })
   }
  })
  ).then(() => {
  // 在所有预取钩子(preFetch hook) resolve 后,
  // 我们的 store 现在已经填充入渲染应用程序所需的状态。
  // 当我们将状态附加到上下文,
  // 并且 `template` 选项用于 renderer 时,
  // 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。
  // 否则会导致客户端和服务端数据不统一造成渲染错误
  context.state = store.state
  resolve(app)
  }).catch(reject)
 }, reject)
 })
}

entry-client.js

import { createApp } from './main'
const { app, router, store } = createApp()

if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
 router.beforeResolve((to, from, next) => {
 const matched = router.getMatchedComponents(to)
 const prevMatched = router.getMatchedComponents(from)
 let diffed = false
 const activated = matched.filter((c, i) => {
  return diffed || (diffed = prevMatched[i] !== c)
 })

 if (!activated.length) {
  return next()
 }

 Promise.all(
  activated.map(component => {
  if (component.asyncData) {
   component.asyncData({
   store,
   route: to
   })
  }
  })
 )
  .then(() => {
  next()
  })
  .catch(next)
 })
 app.$mount('#app')
})

最后

完整代码参考 github地址

顺便贴上这张图

 怎么在vue中使用ssr+koa2构建服务端渲染

关于怎么在vue中使用ssr+koa2构建服务端渲染就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。


网页标题:怎么在vue中使用ssr+koa2构建服务端渲染
网站链接:http://myzitong.com/article/pcgeos.html