jiangjiu / blog-md

前端/健身 思考与笔记~
https://github.com/jiangjiu/blog-md/issues
86 stars 6 forks source link

移动端适配方案探究 #45

Open jiangjiu opened 6 years ago

jiangjiu commented 6 years ago

分享过一个keynote,大部分的内容都在keynote里,以下内容是个简单的补充。 移动端适配方案探究.zip

运营活动布局方案

思路

使用vw实现全尺寸适配布局,不支持vw单位的使用JS动态设置font-size优雅降级。 业务组件开发时,使用rem单位开发。

因运营活动的特殊性,暂不考虑:

  1. min-width/max-width的实现(本方案可以做到)
  2. 字体使用px(以阅读内容为主的可以考虑)

Viewport units 单位

简介

Viewport units又称视口单位,指的是浏览器的可视区域。

根据CSS3规范,视口单位主要包括以下4个:

视口单位区别于%单位,视口单位是依赖于视口的尺寸,根据视口尺寸的百分比来定义的;而%单位则是依赖于元素的祖先元素。 例如,在桌面端浏览器视口尺寸为650px,那么 1vw = 650 * 1% = 6.5px(这是理论推算的出,如果浏览器不支持0.5px,那么实际渲染结果可能是7px)。

兼容性

可以看到,ios6.1+及安卓4.4+原生支持。

QA团队测试机器,以下兼容性较差机型的手百端内测试已通过:

一句话总结:大部分机型在大部分浏览器环境中都可以很好的支持,有少数机型在特定浏览器下存在兼容性问题。

vw + rem 协同

既然vw可以作为大小的单位,为什么不单独使用,而是和rem方案协同呢? 理由有三个:

  1. rem毫无兼容性压力(android2.1+),这意味着只需设置正确的根元素font-size,rem可以给出正确的元素大小
  2. 开发时只使用rem单位,降低学习成本及构建复杂度
  3. vw+rem配合body可以实现最大最小宽度的限制(我们的业务暂时无需考虑)

优缺点

谈到优缺点,就和春玩内容节的实现(rem动态计算)来做个对比吧。

vw+rem方案的优点:

  1. 全尺寸适配
  2. 开发同学无感知

vw+rem方案的劣势:

  1. 不可避免的css+js脚本内联方案(对于兼容性的检测,暂时没有更好的办法)
  2. 强依赖于css处理器编译和构建时处理(未必是缺点)

相比内容节方案的优势:

  1. 解决了画面抖动问题(在低端安卓机器明显感知)。
  2. 极致性能,减少了几十倍的执行时间,减少一次layout(不仅仅是执行时间,html解析阻塞时间变少,意味着浏览器可以更快的并行加载其他资源)。
  3. 对兼容性不好的机型做回退,而不是对所有机型都使用兼容性好,但性能体验较差的方案(这一条尤为重要,其他总结方案也应该如此这一条尤为重要,其他总结方案也应该如此这一条尤为重要,其他总结方案也应该如此)。

相比内容节方案的劣势:

想不出来

code

源码:

// 测试是否支持vw
(function (doc, win) {
    var dummy = doc.createElement('_').style;
    dummy.width = '1vm';
    if (dummy.width) {
        return;
    }
    // 不支持的动态设置font-size
    var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize',
        recalc = function () {
            var clientWidth = docEl.clientWidth;
            if (!clientWidth) {
                return;
            }
            // 12份
            docEl.style.fontSize = (clientWidth / 20)  + 'px';
        };
    recalc();
    win.addEventListener(resizeEvt, recalc, false);
})(document, window);

压缩版

!function(e,n){var t=e.createElement("_").style;if(t.width="1vm",!t.width){var i=e.documentElement,o="orientationchange"in n?"orientationchange":"resize",a=function(){var e=i.clientWidth;e&&(i.style.fontSize=e/20+"px")};a(),n.addEventListener(o,a,!1)}}(document,window);

对比测试及分析

测试环境

  1. chrome最新版浏览器 隐私窗口 (禁掉所有插件)
  2. performance面板 cpu性能调至6x slowdown 模拟移动端性能

内容节方案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">

    <style>
    html {
        font-size: 5vw;
    }

    p {
        width: 12rem;
        height: 12rem;
        font-size: 5rem;
    }
    </style>
</head>
<body>
<script>
let start, end;
(function () {
    start = performance.now();

    function fixAutoResetRem() {
        document.documentElement.style.fontSize = document.documentElement.clientWidth / 12 + 'px';
        setTimeout(function () {
            var oDiv = document.createElement('div');
            oDiv.style.width = '100%';
            document.body.appendChild(oDiv);
            var bDiv = document.createElement('div');
            bDiv.style.width = '12rem';
            document.body.appendChild(bDiv);
            var rfs = document.documentElement.clientWidth / 12;
            document.documentElement.style.fontSize = oDiv.clientWidth / bDiv.clientWidth * rfs + 'px';
            document.body.removeChild(oDiv);
            document.body.removeChild(bDiv);
            end = performance.now();
            console.log('异步执行完毕: ' + (end - start));
        }, 0);
    }

    window.addEventListener('resize', fixAutoResetRem);
    fixAutoResetRem();

})();
</script>
<p>hhh</p>
</body>
</html>

内容节方案测试情况

上图可知,内联script标签执行完毕平均耗时在40-70ms,除了解析html,另外引发了一次layout。

vw+rem 方案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
    <script>
    (function (doc, win) {
        var start = performance.now();
        var dummy = doc.createElement('_').style;
        dummy.width = '1vw';
        if (dummy.width) {
            var end = performance.now();
            console.log('支持:' + (end - start));
            return;
        }

        var docEl = doc.documentElement,
            resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize',
            recalc = function () {
                var clientWidth = docEl.clientWidth;
                if (!clientWidth) {
                    return;
                }
                docEl.style.fontSize = (clientWidth / 20) + 'px';
            };
        recalc();
        win.addEventListener(resizeEvt, recalc, false);
        var end = performance.now();
        console.log(end - start);
    })(document, window);
    </script>
    <style>
    html {
        font-size: 5vw;
    }

    p {
        width: 12rem;
        height: 12rem;
        font-size: 5rem;
    }
    </style>
</head>
<body>
<p>hhh</p>
</body>
</html>

vw+rem 方案测试情况

上图可知,内联script标签执行完毕平均耗时在1-5ms,没有额外的layout。

性能总结

  1. 执行时间减少几十倍,减少了一次额外的layout,总到达时间提前约50-200ms