搭建自己的SSR
一、渲染一个Vue实例
-
mkdir vue-ssr
-
cd vue-ssr
-
npm init -y
-
npm i vue vue-server-renderder
-
server.js
const Vue = require('vue') const renderer = require('vue-server-renderer').createRenderer() const app = new Vue({ template: ` <div id="app"> <h1>{ {message}}</h1> </div> `, data: { message: '肖战' } }) renderer.renderToString(app, (err, html) => { if (err) throw err console.log(html) })
-
node server.js
,运行结果:<div id="app" data-server-rendered="true"><h1>肖战</h1></div>
data-server-rendered="true"
这个属性是为了将来客户端渲染激活接管的接口
二、结合到Web服务器中
使用express对所有的get请求都做同样的处理,new一个Vue,使用vue-server-renderer的renderToString的方法传入Vue实例,回调函数中的html就是最终得到的DOM结构
server.js
const Vue = require('vue')
const express = require('express')
const renderer = require('vue-server-renderer').createRenderer()
const server = express()
server.get('/', (req, res) => {
const app = new Vue({
template: `
<div id="app">
<h1>{
{message}}</h1>
</div>
`,
data: {
message: '肖战'
}
})
renderer.renderToString(app, (err, html) => {
if (err) {
return res.status(500).end('Internal Server Error.')
}
res.setHeader('Content-Type', 'text/html; charset=utf8') // 设置编码,防止乱码
res.end(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
${
html}
</body>
</html>
`)
})
})
server.listen(3000, () => {
console.log('server running at port 3000...')
})
三、使用HTML模板
1. 创建HTML模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
<!--vue-ssr-outlet-->
是占位符,为了接收将来要渲染的变量,不能写错,不能有多余的空格
2. js代码中的createRenderer方法指定模板文件
server.js
const Vue = require('vue')
const express = require('express')
const fs = require('fs')
const renderer = require('vue-server-renderer').createRenderer({
// 这里指定模板文件
template: fs.readFileSync('./index.template.html', 'utf-8')
})
const server = express()
server.get('/', (req, res) => {
const app = new Vue({
template: `
<div id="app">
<h1>{
{message}}</h1>
</div>
`,
data: {
message: '拉钩教育'
}
})
renderer.renderToString(app, (err, html) => {
// 此处的html参数是被模板文件处理过了的,可以直接输出到用户的页面上
if (err) {
return res.status(500).end('Internal Server Error.')
}
res.setHeader('Content-Type', 'text/html; charset=utf8') // 设置编码,防止乱码
res.end(html)
})
})
server.listen(3000, () => {
console.log('server running at port 3000...')
})
四、在模板中使用外部数据
Index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{
{
{ meta }}}
<title>{
{ title }}</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
使用两个花括号可以数据外部数据变量,而标签也会进行转义后输出在页面上。此时可以使用三个花括号原样输出数据,不会对标签进行转义处理
在js代码中给renderer.renderToString
增加第二个参数为外部数据对象
renderer.renderToString(app, {
title: '拉勾教育',
meta: `
<meta name="description" content="拉勾教育" >
`
}, (err, html) => {
if (err) {
return res.status(500).end('Internal Server Error.')
}
res.setHeader('Content-Type', 'text/html; charset=utf8') // 设置编码,防止乱码
res.end(html)
})
五、构建配置
1. 基本思路
2. 源码结构
src
├── components
│ ├── Foo.vue
│ ├── Bar.vue
│ └── Baz.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器
App.vue
<template>
<div id="app">
<h1>{
{message}}</h1>
<h2>客户端动态交互</h2>
<div>
<input v-model="message">
</div>
<div>
<button @click=