解决服务端渲染首屏渲染自适应浏览器窗口大小

1,679 阅读2分钟

背景:

在某些情况下, 使用服务端渲染的应用需要在首屏渲染时获取客户端的某些参数,比如需要拿到window.innerWidth值,然后与设计稿宽度计算得到根元素的font-fize值,与px2rem配合使用,这样我们就不再需要人工计算rem值了,从而使服务端首屏渲染能够自适应窗口大小。 对于我来说,以前开发都是客户端渲染的单页应用SPA,如react和vue,可以在页面挂载之前去拿到window.innerWidth值,这种情况自然不会遇到。但现在用服务端渲染的项目,把首屏渲染的任务放到了服务器上,又需要拿到客户端的window.innerWidth来自适应响应首屏界面,这也反映出了服务端渲染的一个大大的缺点。

原因分析:

服务端渲染路线:

服务端渲染是客户端直接渲染在服务端拼接好的html,再去下载js和css,补充剩下的一部分页面,所以服务端渲染需要注意一点,不能在代码中随意访问window对象,因为在服务端上是访问不到window对象的,要要等到页面挂载上去后才能访问window对象,但是当我们等到useEffect中才去根据window‘大小计算根font-size,此时html页面其实已经渲染过一次了(按照设置的默认根font-size),从而发生了页面元素一下子从缩小到放大的效果(或者从放大到缩小的效果,取决于设置的默认font-size值)

解决方案:

在服务端返回给客户端的html页面头部就去执行根据window大小计算根font-size大小的js代码,然后配置px2rem,根据计算的根font-size自动转换 核心代码

// calcHtmlFontSize.js
/**
 * 根据窗口计算根fontsize
 * @param {*} doc documnet
 * @param {*} win window
 * @param {*} designWidth 设计稿屏幕宽度,default:750
 */
const calcHtmlFontSize = function (doc, win, designWidth = 750) {
  const docEl = doc.documentElement;
  const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  const recalc = function () {
    // 真实屏幕宽
    const width = win.innerWidth;
    // 这里是假设在750px宽度设计稿的情况下,1rem = 100px;
    if (width <= 750) {
      docEl.style.fontSize = `${100 * (width / designWidth)}px`;
    } else {
      docEl.style.fontSize = '100px';
    }
  };
  if (!doc.addEventListener) return;
  // 根据是否有动态修改分辨率的需求添加
  win.addEventListener(resizeEvt, recalc, false);
  doc.addEventListener('DOMContentLoaded', recalc, false);
  recalc();
};
calcHtmlFontSize(document, window);

然后在index.html模版文件中头部引入calcHtmlFontSize.js文件

<Head>
  <title>{title}</title>
  <meta charSet="utf-8" />
  <link rel="shortcut icon" type="image/x-icon" href="/spider.ico" />
  <script src="/calcFontsize.js" />
  <meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>