转载服务端渲染(SSR) - 知乎 (zhihu.com)
一、什么是浏览器端渲染 (CSR)?
CSR是Client Side Render简称;页面上的内容是我们加载的js文件渲染出来的,js文件运行在浏览器上面,服务端只返回一个html模板。
CSR加载图
二、什么是服务器端渲染 (SSR)?
SSR是Server Side Render简称;页面上的内容是通过服务端渲染生成的,浏览器直接显示服务端返回的html就可以了。
SSR加载图
本文以Vue.js 做为演示框架来区分SSR和CSR。默认情况下,Vue.js可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。
附:vue-ssr官方文档
基本用法 | Vue SSR 指南ssr.vuejs.org/zh/guide/
三、不同渲染方式在浏览器解析情况
从输入页面URL到页面渲染完成大致流程为:
解析URL
浏览器本地缓存
DNS解析
建立TCP/IP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器根据深度遍历的方式把html节点遍历构建DOM树
遇到CSS外链,异步加载解析CSS,构建CSS规则树
遇到script标签,如果是普通JS标签则同步加载并执行,阻塞页面渲染,如果标签上有defer / async属性则异步加载JS资源
将dom树和CSS DOM树构造成render树
渲染render树
performance.timing
CSR-浏览器performance情况
SSR-浏览器performance情况
FP:首次绘制。用于标记导航之后浏览器在屏幕上渲染像素的时间点。这个不难理解,就是浏览器开始请求网页到网页首帧绘制的时间点。这个指标表明了网页请求是否成功。
FCP:首次内容绘制。FCP 标记的是浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG 甚至
FMP:首次有效绘制。这是一个很主观的指标。根据业务的不同,每一个网站的有效内容都是不相同的,有效内容就是网页中"主角元素"。对于视频网站而言,主角元素就是视频。对于搜索引擎而言,主角元素就是搜索框。
TTI:可交互时间。用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点。应用可能会因为多种原因而无法响应用户输入:①页面组件运行所需的JavaScript尚未加载完成。②耗时较长的任务阻塞主线程
根据上图devtool时间轴的结果,虽然CSR配合预渲染方式(loading、骨架图)可以提前FP、FCP从而减少白屏问题,但无法提前FMP;SSR将FMP提前至js加载前触发,提前显示网页中的"主角元素"。SSR不仅可以减少白屏时间还可以大幅减少首屏加载时间。
附:首屏时间获取方法
前端 白屏时间如何获取?18 赞同 · 9 评论回答
四、node服务(server.js)
第一步 利用express框架写一个简单node服务
Express是基于Node.js平台,快速、开放、极简的 Web 开发框架
/*第一步 利用express框架写一个简单node服务*/letexpress=require('express');letapp=express();app.get('*',function(req,res){res.send('hello world');});constport=process.env.PORT||8080app.listen(port,()=>{console.log(`server started at localhost:${port}`)})
附:express文档
Express - 基于 Node.js 平台的 web 应用开发框架www.expressjs.com.cn/
第二步 利用vue-server-renderer提供的createRenderer将vue与node结合
renderer.renderToString(vm, context?, callback?): ?Promise
将 Vue 实例渲染为字符串。上下文对象 (context object) 可选。回调函数是典型的 Node.js 风格回调,其中第一个参数是可能抛出的错误,第二个参数是渲染完毕的字符串。
/*第一步 利用express框架写一个简单node服务第二步 利用vue-server-renderer提供的createRenderer将vue与node结合*/
let express=require('express');
let app=express();
constVue=require('vue')constrenderer=require('vue-server-renderer').createRenderer()app.get('*',function(req,res){render(req,res)});functionrender(req,res){constapp=newVue({data:{url:req.url},template:`
req.url:{{ url }}`})renderer.renderToString(app,(err,html)=>{if(err){res.status(500).end('Internal Server Error')return}else{res.end(`${html}`)}})}constport=process.env.PORT||8080app.listen(port,()=>{console.log(`server started at localhost:${port}`)})
第三步 读入index.template.html文件
创建 renderer 时提供一个页面模板。多数时候,我们会将页面模板放在特有的文件中,例如index.template.html