阅读建议:建议通过左侧导航栏进行阅读
文章简介:本文是Vue.js
服务器端渲染的另一种解决方案-SSR
(Server-Side Rendering
)学习笔记
Vue SSR是什么
官方文档解释:
Vue.js
是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出Vue
组件,进行生成DOM
和操作DOM
。然而,也可以将同一个组件渲染为服务器端的HTML
字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
服务器渲染的Vue.js
应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。
Vue SSR
(Vue.js Server-Side Rendering
)是Vue.js
官方提供的服务端渲染解决方案- 使用
SSR
,可以构建基于vue.js的同构应用
Vue SSR使用场景
在使用SSR之前,要从以下两方面考虑是否真的需要它。
技术层面
- 有 利于SEO
- 更快的首屏渲染速度
业务层面
- 不适合管理系统
- 适合移动网站和门户资讯类网站,如企业官网、知乎、简书等
Vue SSR实现方案
基于 Vue SSR 官方文档提供的解决方案
官方方案具有更直接的控制应用程序的结构,更深入底层,更加灵活,同时在使用官方方案的过程中,也会对Vue SSR
有更加深入的了解。
该方式需要你熟悉 Vue.js
本身,并且具有 Node.js
和 webpack
的相当不错的应用经验。
Nuxt.js 开发框架
Nuxt.js
提供了平滑的开箱即用的体验,它建立在同等的Vue.js
技术栈之上,但抽象出很多模板,并提供了一些额外的功能,例如静态站点生成。通过 Nuxt.js
可以快速的使用 Vue SSR
构建同构应用。
Vue SSR基于官方文档的基本使用
渲染一个Vue实例
体会服务端渲染中最基础的工作-模板渲染,了解如何使用
Vue SSR
将一个 Vue 实例渲染为 HTML 字符串,也就是如何在服务端使用 Vue.js 的方式解析替换字符串。
新建一个项目,在项目根目录下依次执行以下命令
//初始化项目,新建package.json
yarn init
//安装项目基本依赖包
yarn add vue vue-server-renderer express
在项目根目录下分别新建server.js
和index.html
server.js
const Vue = require('vue');
const express = require('express');
const fs = require('fs');
const renderer = require('vue-server-renderer').createRenderer({
//使用utf-8进行编码的index.html作为渲染模板
template: fs.readFileSync('./index.html', 'utf-8')
});
const server = express();
//设置路由,客户端以get请求网站根路径
server.get('/', (req, res) => {
//创建一个vue实例
const app = new Vue({
template:`
<div>
<h1>{
{message}}</h1>
</div>
`,
data: {
message: '创建一个vue实例'
}
});
//将vue实例转换为html字符串,并发送给客户端
renderer.renderToString(app, {
title: '服务器端渲染',
meta: `<meta name="desc" content="服务器">`
}, (err, html) => {
if(err) {
res.status(500).end('server error...');
};
res.end(html);
})
})
//启动web服务
server.listen(3000, () => {
console.log('server running...')})
index.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>
使用nodemon server.js(nodemon需要在本机安装)启动项目。
在客户端访问http://localhost:3000/
,可以看到渲染结果,页面结构:
案例总结:
- 解决页面显示乱码的两种方案
1、设置响应头
res.setHeader('Content-Type', 'text/html;charset=utf8');
res.end(html);
2、发送完整的html
页面,使用charset="UTF-8"
进行编码
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>
`)
- 服务端向客户端发送完整页面的两种方式
1、利用ES6的模板语法直接在请求响应中发送完整页面模板
2、使用渲染模板,结合vue-server-renderer
解析替换字符串,如server.js就采用这种方式
注意:
1.使用渲染模板接收外部数据时,如果修改的是静态模板如head中的内容,需要重启项目才能生效,因为模板是在项目启动时获取的
2.如果需要将外部数据渲染为html标签,需要使用三个“{}”,如{ { { meta }}}
Vue SSR同构渲染构建
构建流程
源码结构
1.使用webpack
打包的原因
- 通常
Vue
应用程序是由webpack
和vue-loader
构建,并且许多webpack
特定功能不能直接在Node.js
中运行(例如通过file-loader
导入文件,通过css-loader
导入CSS
) - 尽管
Node.js
最新版本能够完全支持ES2015
特性,我们还是需要转译客户端代码以适应老版浏览器。这也会涉及到构建步骤。
对于客户端应用程序和服务器应用程序,我们都要使用 webpack
打包 - 服务器需要「服务器 bundle
」然后用于服务器端渲染(SSR
),而「客户端 bundle
」会发送给浏览器,用于混合静态标记。
2.使用webpack的源码结构
使用 webpack 来处理服务器和客户端的应用程序,大部分源码可以使用通用方式编写,可以使用 webpack 支持的所有功能,推荐的源码结构:
src
├── components
│ ├── Foo.vue
│ ├── Bar.vue
│ └── Baz.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器
在项目根目录下新建src
目录
src/app.vue
<template>
<div id="app">
<h1>{
{ message }}</h1>
<h2>客户端动态交互</h2>
<div>
<input v-model="message" />
</div>
<div>
<button @click="onClick">点击按钮</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
message: "创建一个vue实例",
};
},
methods: {
onClick() {
console.log("---点击按钮---");
},
},
};
</script>
src/app.js
app.js 是我们应用程序的「通用 entry」,简单地导出一个工厂函数createApp 函数,用于创建新的应用程序、router 和 store 实例。
/**
* 通用启动入口
*/
import Vue from 'vue'
import App from './App.vue'
// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
const app = new Vue({
// 根实例简单的渲染应用程序组件。
render: h => h(App)
})
return {
app }
}
src/entry-client.js
客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中。
import {
createApp } from './app'
// 客户端特定引导逻辑……
const {
app } = createApp()
//将创建的vue实例绑定
app.$mount('#app')
src/entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,它不会做太多事情 - 但是稍后我们将在此执行服务器端路由匹配 (server-side route matching) 和数据预取逻辑 (data pre-fetching logic)
import {
createApp } from './app'
export default context => {
const