什么是SSR?
你可能会想到某游戏或者梯子,再或者某牛奶,没错我也是这么想的。简单理解就是原来由客户端生成的html,现在由服务端渲染后,在交给浏览器,这样可以理解为啥ssr的首屏渲染快了,
再就是ssr的SEO能力好,国内的搜索引擎好像都不太支持js动态网站,当然首页会给你收录,这样如果你的网站是动态的,seo相比会差很多。
与ssr相对的,还有预渲染(Prerendering, 预渲染直接吐真正的页面给浏览器。),其实我不太懂,我猜想可能是部分页面直接使用html页面
SSR优势?
https://ssr.vuejs.org/zh/#为什么使用服务器端渲染-ssr-?
不要为了使用SSR而使用SSR,SSR相比传统模板或者SPA开发来说,技术难度和运维成本都要高
Vue-SSR
vue的ssr主要借助vue-server-renderer,它能将Vue实例渲染为html,而核心就是将原来浏览器挂载Vue,交给服务端了。
安装:
cnpm install vue vue-server-renderer --save
PS:
vue-server-renderer
和vue
必须匹配版本。vue-server-renderer
依赖一些 Node.js 原生模块,因此只能在 Node.js 中使用。
基础的SSR项目参考:https://ssr.vuejs.org/zh/guide/#完整实例代码
基本的项目构建,参考下图:
基本的项目结构参考:
src
├── components
│ ├── Index.vue
│ ├── Detail.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器
app.js
导出一个工厂函数,用于创建新的,应用程序、router 和 store 实例
// 服务端入口
// 创建vue实例
import Vue from 'vue'
// 注意一定要使用App.vue而非App,不然会与app.js冲突
import App from './App.vue'
import createRouter from './router/index2'
import { createStore } from './store/store'
import { sync } from 'vuex-router-sync'
export default function createApp () {
const router = createRouter()
const store = createStore()
// 同步路由状态(route state)到 store
sync(store, router)
const app = new Vue({
router,
store,
render: h => h(App)
})
return { app, router, store }
}
entry-client.js
挂载应用程序,Vue 在浏览器端接管由服务端发送的静态 HTML,使其变为由 Vue 管理的动态 DOM 的过程。
import Vue from 'vue'
// 挂载、激活app
import createApp from './app'
const { app, router, store } = createApp()
// 客户端预取数据
Vue.mixin({
beforeMount () {
const { asyncData } = this.$options
if (asyncData) {
asyncData({
store: this.$store,
route: this.$route
})
}
}
})
// 当使用 template 时,context.state 将作为 window.__INITIAL_STATE__ 状态,
// 自动嵌入到最终的 HTML 中
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
router.onReady(() => {
app.$mount('#app')
})
entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此文件还可以执行服务器端路由匹配和数据预取逻辑
// 渲染首屏
import createApp from '../src/app'
// 5
export default context => {
return new Promise((resolve, reject) => {
const { app, router, store } = createApp()
// 进入首屏
router.push(context.url)
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
if (!matchedComponents.length) {
// eslint-disable-next-line prefer-promise-reject-errors
return reject({ code: 404 })
}
// 对所有匹配的路由组件调用 `asyncData()`
Promise.all(matchedComponents.map(Component => {
if (Component.asyncData) {
return Component.asyncData({
store,
route: router.currentRoute
})
}
})).then(() => {
// 在所有预取钩子(preFetch hook) resolve 后,
// 我们的 store 现在已经填充入渲染应用程序所需的状态。
// 当我们将状态附加到上下文,
// 并且 `template` 选项用于 renderer 时,
// 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。
context.state = store.state
resolve(app)
}).catch(reject)
}, reject)
})
}
与传统SPA应用相比,它是直接使用服务端启动,因此你需要一个服务端脚本:
// eslint-disable-next-line no-unused-vars
const Vue = require('vue')
const express = require('express')
const app = express()
const fs = require('fs')
const path = require('path')
// 创建渲染器
const { createBundleRenderer } = require('vue-server-renderer')
const serverBundle = require('../dist/server/vue-ssr-server-bundle.json')
const clientManifest = require('../dist/client/vue-ssr-client-manifest.json')
const renderer = createBundleRenderer(serverBundle, {
runInNewContext: false,
template: fs.readFileSync('../public/index.temp.html', 'utf-8'), // 宿主模板文件
clientManifest
})
// 中间件处理静态文件请求
app.use(express.static(path.resolve(__dirname, '../dist/client'), { index: false }))
// 路由处理交给vue
app.get('*', async (req, res) => {
try {
const context = {
url: req.url,
title: 'ssr test'
}
// 将vue实例转为html
const html = await renderer.renderToString(context)
// console.log(html)
res.send(html)
} catch (e) {
res.send('服务端异常')
}
})
app.listen(8000, () => {
console.log('listen 8000')
})
这里省去了webpack配置。
效果:
完整项目地址:点击访问
评论列表
已有2条评论
GH
老哥你这项目clone下来没法运行,少了.env吧,而且npm run serve也没有