一个变相的服务端渲染系统

前端发展到如今,SPA应该已经被应用的很是广了。惋惜的是,咱们前进的是快,而人家搜索引擎爬虫跟用户的浏览器设备还跟不上脚步。辛辛苦苦写好的单页应用,结果到了SEO跟浏览器兼容这一步懵逼了。javascript

不少同窗确定都想过服务端渲染的问题。然而一看vue、react关于服务端渲染的文档,可能就被唬住了。以前写好的并不能无缝迁移。并且,每当有个项目,就须要去run一套node服务。固然,架构能力好些的朋友,能够作好集中化管理。html

因此,当我想在项目中,采用vue或者react的时候,就遇到这些很是大的阻力。正当我头疼脑热的时候呢,我发现了一条新途径。前端

在前不久呢,同事在群里分享了puppeteer,它GitHub的介绍以下:vue

Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome.java

大意就是说,一个提供操做Headless Chrome的API的node库。node

再具体的说,就是能在node环境中,经过一些API,来“模拟”真实chrome访问页面,并对其进行模拟用户操做、获取DOM等。react

那既然它可以像真实Chrome那样去访问页面而且输出渲染后的html,我为何不能经过它来给咱们作服务端渲染呢?git

设想一下,咱们有这样一个服务A,它可以像chrome同样访问指定页面,并把最终页面上的dom返回给你。github

而你本来的业务服务器B,只须要判断是爬虫,或者低版本IE来访问时,调取该服务,获得html,将html返回给用户,这就实现了服务端渲染。大体流程图以下:vue-router

有这样一个思路后,咱们就想办法来实践它。实践的过程,就是解决问题的过程。仔细想一想,咱们会遇到以下几个问题:

Q1: 即便是模拟Chrome去请求页面,不少时候视图也是异步渲染的。好比先请求列表接口,获得数据再渲染出列表DOM。这个时间,咱们并无办法把控。那这个服务,到底时候才应该把加载完成的HTML返回呢?

遇到问题时,首先能够看看人家的文档 Puppeteer API。欣喜的是,咱们找到了以下几个方法:

page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
page.waitForFunction(pageFunction[, options[, ...args]])
page.waitForNavigation(options)
page.waitForSelector(selector[, options])复制代码

咱们能够经过一些设定,让页面在某种状况下才返回。好比咱们经过设定 page.waitForSelector('#app'), 让页面出现 id="app" 的元素时,才把html内容返回。

或者经过设定 page.waitForFunction('window.innerWidth < 100'),当页面宽度小于100px时,才将此时的html内容返回。

经过这些方法,咱们就能有办法控制,想要输给爬虫的,是何时、什么样的页面。

Q2: 若是IE用户访问量比较大怎么办。咱们虽然经过这样的系统,让本渲染不出页面的部分浏览器(IE9如下)可以渲染出页面了。但这样的请求过程相对而言会更耗时,这不是很合理。

那咱们只要作一个缓存系统便好。每次请求,都会去判断此请求是否存在未过时的缓存HTML,若是存在,则直接返回缓存HTML,不然再去请求页面,保存缓存。

Q3: 虽然页面是出来了,IE用户仍是没办法作一些JS的交互。

这个咱们没办法在服务层上去解决了,但咱们能够在前端上作更友好的交互提示。若是判断用户是低版本IE,则出现一个小Tip,提示用户下载更好的浏览器,获取更好的体验。

Q4: 单页应用的路由可能是用锚点(哈希模式)来作的,而哈希参数,服务端没法获取,那就没办法请求正确的页面了。

这个有办法解决,能够采用HTML History模式的路由,如vue-router,而后路由连接最好以生成a标签+href的模式写在页面中,而不是onclick后js跳转,这样爬虫能最好的爬取整站页面。

当问题都想到办法解决后,咱们就能开始真正coding了。

啪啪啪,啪啪啪 => SSR-SERVICE

好,而后就行了,不到200行的代码,咱们就实现了一个 通用化的、服务化的、单页应用服务端渲染解决方案。

相关文章
相关标签/搜索