移动端适配原理:flexiable.js和px2rem结合方案的一点理解总结

2,047 阅读3分钟
  1. 通过 flexiable.js 实现动态计算根元素 htmlfontSize 值,以保证实际设备视口尺寸 (与meta[name="viewport"]标签密切相) 永远等于10rem
  2. 通过获取当前设备像素比dpr后, flexiable.js 动态设置 meta[name="viewport"] 标签,根据dpr进行scale值的设置(scale值直接会影响视口的实际尺寸,即设备尺寸/scale ===视口的实际尺寸 )

场景如下: 视觉稿:750*1334(2倍) 适配引入: flexiable.js + px2rem-loader div尺寸:600px

  1. iphone6/7/8 375*667 [window.devicePixelRatio === 2] => dpr === 2 => scale === 1/dpr === 1/2 === 0.5 => 页面插入 <meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no"> => 根元素fontSize === (实际设备尺寸/viewport[scale] === 375/0.5 === 750) / 10 === 75px => 600px 经过px2rem-loader 处理,600 /75 === 8rem其中 75 为按照实际视觉稿的尺寸计算的1rem对应的值,即 750/10

即此时div尺寸为8rem,永远是相对与根元素的尺寸进行等比例缩放,从而实现自适应尺寸布局。 此时8rem实际的尺寸为 875 === 600px 此时设备宽度为375px,但是此时meta[name="viewport"] scale 为 0.5,故实际600px对应的设备宽度仅为300px(600px0.5)。自适应完成✅

  1. iphone6/7/8 Plus 414*736 [window.devicePixelRatio === 3] => dpr === 3 => scale === 1/dpr === 1/3 === 0.3333333333 => 页面插入 <meta name="viewport" content="initial-scale=0.3333333333, maximum-scale=0.3333333333, minimum-scale=0.3333333333, user-scalable=no"> => 根元素fontSize === (实际设备尺寸/viewport[scale] === 414/0.3333333333 === 1242) / 10 === 124.2px => 600px 经过px2rem-loader 处理,600 /75 === 8rem其中 75 为按照实际视觉稿的尺寸计算的1rem对应的值,即 750/10

即此时div尺寸为8rem,永远是相对与根元素的尺寸进行等比例缩放,从而实现自适应尺寸布局。即此时8rem实际的尺寸为 8*124.2 === 993.6px 此时设备宽度为414px,但是此时meta[name="viewport"] scale 为 0.3333333333,故实际600px对应的设备宽度仅为331.2px(993.6px*0.3333333333)自适应完成

综上所述:设计稿的600px iphone6/7/8 375*667 dpr = 2 实际看到的为 300px iphone6/7/8 Plus 414*736 dpr = 3 实际看到的为 331.2px 实际根据rem与根元素的比例关系进行适配

// [flexiable.js](<https://github.com/posuihushui/flexible.js>)

//创建meta标签,设置初始比、最小比和最大比均为scale值,并将其插入到页面
//设置了缩放比,那么相当于这个屏幕渲染在一个被放大的画布之上。
if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    if (docEl.firstElementChild) {
        docEl.firstElementChild.appendChild(metaEl);
    } else {//并没有什么作用
        var wrap = doc.createElement('div');
        wrap.appendChild(metaEl);
        doc.write(wrap.innerHTML);
    }
}

// 自定义页面元素的fontsize,方便rem的配置
function refreshRem(){
    var docEl = window.document.documentElement;
// 重点:通过getBoundingClientRect()获取的是实际渲染的画布尺寸;(与meta[name="viewport"]标签密切相关)
// 即:如果设置initial-scale为0.5,deviceWidth(iphone6s)为375,则width = 375 / 0.5 = 750
// 即:如果设置initial-scale为0.3,deviceWidth(iphone6s)为375,则width = 375 / 0.3 = 1250
    var width = docEl.getBoundingClientRect().width;
    
		// 限制最大fontSize为64px
    if (width / dpr > 640) {
        width = 640 * dpr;
    }

		// 永远保证:实际设备视口宽度(与meta[name="viewport"]标签密切相关) === 10rem
    var rem = width / 10;
    docEl.style.fontSize = rem + 'px';
    flexible.rem = win.rem = rem;
}

// 监听[resize|pageshow]触发更新设置根元素fontSize值
window.addEventListener('resize', function() {
    clearTimeout(tid);
    tid = setTimeout(refreshRem, 300);
}, false);
window.addEventListener('pageshow', function(e) {
    if (e.persisted) {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }
}, false);
// 通过使用react-app-rewired和customize-cra对默认webpack自定义添加px2rem-loader配置
// 参考源码 [customize-cra/src/customizers/webpack.js](<https://github.com/arackaf/customize-cra/blob/master/src/customizers/webpack.js>)
// 参考配置 [px2rem-loader配置](<https://github.com/Jinjiang/px2rem-loader>)

const { override } = require("customize-cra");

const addPx2remLoader = (options) => (config) => {
	// 获取config所有loaders配置
  const loaders = config.module.rules.find((rule) =>
    Array.isArray(rule.oneOf)
  ).oneOf;
	
	// 获取针对/\.css$/文件的loader配置
  const cssLoader = loaders.find((rule) => {
    return rule.test && rule.test.toString() === "/\\.css$/";
  });
	
	// 追加px2rem-loader预处理loader
  cssLoader.use = [
    ...cssLoader.use,
    {
      loader: "px2rem-loader",
	// 采用参数传递px2rem-loader相关配置项
      options,
    },
  ];

  return config;
};

module.exports = override(
  addPx2remLoader({
    remUnit: 75,
    remPrecision: 8,
  })
);

下面分别附上flexiable.js和px2rem的源码地址(1、3): GitHub - posuihushui/flexible.js: 手机淘宝flexible.js解读

webpack配置相关:customize-cra/webpack.js at master · arackaf/customize-cra

GitHub - Jinjiang/px2rem-loader: Webpack loader for px2rem css file