Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

移动端适配方案探究 #45

Open
jiangjiu opened this issue May 14, 2018 · 0 comments
Open

移动端适配方案探究 #45

jiangjiu opened this issue May 14, 2018 · 0 comments

Comments

@jiangjiu
Copy link
Owner

jiangjiu commented May 14, 2018

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

运营活动布局方案

思路

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

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

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

Viewport units 单位

简介

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

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

  • vw : 1vw 等于视口宽度的1%
  • vh : 1vh 等于视口高度的1%
  • vmin : 选取 vw 和 vh 中最小的那个
  • vmax : 选取 vw 和 vh 中最大的那个

视口单位区别于%单位,视口单位是依赖于视口的尺寸,根据视口尺寸的百分比来定义的;而%单位则是依赖于元素的祖先元素。

例如,在桌面端浏览器视口尺寸为650px,那么 1vw = 650 * 1% = 6.5px(这是理论推算的出,如果浏览器不支持0.5px,那么实际渲染结果可能是7px)。

兼容性

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

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

  • iphone 5s(ios8.3)
  • 三星note2(android4.3)
  • 小米2 (android5.0)

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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant