作前端的同窗们确定或多或少据说过
CSR
,SSR
,Prerender
这些名词,可是大多确定只是停留在据说过,了解过,略懂一点,可是,你真的理解这些技术吗?html这些名词具体是什么意思呢?前端
为何会产生这种技术,要解决的问题是什么呢?node
每种技术背后的原理又是什么呢?react
在了解这些概念以前,咱们要先了解一个熟知的概念,那就是 SPA(Single Page Application)
,没错,就是你们熟知的单页应用,其实 CSR、SSR、Prerender
都是基于 SPA
,关于 SPA
的概念我就很少阐述了。webpack
即,渲染过程所有交给浏览器进行处理,服务器不参与任何渲染。web
打包下来页面是这个样子:shell
流程:浏览器 --> 服务器 --> index.html(白屏) --> bundle.js --> images --> Renderredux
咱们来使用 create-react-app
来创建一个 web
工程,并在 Chrome
里使用 slow 3G
网络下作个实验:浏览器
能够看到,从页面空白到元素绘制用了足足 12 秒左右,这个白屏时间太可怕了,这也就是为何在 Web App
盛行的当下,包体积愈来愈大会致使白屏时间愈来愈长,你们想要优化这个现象的缘由。缓存
即,打包的时候就预先渲染页面,因此在请求到 index.html
就已是渲染过的内容
流程:浏览器 --> 服务器 --> index.html(预渲染的内容) --> Render --> bundle.js + images --> Render
咱们将刚刚的工程加入 prerender-spa-plugin
这个插件,再次运行看看结果
此次打包下来的主页 html 是这个样子的:
首页就已经预渲染好了,这时咱们再来运行一次看看:
此时能够看到,页面只用了 2 秒就已经渲染出元素,不会形成长时间的白屏问题
流程:浏览器 --> 服务器 --> 服务器执行渲染 --> index.html(实时渲染的内容)) --> Render --> bundle.js + images --> Render
可见 SSR
在服务端多作了一些实时渲染的操做,那么咱们此次运行下来回事什么结果呢?
能够看出来,SSR
和 Prerender
的效果一致,都能很好的减小白屏时间
从上面的实验,能够看出来,不管是 SSR
仍是 Prerender
,咱们要解决的问题主要是白屏时间太长的问题,这两种技术都是为了解决 CSR
的不足之处,那么这两种方案有什么区别?使用场景又有哪些呢?
P.S 其实另外一方面的缘由是,
CSR
对SEO
太不友好了,搜索引擎抓取不到关键信息,只能抓取一个毫无元素的白屏页面,会致使搜索引擎搜索不到你的页面信息进行推荐,SSR
和Prerender
都能很好的解决这个问题。(吐槽一下:SPA
的CSR
)
在作出选择以前,咱们必需要充分的了解二者的差别。
Prerender
原理是在构建阶段就将 html
页面渲染完毕,不会进行二次渲染,也就是说,当初打包时页面是怎么样,那么预渲染就是什么样,若是页面上有数据实时更新,那么浏览器第一次加载的时候只会渲染当时的数据,等到 JS 下载完毕再次渲染的时候才会更新数据更新,会形成数据延迟的错觉。
Prerender
须要预先指定须要渲染的页面,须要手动在 webpack 里设置
因此页面数量很大的状况下,想将每一个页面进行预渲染是很大工做量,并且打包时间会很长,还可能会遗漏
说了那么多利弊,那么,预渲染是怎么作到生成页面的呢?
作过爬虫的同窗确定知道 headless 的概念
Headless Chrome 在 Chrome59 中发布,用于在 headless 环境中运行 Chrome 浏览器,也就是在非 Chrome 环境中运行 Chrome。它将 Chromium 和 Blink 渲染引擎提供的全部现代 Web 平台功能引入命令行。 它有什么用处呢? headless 浏览器是自动测试和服务器环境的绝佳工具,您不须要可见的 UI shell。例如,针对真实的网页进行测试,建立网页的 PDF,或者只是检查浏览器如何呈现 URL。
Prerender
就是利用 Chrome
官方出品的 Puppeteer
工具,对页面进行爬取。它提供了一系列的 API, 能够在无 UI 的状况下调用 Chrome
的功能, 适用于爬虫、自动化处理等各类场景。它很强大,因此很简单就能将运行时的 HTML 打包到文件中。
原理是在 Webpack
构建阶段的最后,在本地启动一个 Puppeteer
的服务,访问配置了预渲染的路由,而后将 Puppeteer
中渲染的页面输出到 HTML
文件中,并创建路由对应的目录。
下面是流程图
目前来讲,主流的框架 React
、Vue
都已经支持 SSR
,只是配置会繁琐点,有人就会疑惑,框架还要支持 SSR
?
可事实是,正是由于现代 SPA
的 Virtual DOM
的存在,才能使 SSR
变成现实,可是,SSR
这种理念的实现,并不是易事。
咱们先来看看详细的 SSR
流程图:
能够看出,SSR
和 Prerender
的最大区别就在于,Prerender
是静态的,SSR
是动态的,SSR
会在服务端实时构建出对应的 DOM
。
这也是 SSR
的难点所在:同构(即服务器和浏览器共同构建)。
同构这个概念存在于 Vue
,React
这些新型的前端框架中,同构其实是客户端渲染和服务器端渲染的一个整合。咱们把页面的展现内容和交互写在一块儿,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互。
上面咱们说过,SSR
的工程中,React
代码会在客户端和服务器端各执行一次。你可能会想,这没什么问题,都是 JavaScript
代码,既能够在浏览器上运行,又能够在 Node
环境下运行。但事实并不是如此,若是你的 React
代码里,存在直接操做 DOM
的代码,那么就没法实现 SSR
这种技术了,由于在 Node
环境下,是没有 DOM
这个概念存在的,因此这些代码在 Node
环境下是会报错的。
可是就是因为 Virtual DOM
技术的存在,让这一切变成了可能,这里不过多介绍 Virtual DOM
,简单来讲,它就是一个普通的 JS
对象,只不过映射了 HTML DOM
的结构,React
在作页面操做时,实际上不是直接操做 DOM
,而是操做 Virtual DOM
,也就是操做普通的 JavaScript
对象,这就使得 SSR
成为了可能。
咱们能够直接在代码里判断当前的运行环境,若是是浏览器,就能够直接操做 DOM
,若是是服务器,就须要使用 Virtual DOM
生成 HTML
字符串。
浏览器路由和服务器路由彻底是两种不一样的运行机制,SPA
浏览器路由机制能够看这里,其实缘由很简单,在服务器端须要经过请求路径,找到路由组件,而在客户端需经过浏览器中的网址,找到路由组件,是彻底不一样的两套机制,因此这部分代码是确定没法公用。
因此 React
分别为浏览器端和服务器端分别提供了 BrowserRouter
和 StaticRouter
两种路由,经过 BrowserRouter
咱们可以匹配到浏览器即将显示的路由组件,对浏览器来讲,咱们须要把组件转化成 DOM
,因此须要咱们使用 ReactDom.render
方法来进行 DOM
的挂载。而 StaticRouter
可以在服务器端匹配到将要显示的组件,对服务器端来讲,咱们要把组件转化成字符串,这时咱们只须要调用 ReactDom
提供的 renderToString
方法,就能够获得 App
组件对应的 HTML
字符串。
尚未!
对于一个 React
应用来讲,路由通常是整个程序的执行入口。在 SSR
中,服务器端的路由和客户端的路由不同,也就意味着服务器端的入口代码和客户端的入口代码是不一样的。而入口则是 Webpack
进行打包完成的。
针对代码运行环境的不一样,要进行有区别的 Webpack
打包,咱们须要在 Webpack
的配置中加入 target: 'node'
,代表是服务器环境进行打包,除此以外,还有各类各样的配置须要解决。
Redux
来进行状态管理呢若是要用到 redux
进行全局状态管理,必定要记得写成这种形式:
由于服务器端的 Store
是全部用户都要用的,可是不能让全部用户共享 Store
,因此在服务器端渲染中,Store
的建立应该像下面这样,返回一个函数,每一个用户访问的时候,这个函数从新执行,为每一个用户提供一个独立的 Store
。
因为服务器不存在挂载元素这一辈子命周期,因此例如 React
的 componentDidMount
或者 VUE
的 mounted
生命周期都不会执行了,因此在服务端利用接口获取数据的时候,不能写入上述的生命周期中。
Prerender
,这是一种经过预览打包的方式构建页面,也不会增长服务器负担,但其余状况并不推荐。SSR
的实时构建会加重服务器 CPU
的消耗,需结合其余技术进行处理(例如 CDN,服务器缓存,负载均衡等)SEO
和加载速度有需求的,建议使用 SSR
CSR
可能更加适合,页面显示元素即绑定了操做,而 SSR
和 Prerender
虽然会提早显示页面,但此时页面元素没法操做,仍须要下载完 bundle.js
进行事件绑定才能执行固然在真正实现
SSR
架构的过程当中,难点有时不是实现的思路,而是细节的处理。好比说如何针对不一样页面设置不一样的title
和description
来提高SEO
效果,这时候,咱们其实能够用react-helmet
这样的工具帮咱们达成目标,这个工具对客户端和服务器端渲染的效果都很棒,值得推荐。还有一些诸如工程目录的设计,404,301 重定向状况的处理等等,不过这些问题,咱们只须要在实践中遇到的时候逐个攻破就能够了。