优化 CSS 和 JavaScript 请求数的另类方法

Jan Hančič 公布了一个在一次请求里同时加载 CSS 和 JavaScript 的方案,platypus.js

实现原理是利用 CSS 的 content 属性,把 JavaScript 转换成 base64 字符串放进去,再动态插入 DOM。

示例 CSS:

#platypus-0 {
  display: none;
  content: 	'YWxlcnQoJ0hpLCBteSBmcmllbmQhIEZyb20gQ2hyaXMnKQo=';
}

示例 JS:

(function ( document ) {
  var linkTags = document.querySelectorAll ( 'link[rel="stylesheet"]' );
  for ( var i = 0; i < linkTags.length; i++ ) {
    var dummyDiv = document.createElement ( 'div' );
    dummyDiv.id = 'platypus-' + i;
    document.body.appendChild ( dummyDiv );

    var styles = document.defaultView.getComputedStyle ( dummyDiv, null );
    var js = styles.getPropertyValue ( 'content' ); // w3c
    //backgroundImage = someElement.currentStyle["background-image"]; //IE
    if ( js === '' ) {
      continue;
    }

    dummyDiv.parentNode.removeChild ( dummyDiv );

    if ( js[0] === "'" || js[0] === '"' ) {
      js = js.substr ( 1, js.length - 2 );
    }

    var scriptTag = document.createElement ( 'script' );
    // window.atob - decode base64 string, unavailable on IE9-, MDN says IE10 supports it
    scriptTag.innerHTML = atob ( js );
    document.body.appendChild ( scriptTag );
  };
} ( document ) );

按照 Jan 的说法,platypus.js 兼容非兼容视图的 IE9,大致上主流的桌面、移动浏览器都可以兼容。

这个方案很有意思,虽然 CSS 文件的大小增加了,但在越来越快的带宽和越来越好的 CDN 的影响下,DNS Lookup、Connecting 和 Wating 的消耗说不定比多出的部分少不了多少,特别是小文件。

我也想过利用 localStorage 来实现类似 manifest 的效果,简单的示例代码如下:

var load = (function() {
  var insertToDOM, getSource

  insertToDOM = function( content ) {
    var script = document.createElement( 'script' )
    script.textContent = content
    document.body.appendChild( script )
  }

  getSource = function( file ) {
    var source = localStorage.getItem( file )
    if (typeof source !== 'string') {
      // if no record, get source from server
      $.ajax( {
        url: file,
        success: function( data ) {
          localStorage.setItem( file, data )
          insertToDOM( data )
        }
      } )
    } else {
      insertToDOM( source )
    }
  }

  return function(files) {
    files.forEach( function( file ) {
      getSource( file )
    })
  };
})();

load( ['//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js',
  'script.js'] )

特点:

  • 可以支持很多文件,理论上能用 Data URI 加载的都可以;
  • localStorage 的访问速度远远快于 DNS Lookup 等消耗;
  • localStorage 默认能支持最大 4MB 的储存,对于大部分站点完全足够;
  • 把脚本封装成 (fn)() 的形式并在底部插入特定的变量可判断版本、是否加载完成等,便于控制;
  • 可控性和易用性高于 manifest,不需要配置服务器,需要清空时操作 localStorage 即可,而 localStorage 的存储持久性和 manifest 基本一样。

只不过,这种做法和 AMD、CommonJS 等规范不太合拍——至少我还没想到好做法——小团队项目开发似乎挺不错,暂时只是草稿性质的想法。