-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vue SSR初上手 #11
Comments
参考一段服务端渲染的js脚本: // FROM: https://juejin.im/post/5a9ca40b6fb9a028b77a4aac
const fs = require('fs')
const path = require('path')
const Koa = require('koa')
const KoaRuoter = require('koa-router')
const serve = require('koa-static')
const { createBundleRenderer } = require('vue-server-renderer')
const LRU = require('lru-cache')
const resolve = file => path.resolve(__dirname, file)
const app = new Koa()
const router = new KoaRuoter()
const template = fs.readFileSync(resolve('./src/index.template.html'), 'utf-8')
function createRenderer (bundle, options) {
return createBundleRenderer(
bundle,
Object.assign(options, {
template,
cache: LRU({
max: 1000,
maxAge: 1000 * 60 * 15
}),
basedir: resolve('./dist'),
runInNewContext: false
})
)
}
let renderer
const bundle = require('./dist/vue-ssr-server-bundle.json')
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
renderer = createRenderer(bundle, {
clientManifest
})
/**
* 渲染函数
* @param ctx
* @param next
* @returns {Promise}
*/
function render (ctx, next) {
ctx.set("Content-Type", "text/html")
return new Promise (function (resolve, reject) {
const handleError = err => {
if (err && err.code === 404) {
ctx.status = 404
ctx.body = '404 | Page Not Found'
} else {
ctx.status = 500
ctx.body = '500 | Internal Server Error'
console.error(`error during render : ${ctx.url}`)
console.error(err.stack)
}
resolve()
}
const context = {
title: 'Vue Ssr 2.3',
url: ctx.url
}
renderer.renderToString(context, (err, html) => {
if (err) {
return handleError(err)
}
console.log(html)
ctx.body = html
resolve()
})
})
}
app.use(serve('/dist', './dist', true))
app.use(serve('/public', './public', true))
router.get('*', render)
app.use(router.routes()).use(router.allowedMethods())
const port = process.env.PORT || 8089
app.listen(port, '0.0.0.0', () => {
console.log(`server started at localhost:${port}`)
}) |
一些注意的地方preFetch预渲染一般的,我们会在preFetch中预加载数据到vuex中,然后组件对其中的vuex state进行渲染(当然,你也可以不将数据放到vuex中,那样在钩子函数完成数据请求也是可以的)。在数据预获取时,需要注意数据的对称性,假设组件中依赖的vuex数据缺失,将导致组件的渲染失败。另外,需要注意前面的preFetch仅在一级组件中有效,在子组件中是不会调用的。 关于storage、document、window的使用在vue的生命周期函数中, 关于部分第三方组件引用时报错在vue入口文件引用一些第三方组件时,会提示 // webpack.client.config.js
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"client"',
})
// webpack.server.config.js
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"server"',
}) 通过这个环境变量,我们可以判断上下文环境是处于客户端还是服务器端,当判定在客户端时,我们就可以引入或渲染(v-if)第三方组件: <mavon-editor v-model="mdVal" v-if="isBrowser"></mavon-editor>
<script>
var mavonEditor = require("mavon-editor");
import "mavon-editor/dist/css/index.css";
export default {
components: {
mavonEditor
},
data() {
return {
mdVal: '',
isBrowser:
process.env.VUE_ENV === "client" || process.env.VUE_ENV !== "server",
}
}
}
</script> 如何动态修改标签标题如果要简单一点的方式,可以在组件内用路由监听或者路由钩子对 |
Vue SSR上手
本文将介绍如何使用
vue-server-renderer
进行服务端渲染,很多内容是搬运自官方文档,可以看作是笔记吧,对vue ssr的过程和原理有大致的描述。Vue SSR比起Vue SPA的优势:
我们都知道,浏览器在刚开始访问SPA应用时,服务器会返回一个基本的html骨架和一些js文件,html文件内没有网页的主体内容,需要浏览器解析js并渲染到页面中。这样,搜索引擎不仅不能抓取到关键信息,首屏加载时js还会阻塞页面渲染,导致白屏现象。在这种情况下,我们可以用服务端渲染进行优化。
上手前的注意点:
当然,坑点不仅仅是以上的内容,更多内容见后续
先来一个最简单的demo
先保证已经安装了插件:
npm install vue vue-server-renderer --save
开始一步步来完成吧:
和node配合使用:
然后我们创建一个模板文件:
<!--vue-ssr-outlet-->
标记的位置就是注入HTML文档的地方。我们还可以在模板中使用插值,比如上述的
meta
,我们可以这样注入:模板还支持一些高级特性(搬运至vue ssr指南):
当然,我们正常的开发不可能像上面这个demo那样简单粗暴,就像正常的vue开发也不会在一个html文件里面引用vue开发。
SSR的流程
由于vue ssr是同构的,所以在客户端和服务端都要在入口文件中创建vue实例,通过webpack分别进行打包,生成client bundle和server bundle。前者是客户端标记,等待拿到服务端渲染完成的数据后,混入完成初始化渲染;后者会在服务端上运行并生成预渲染的HTML字符串,再发送给客户端以完成初始化渲染。
vue ssr的常见目录结构
常用的webpack配置文件结构如下:
同时,还需要创建客户端的入口文件
entry-client.js
和服务端的入口文件entry-server.js
,以及通用的入口文件app.js
。通用配置:
通用配置:
setup-dev-server.js
这里的配置比较复杂,而且定义的自由度较高,不过多阐述。
server.js
这是整个ssr项目的入口文件。具体功能见上面的描述。定义的自由度较高,下面值放出一段最基础的配置(没有用到HMR),不过多阐述:
常见的优化参考下面:
缓存
流式渲染
手动资源注入
在
server.js
文件中,如果提供了template模板,那么资源注入是自动的。但是也可以选择不提供模板,手动注入,详情见:构建配置app.js
:通用入口文件的基本职责是创建vue实例,然后将其模块化导入到客户端和服务端的入口文件。所以,在通用入口文件中,我们将创建一个工程函数。并且,vuex和vue-router也在此创建实例(还需要引入vuex-router-sync进行store和router的同步)。
如下:
服务端每次请求渲染时,都会重写执行createApp方法,初始化store、router,不然数据不会更新。
entry-client.js
:它的主要工作其实很简单,就是创建vue实例,并挂载到DOM。
它的基本结构如下:
这里有个陌生的概念
window.__INITIAL_STATE__
,我们会在后续谈到。entry-server.js
:服务端每次渲染时,都会调用该入口文件,它原本的工作是创建vue实例,但是在此我们可以赋予它更多内容,比如服务端路由匹配和数据预获取:
上面函数的参数
context
等同于node中的ctx
,是一个全局上下文环境对象。数据获取和状态
在服务端渲染生成html前,我们需要预先获取并解析依赖的数据。同时,在客户端挂载(mounted)之前,需要获取和服务端完全一致的数据,否则客户端会因为数据不一致导致混入失败。如果在
beforeCreate
或created
时执行请求,由于这两个生命周期函数会在服务端执行(也就只有这两个vue生命周期函数会在服务端执行了,其他vue生命周期函数都是客户端中执行),且请求是异步的
,导致请求发出后,数据还没有返回,渲染就结束了。为了解决这个问题,预获取的数据要存储在状态管理器(store)中,以保证数据一致性。vue ssr有一个名为
asyncData
的函数,用来请求数据,它需要在服务端入口文件中预先配置,需要返回一个promise,等待所有请求都完成再渲染组件。然后在单独的视图组件中调用该方法,asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据并返回给当前组件。注意,由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。也可以将数据预获取放在路由钩子完成,比如:
逻辑配置组件的数据预获取
服务端的数据预获取
还记得前面提到的
window.__INITIAL_STATE__
吗?当我们预获取数据完成,就会将context.state
作为window.__INITIAL_STATE__
的状态,自动混入客户端:客户端的数据预获取
客户端数据预获取的方式有两种:在路由导航之前获取,在匹配待渲染的视图后再获取
通过这种策略,应用会在等待视图所需的数据全部解析之后,再传入数据并处理当前视图。好处是可以直接再数据准备就绪时,传入视图渲染完整内容,但是如果数据预获取的实践过长,用户在视图中就需要等待一段时间。
在此策略中,客户端数据预获取的逻辑是放在视图组件
beforeMount
中的,当路由导航触发后,立即切换视图,因此有更快的响应速度。 但是,在渲染视图时不能得到完整的数据,所以需要条件判断:使用以上哪种方式取决于用户体验场景,但是无论是哪种,在路由组件复用的情况下,更改路由params或query,也需要调用
asyncData
:vue ssr的坑
一套代码,两套执行环境
在vue的生命周期函数中,只有
beforeCreate
和created
会在ssr过程中执行,其他的生命周期函数只会在客户端执行。所以应该避免在这两个生命周期函数中产生全局副作用的代码,比如定时器。同时,由于前端代码会在后端中执行,而Node.js和浏览器JavaScript有区别,导致在前端视图中的部分JavaScript属性或方法在执行时会报错。比如在使用一些插件的时候会提示window
或document
是undefined
,在这种情况下,可以用vue-no-ssr
让相关组件不走ssrcookie不可用
关于vue ssr不可用的解决方案,可以参考:再说Vue SSR的Cookies问题
参考:
Vue SSR指南
解密Vue SSR
Vue SSR Demo
一个极简版本的 VUE SSR demo
带你走近Vue服务器端渲染(VUE SSR)
基于vue-ssr服务端渲染入门详解
理解vue ssr原理,自己搭建简单的ssr框架
The text was updated successfully, but these errors were encountered: