溯源flexible的成长

溯源flexible的成长

在移动端适配中,flexible占据着非常重要的位置,尽管它有许多不足。但是其适配的思想仍然是非常值得学习的,这里我尝试着从最初版本的flexible源码开始解读,看看它的前世今生。

----如果只是想知道现在的flexible是怎么运作的,那就没必要往下看了,直接跳转到这篇文章即可

这里的所有代码都在github的amfe/lib-flexible中,这里有flexible比较详尽的发展过程。

2014.06.05

可以看到第一次提交是在2014年6月5日,最初版本的flexible就是这么质朴,一些变量的定义和处理和现在大相径庭。
溯源flexible的成长_第1张图片

  • 首先dpr和scale在初始化时就有了值,dpr是通过win.navigator.appVersion.match(/iphone/gi)来捕获iPhone信息,对于iPhone以外的设备,dpr默认为1

      var dpr = win.navigator.appVersion.match(/iphone/gi)?win.devicePixelRatio:1;
      var scale = 1 / dpr;
    
  • setUnitA(),这个A是有特殊含义的,在README中可以看到,在这里插入图片描述
    a是视觉稿中的单位,1a = 屏幕宽度/160 ,所以代码中这样计算根字体的大小是有道理的,这样能保证 1a = 0.1rem 。(因为刚工作没多长时间,不知道现在的视觉稿是不是这样,或者说现在还有视觉稿这种东西吗?请大佬指正)

      	function setUnitA(){
              win.rem = window.innerWidth / 16;
              document.documentElement.style.fontSize = win.rem + 'px';
          }
    
  • 好的,至此根字体已经设置完成了,就是这么easy,或者说有些“草率”,后面的就比较简单了,监听页面变化去执行setUnitA(),给html标签添加data-dpr属性,根据scale创建meta标签去做页面初始配置。

          win.dpr = dpr;
          win.addEventListener('resize', function() {
              clearTimeout(h);
              h = setTimeout(setUnitA, 300);
          }, false);
      
          document.documentElement.setAttribute('data-dpr', dpr);
          document.write('');
          setUnitA();
    
  • 到此,最初版本的flexible就这样结束了,看过0.3.2版本的小伙伴肯定一眼就能看出其中有诸多不合理的地方,但是在2014年,这样的适配思想已经非常难得了。

更新浏览器回退bug(2014.06.27)

  • 官方很快的意识到了第一个问题,仅仅监听页面变化是不行的,所以我们来看下这次代码的变更。
    溯源flexible的成长_第2张图片

  • 新定义了文档对象和meta标签对象

      var docEl = document.documentElement;
      var metaEl = document.createElement('meta');
    
  • 重点在这里,除了监听resize,还监听了pageshow,这样在浏览器回退时仍然能正常的适配。e.persisted就是用来判定当前页面是否是通过回退达到的。

       win.addEventListener('pageshow', function(e) {
              if (e.persisted) {
                  clearTimeout(h);
                  h = setTimeout(setUnitA, 300);
              }
          }, false);
    
  • 最后,既然定义了文档对象和meta标签对象,那么最后的文档配置也要做一定修改。

      docEl.setAttribute('data-dpr', dpr);
      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 = document.createElement('div');
          wrap.appendChild(metaEl);
          document.write(wrap.innerHTML);
      }
    
  • 这里有一个迷惑了我好长时间的问题,就是这个docEl.firstElementChild,难道还会不存在第一个子元素吗,那这个页面不就空了吗?后来才得知,这是为了兼容firstChild,我们要获取某个标签的第一个子元素时,一般都会用firstElementChild,但是这个属性在一些浏览器版本中是不兼容的,“Can I Use”中的介绍是这样的。在低版本的浏览器中必须使用firstChild获取第一个子元素,或者使用上面的方法,无脑把标签扔进去。
    溯源flexible的成长_第3张图片

支持手动设置meta标签(2014.09.19)

  • 我们在html都会习惯性的使用这样的配置,那么如果已经存在viewport配置后,flexible要做出有效的判断,从而有不同的处理方式,而不是一味的新增meta标签

      
    

