背景:
在某些情况下, 使用服务端渲染的应用需要在首屏渲染时获取客户端的某些参数,比如需要拿到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>