自适应服务端渲染(服务端根据客户端环境自适应地响应首屏)

前言

相信不少人看了标题都以为一脸懵逼,我就不卖关子用人话解释一下吧:
就是在某些状况(需求)下的服务端渲染应用须要获取客户端的某些参数(window.innerWidth之类的)使服务端首屏渲染可以响应适合的内容(根据window.innerWidth作响应式网站),因此我把这种作法称做自适应服务端渲染(RSSR)(本身编的)。javascript

这种需求仍是有的,好比rem布局,须要客户端加载js获取当前窗口宽度来设置根元素的font-size,对于我来讲,之前开发都是客户端渲染的单页应用,这种状况天然不会遇到。但如今用服务端渲染的项目,把首屏渲染的任务放到了服务器上,又须要拿到客户端的window.innerWidth来自适应响应首屏界面,这也反映出了服务端渲染的一个大大的缺点。html

自适应服务端渲染方案

对于客户端首屏请求传参的方式我第一个想到的就是Service Worker,咱们先大体了解一下Service Worker的做用和使人振奋的特性吧:java

Service Worker 是浏览器在后台独立于网页运行的脚本,它打开了通向不须要网页或用户交互的功能的大门。 如今,它们已包括如推送通知和后台同步等功能。 未来,Service Worker 将会支持如按期同步或地理围栏等其余功能。 本文提到的是关于拦截和处理网络请求的特性。web

Service Worker 相关特性:编程

  • 它是一种 JavaScript Worker,没法直接访问 DOM。 Service Worker 经过响应 postMessage 接口发送的消息来与其控制的页面通讯,页面可在必要时对 DOM 执行操做。
  • Service Worker 是一种可编程网络代理,让您可以控制页面所发送网络请求的处理方式。

了解更详细内容可访问 developers.google.com/web/fundame…浏览器

经过在浏览器开出的service worker即可拦截首屏请求带上相关headers 再fetch,这样服务器就能够知道客户端的参数作相应了。服务器

注册Service Worker

咱们先在首屏文档中注册service worker:网络

<script> if ('serviceWorker' in navigator) { // 带上须要的客户端参数 navigator.serviceWorker.register('/service-worker.js?window-width=' + window.innerWidth); } </script>
复制代码

因为service-worker.js里的this上下文(ServiceWorkerGlobalScope)没有相关window对象或详细的客户端内容,因此只能从注册时带上query参数,service-workder线程才能取到客户端参数。布局

也能够在register后postMessage让service-worker接收到。post

而后编写service-worker.js:

// 解析service-worker地址的参数
const query = (function () {
  var search = location.search
  if (search.indexOf('?') == 0) {
    search = search.substring(1);
  }
  return search
    ? search.replace('?', '').split('&').reduce(
      (o, v) => {
        const exec = v.split('=');
        o[exec[0]] = exec[1];
        return o;
      },
      {},
    )
    : {};
})()

// 取得window-width
const windowW = query[’window-width’];

this.addEventListener('fetch', function(event) {
  const first_path = event.request.url.split('/')[3]
  // 判断是根路径即表明当前请求的是首屏文档
  if (first_path === '') {
    var new_req = new Request(url, {
      // 自定义header
      headers:   {
        'window-width': windowW
      }
    });
    // 拦截改造header后发起请求
    return fetch(new_req).then(function() {
      ...
    })
  }
})

复制代码

接下来只要在服务端拿到这个header就能够自适应服务端渲染啦!

缺陷

使用Service Worker这个方案有个致命的缺陷,它不是实时的,浏览器开出的Service Worker是一个独立线程,不能彻底阻塞主线程拦截或者不能挡在主线程以前就开始拦截控制页面,因此每次都会等主线层解析首屏文档的navigator.serviceWorker.register, 才轮到service worker线程进行install、activate操做以后,才能用客户端参数拦截请求,因而就会形成永远晚一步的局面(当前刷新后看到的是上一次的内容)。

这不是Service Worker的设计缺陷,只是Service Worker本意并非对这种需求而已。

但这是个很好的思路,说不定之后浏览器会提供这种主线程拦截器之类的呢,兴许还能发现更多Service Worker的新姿式呢。

永远保持一颗探索的心,才能迸发新鲜灵感

相关文章
相关标签/搜索