Respond 的响应式代码阅读

阅读更多
  • 判断浏览器是否支持响应式。
( function (w) {
    "use strict" ;
  w.matchMedia = w.matchMedia ||   function (doc,   undefined   ) {
      var   bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, fakeBody = doc.createElement( "body"   ), div = doc.createElement( "div"   );
    div.id =   "mq-test-1" ;
    div.style.cssText =   "position:absolute;top:-100em"   ;
    fakeBody.style.background =   "none" ;
    fakeBody.appendChild(div);
      return   function (q) {
//通过对元素加入media的样式,在判断元素是否因此而改变。
        div.innerHTML = ';
      docElem.insertBefore(fakeBody, refNode);
      bool = div.offsetWidth === 42;
 
      docElem.removeChild(fakeBody);
        return   {
        matches: bool,
        media: q
      };
    };
  }(w.document);
})( this );
 

 

  • 收集所有css link 的 href。
ripCSS =   function () {
//links在之前已经完成整理。 head = doc.getElementsByTagName( "head" )[0] || docElem;links = head.getElementsByTagName( "link" );
 


 
      for (var i = 0; i < links.length; i++) {
        var   sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() ===   "stylesheet"   ;
        if   (!!href && isCSS && !parsedSheets[href]) {
          if   (sheet.styleSheet && sheet.styleSheet.rawCssText) {
          translate(sheet.styleSheet.rawCssText, href, media);
          parsedSheets[href] =   true ;
        }   else   {
            if   (! /^([a-zA-Z:]*\/\/)/   .test(href) && !base || href.replace(RegExp.$1,   "" ).split( "/"   )[0] === w.location.host) {
              if   (href.substring(0, 2) ===   "//"   ) {
              href = w.location.protocol + href;
            }

Respond 的响应式代码阅读_第1张图片
 
            requestQueue.push({
              href: href,
              media: media
            });
          }
        }
      }
    }
    makeRequests();
  };
 
  • 整理相关 @media 媒体查询脚本的元数据。
translate =   function (styles, href, media) {
 
//styles是.css文件的文本。通过ajax发起请求获取得到。
//对styles替换掉无效部分,匹配其中@media部分到变量qs。

Respond 的响应式代码阅读_第2张图片
 
    var qs = styles.replace( respond.regex.comments, "").replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0;
 
    href = href.substring(0, href.lastIndexOf(   "/" ));
      var   repUrls =   function (css) {
        return   css.replace( respond.regex.urls,   "$1"   + href +   "$2$3" );
    }, useMedia = !ql && media;
      if   (href.length) {
      href +=   "/" ;
    }
      if   (useMedia) {
      ql = 1;
    }
 
//分析qs,得出mediastyles的各项参数。整理出下图的数据结构。

Respond 的响应式代码阅读_第3张图片
 
      for ( var i = 0; i < ql; i++) {
      var fullq, thisq, eachq, eql;
      if (useMedia) {
        fullq = media;
        rules.push(repUrls(styles));
      } else {
        fullq = qs[i].match( respond.regex.findStyles) && RegExp.$1;
        rules.push(RegExp.$2 && repUrls(RegExp.$2));
      }
      eachq = fullq.split( ",");
      eql = eachq.length;
      for ( var j = 0; j < eql; j++) {
        thisq = eachq[j];
        if (isUnsupportedMediaQuery(thisq)) {
          continue;
        }
        mediastyles.push({
          media: thisq.split( "(")[0].match(respond.regex.only) && RegExp.$2 || "all",
          rules: rules.length - 1,
          hasquery: thisq.indexOf( "(") > -1,
          minw: thisq.match( respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || "" ),
          maxw: thisq.match( respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "" )
        });
      }
    }
 
    applyMedia();
  }
 
 
  • 实现 @media 的相关功能。

applyMedia =   function (fromResize) {
      var   name =   "clientWidth" , docElemProp = docElem[name], currWidth = doc.compatMode ===   "CSS1Compat"   && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now =   new   Date().getTime();
//之前已经定义resizeThrottle = 30;
//如果连续的resize在30ms内,延迟30ms后在触发一次resize job。
//formResize表示经过Respond初始化resize后,手工改变屏幕大小。
      if (fromResize && lastCall && now - lastCall < resizeThrottle) {
      w.clearTimeout( resizeDefer);
      resizeDefer = w.setTimeout( applyMedia, resizeThrottle);
      return;
    } else {
      lastCall = now;
    }
//整理出不含@media的各种设备对应的样式。

Respond 的响应式代码阅读_第4张图片
 
      for (var i in mediastyles) {
      if (mediastyles.hasOwnProperty(i)) {
        var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null , maxnull = max === null, em = "em";
        if (!!min) {
          min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
        }
        if (!!max) {
          max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
        }
//如果满足以下条件,将样式添加到该设备的样式集合styleBlocks中。
//1.如果不是媒体查询 !thisstyle.hasquery。
//2.如果存在媒体查询的min-width或者max-width配置。
//3.如果当前设备宽度在媒体查询的min-width、max-width之间。
        if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) {
          if (!styleBlocks[thisstyle.media]) {
            styleBlocks[thisstyle.media] = [];
          }
          styleBlocks[thisstyle.media].push(rules[thisstyle.rules]);
        }
      }
    }
 //初始appendedEls = [],head = doc.getElementsByTagName( "head" )[0] || docElem,是html head元素。。由于本方法是在window.setTimeout中执行,appendedEls作为全局变量,有记忆功能。用于删除中的,因为.css中的@media由Respond resize代替。
   for ( var j in appendedEls) {
      if (appendedEls.hasOwnProperty(j)) {
        if (appendedEls[j] && appendedEls[j].parentNode === head) {
          head.removeChild(appendedEls[j]);
        }
      }
    }
    appendedEls.length = 0;
 
//将同一设备的样式组合到一个