溯源flexible的成长_第4张图片
溯源flexible的成长_第5张图片

  • 这里摈弃了之前初始化赋值的操作,开始的定义只用于初始化。这样做也是为了之后的判断,当我们没有viewport配置的时候,dpr和scale自然为空,可以对此进行一定处理;反之,就不需要处理,通过这种方法区分出了开发者手动配置meta的情况。另外,对于meta标签对象,也有了更细致的查询。

      var docEl = document.documentElement;
      var metaEl = document.querySelector('meta[name="viewport"]');
      var dpr;
      var scale;
      var tid;
    
  • 这里的一系列判断和当前版本的代码已经非常像了。识别出已有viewport配置的情况,然后进行特殊的处理。

         if (metaEl) {
             console.warn('将根据已有的meta标签来设置缩放比例');
             var match = metaEl.getAttribute('content').match(/initial\-scale=(["']?)([\d\.]+)\1?/);
             if (match) {
                 scale = parseFloat(match[2]);
                 dpr = 1 / scale;
             }
         }
    
         if (!dpr && !scale) {
             dpr = win.navigator.appVersion.match(/iphone/gi)?win.devicePixelRatio:1;
             scale = 1 / dpr;    
         }
     
         docEl.setAttribute('data-dpr', dpr);
         if (!metaEl) {
             metaEl = document.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 = document.createElement('div');
                 wrap.appendChild(metaEl);
                 document.write(wrap.innerHTML);
             }
         }
    
  • 需要注意的是“ scale = parseFloat(match[2]); ”这个地方,match很好理解,就是content内容拆分出来的数组,为什么是索引2呢,按照现在的源码来看应该是1才对呀。原因是这样的,我在当时那个版本的html文件中看到,viewport的配置是这样的。索引为2获取到的是最小缩放倍数,当然可以用作scale的计算。

      
    

更新rem方案,安卓在大于2倍的屏幕下用2的方案,其余用1的方案。ios在2和3倍的屏幕下用2的方案,其余用1的方案。(2014.10.14)

  • 之前的所有版本,仅有iPhone一家受宠。终于在这个版本,安卓也被重视起来。之所以做这样的区分,为的是获取不同的dpr,dpr用来影响后面meta标签的创建,所以很容易想到,这些判断应该出现在开发没有手动创建meta标签的情况下,恰巧,这就是上个版本主要做的事。
    溯源flexible的成长_第6张图片
  • 另外,这个版本还做了一个更新,就是对页面中字体单位的把控,之前并没有详细介绍,其实在flexible中元素的宽高布局单位推荐使用rem,而字体大小是推荐使用px的。原因是rem计算后的数值精确度较差,这种较差的精确度反应在字体上会更加明显。所以flexible的做法是在body中根据dpr设置font-size,这样一来,字体的px遵循body,元素的rem遵循html,能够非常明显的做出区分。
    在这里插入图片描述

andriod暂时只用1的方案(2014.10.14)

  • 所谓“天妒英才”,短短一天内,安卓再次被打入“冷宫”,时至今日,大家在源码中可以看到,依旧没有对安卓dpr的判断。为什么要做这样的设计?我在文件中并没有找到答案。事实上,在flexible的实践中,面对部分高dpr的安卓机,确实会出现一些适配的问题,只能手动去解决。这里flexible设计师到底是出于怎样的考虑,恳请大佬指明!
    溯源flexible的成长_第7张图片

更新为flexible,更改dpr的计算规则(2014.10.22)

  • 这个版本和我们现在看到的差别已经不大了,希望大家能记住“flexible”的本名叫“tbm”(^ _ ^)。
    溯源flexible的成长_第8张图片
    溯源flexible的成长_第9张图片

  • 这里除了一些变量名上的变更,主要有两点更新。第一,对最大宽度做了限制。这样做的目的主要是避免元素无限变大,在特大屏的设备上显示对应比例的元素,并不一定是明智之举,这样会一定程度的影响美观。

      	function setUnitA(){
          var width = docEl.getBoundingClientRect().width;
          if (width / dpr > 540) {
              width = 540 * dpr;
          }
          win.rem = width / 16;
          docEl.style.fontSize = win.rem + 'px';
       }
    
  • 第二,将body中font-size的设置抽离出来。能够更好的根据页面状态去设置字体大小,也能使字体大小和元素宽高两者的适配更明确的分开。

      	 if (doc.readyState === 'complete') {
          doc.body.style.fontSize = 12 * dpr + 'px';
          } else {
              doc.addEventListener('DOMContentLoaded', function(e) {
                  doc.body.style.fontSize = 12 * dpr + 'px';
              }, false);
          }
    

更新1/100的方案(2014.11.04)

  • 上面提到过,依据方便视觉稿中a的计算,使用“width/16”来解决这个问题,这里将16改为10,可能是视觉稿中a的含义发生变化。因为这里只做了一个数值上的变化,没有做其他解释,如果这里理解有误,希望大佬批评指正。
    溯源flexible的成长_第10张图片

END

  • 至此,flexible的基本功能已经实现了,之后的更新没有什么大的改动,都是一些小的优化,比如变量名更加规范,正则bug的修复…有兴趣的小伙伴可以去追一下源码。截至0.3.2版本,flexible停止了更新,在适配功能上,它也许有很多弊端没有解决,当前也有很多更好的方法能够取代它,但是去看一下源码,了解一套方案的成长过程,学习学习大佬的思维模式和编程方式,这才是我们看源码的目的,如果只是单纯的使用,Duck不必看这么深。

你可能感兴趣的:(前端,javascript,前端)