深入探究flexiblejs

发布时间:2023-05-18

一、flexiblejs原理

flexiblejs是一款基于淘宝移动端适配方案的自适应方案,在移动端设备中,通过重新计算文档的根节点字体大小,实现了不同屏幕尺寸下的页面自适应。 具体来说,它通过以下几个步骤来实现自适应:

  1. 获取设备的屏幕宽度,并计算出font-size的大小。
(function(win, lib) {
    var timer,
        doc = win.document,
        docElem = doc.documentElement,
        vpMeta = doc.querySelector('meta[name="viewport"]'),
        flexMeta = doc.querySelector('meta[name="flexible"]'),
        dpr = 0,
        scale = 0,
        tid;
    // 获取设备CSS像素比
    if (vpMeta) {
        console.warn('将根据已有的meta标签来设置缩放比例');
        var initial = vpMeta.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (initial) {
            scale = parseFloat(initial[1]);
            dpr = parseInt(1 / scale);
        }
    }
    // 获取flexible配置信息
    if (flexMeta) {
        var flex = flexMeta.getAttribute('content');
        if (flex) {
            var arr = flex.split(';');
            for (var i = 0; i < arr.length; i++) {
                var item = arr[i].split('=');
                if (item.length == 2) {
                    switch (item[0]) {
                        case 'initial-dpr':
                            dpr = parseFloat(item[1]);
                            scale = parseFloat((1 / dpr).toFixed(2));
                            break;
                        case 'maximum-dpr':
                            var max = parseFloat(item[1]);
                            if (dpr > max) {
                                dpr = max;
                                scale = parseFloat((1 / dpr).toFixed(2));
                            }
                            break;
                        case 'design-width':
                            lib.designWidth = parseFloat(item[1]);
                            break;
                        case 'maxWidth':
                            lib.maxWidth = parseFloat(item[1]);
                            break;
                        case 'disableScale':
                            lib.disableScale = item[1] === 'true';
                            break;
                    }
                }
            }
        }
    }
    // 设置data-dpr属性,方便调试
    docElem.setAttribute('data-dpr', dpr);
    // 设置viewport
    if (!vpMeta) {
        vpMeta = doc.createElement('meta');
        vpMeta.setAttribute('name', 'viewport');
        vpMeta.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
        if (docElem.firstElementChild) {
            docElem.firstElementChild.appendChild(vpMeta);
        } else {
            var div = doc.createElement('div');
            div.appendChild(vpMeta);
            doc.write(div.innerHTML);
        }
    } else {
        vpMeta.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    }
    // 设置根字体大小
    function refreshRem() {
        var width = docElem.getBoundingClientRect().width;
        if (lib.maxWidth && (width / dpr > lib.maxWidth)) {
            width = lib.maxWidth * dpr;
        }
        var rem = width / 10;
        docElem.style.fontSize = rem + 'px';
        lib.rem = win.rem = rem;
    }
    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    win.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }
    }, false);
    refreshRem();
    lib.dpr = win.dpr = dpr;
    lib.refreshRem = refreshRem;
    lib.rem2px = function(d) {
        var val = parseFloat(d) * this.rem;
        if (typeof d === 'string' && d.match(/rem$/)) {
            val += 'px';
        }
        return val;
    };
    lib.px2rem = function(d) {
        var val = parseFloat(d) / this.rem;
        if (typeof d === 'string' && d.match(/px$/)) {
            val += 'rem';
        }
        return val;
    };
})(window, window['lib'] || (window['lib'] = {}));
  1. 设置viewport的meta标签。
vpMeta.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  1. 设置根字体大小。
docElem.style.fontSize = rem + 'px';

二、flexiblejs还有人用吗

随着CSS3的普及,越来越多的开发者开始使用CSS3的媒体查询来实现移动端适配。不过,flexiblejs依然是一款优秀的适配方案,尤其适用于需要兼容旧设备的场景。它的优点包括:

  • 无需在每个页面都进行像素单位的转换,大大提高了开发效率。
  • 支持多种分辨率的设备,页面表现更加一致。
  • 支持rem和px单位的自由切换。
  • 灵活,可以根据需求进行定制。

三、flexiblejs引入

在引入flexiblejs之前,需要在head区域加入viewport的meta标签,并去除默认的缩放限制。

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

然后,在任意一个JS文件中添加以下代码即可:

(function(win, lib) {
    // ...
})(window, window['lib'] || (window['lib'] = {}));

四、flexiblejs不用了

在使用flexiblejs的过程中,我们也要注意一些需要避免的坑,比如:

  • 尽量避免使用图片和视频等非常大的元素。
  • 避免使用过多的字体样式。
  • 尽量遵循设计规范,避免过多使用宽度百分比。
  • 不要在样式表中使用!important,这样会破坏原本的比例关系。 如果你已经要废弃flexiblejs,可以单独在head区域删除相关的meta标签,然后手动设置根字体大小即可。
html {
    font-size: 16px;
}

五、flexiblejs的缺点

虽然flexiblejs是一款非常优秀的移动端适配方案,但也有一些缺点:

  • 有时候会出现模糊的问题。
  • 使用rem作为单位,在某些场景下会显得不够灵活。
  • 对于在Android4.4以下的系统上,可能会遇到一些兼容性问题。

六、flexiblejs替代方案选取

除了flexiblejs以外,还有一些其他的移动端适配方案,比如:

  • 媒体查询:这是最传统的适配方案,通过CSS3的媒体查询来适配不同尺寸的设备。
  • Bootstrap:这是一款非常流行的响应式框架,可以支持更丰富的页面布局效果。
  • Viewport units:除了rem以外,还可以使用vw和vh等单位来实现自适应。 综合性能、兼容性和使用难度等因素,可以按照不同项目的需要来选择适当的方案。