一、普通页面
众所周知,当用户输入url到浏览器展示页面会经历以下流程:
浏览器找服务器拿静态页面(HTML)
浏览器找服务器(也可以是CDN)拿CSS
浏览器找服务器(也可以是CDN)拿JS
执行JS,请求数据就是在这一步
拿请求到的数据渲染页面。
显而易见,页面最终渲染出来之前需要经过多次请求,且直到拿到数据页面才能最终渲染完成。
此外,如果采用了vue/react等mvvm框架,由于使用了virtual dom的缘故,html是靠js生成的,于是在获取到所有js并加载完成之前,页面都无法展示,这也就造成了单页面应用的首页白屏问题。
二、服务端渲染/预渲染
mvvm框架使用js生成页面这一步是如此耗时,那我们是不是可以把这一步从浏览器挪到客户端来做呢?
我们把这种做法称之为服务端渲染/预渲染;是服务端渲染还是预渲染取决于渲染发生在请求数据前还是请求数据后。
用户请求前的服务器渲染即为「预渲染」。
用户请求后的服务器渲染即为「服务端渲染」。
知道了服务端渲染与预渲染的区别,那么我们该什么时候使用服务端渲染,什么时候使用预渲染呢?
三、预渲染
当我们需要构建一个静态页面,这个页面并不需要与服务端进行数据交互,不需要进行请求时,我们就可以采用预渲染,让服务端将页面渲染(js生成html)的工作完成,就能直接对浏览器输出html。
因为浏览器是直接拿到html而不是像mvvm框架那样等待js生成html,因此浏览器不需要等待js加载完成就可以展现页面,而且由于预渲染可以让服务端直接输出渲染好的html,它还能解决单页面应用的SEO问题。
实现(vue)
首先npm init出一个目录,我这边直接npm init ssr来创建一个名为ssr的项目。
然后npm install这些东西:
- npm install express
- npm install vue
- npm install vue-server-renderer
现在我们需要使用express来创建一个node服务,在ssr目录下创建index.js,并写出这些代码:
const express = require("express");
const app = express();
app.get('*',(request,respones) => {
respones.end("ok");
})
app.listen(3000,() => {
console.log("好耶!")
});
再改一下package.json,方便我们把服务跑起来
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
}
现在可以在命令行执行npm start,然后浏览器访问localhost:3000,看到“ok”就说明服务跑起来了。
我们的目标是把vue给渲染成html输出出去,这就是我们引入vue-server-renderer的原因,执行vue-server-renderer的createRenderer方法,就能创建一个渲染实例,它能将vue实例给转换成html的形式,然后就可以被服务端输出出去。
那我们现在需要的就是一个vue实例了,我们可以new出一个vue实例,然后将其作为参数放到渲染实例的renderToString方法中,renderToString方法就是我们将vue转换成html的秘密所在。
我们把index.js改成这样: