Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

关于模块预实现的设想

  目前对于模块加载通常有两种方式,一种是传统方式的直接随着页面加载,另一种是根据其它模块的需求来加载。无论使用何种方式,总是一个概念,需求→加载→使用。对于传统的网页,这样也许已经足够了。但是在现代Web中我们会遇上更复杂的情况。
  假如有A、B两个模块,A模块中有部分代码依赖于B模块,但A模块也有存在一部分不依赖于B模块的代码。遇到这种情况我们通常会先加载B模块、然后再加载A模块。这样虽然可以实现,但是如果B模块是个很大的库,加载需要很多时间,那么A模块的执行就被耽误了。也许有人会试着把A模块中依赖B模块的代码独立出来做成一个新的模块。但这么做只会让程序的内聚性降低,有时候甚至逻辑上都根本不允许这么做。
  如果A模块对于程序来说非常重要,我们当然希望优先加载并执行它,而不是去加载庞大而又不重要的B模块。但是关键问题是,A模块中存在依赖B模块的代码,而A模块执行时B模块还没加载完,要是遇上该怎么办?
  我想我们是不是可以在B模块加载完成前先实现个B模块的简易接口呢?就像写C++的时候我们有头文件和源文件;头文件中声明,源文件中实现。实际上我们只要头文件就可以确定一个类的接口,甚至只要头文件的public部分。我们只要把在A模块中使用的B模块中的对象或函数名抽取出来做一次预定义,即使B模块没加载完成,调用也不会出错。而且我们还可以把这些调用记录放入一个队列中,等到B模块加载完成后再一起处理。
  但是要做到这样,对于B模块本身提供的接口是有一定要求的。至少它提供的方法得是异步的,通过回调函数返回数据。只有这样我们才能把调用放入队列中。同步调用的处理就比较困难,特别是值类型的数据返回和方法链调用的处理。在模块真正加载前,需要返回值类型的函数我们根本无法确定这个值,最多只能定义个初始值。如果返回的是一个对象类型,我们可以留对象引用,在模块加载完成后再直接往引用上添加属性。属性类型如果是函数的,我们可以再次预实现它;如果是值类型依然只能使用一个初始值。
  也许这么做会让人觉得很烦,但实际上A模块可能并没有用到太多B模块提供的方法,所以也没必要把整个B模块的接口部分全都导出来。只要把这整个实现过程封装好,就可以很容易的操作这些。而且ES6中提供了Proxy,使用它来实现可以减少很多阻碍。但遗憾的是目前只有Firefox支持Proxy。
  下面是个没封装的简易的例子 <script>
//这里是预实现,$.post,并异步加载jQuery
var $=(function(){
  //加载相应模块
  var xhr=new XMLHttpRequest;
  xhr.open("GET","jquery.js",true);
  xhr.send();
  xhr.onreadystatechange=function(){
    if(xhr.readyState<4)return;
    window.eval(xhr.responseText);
    //处理队列
    for(var i=0;i<queue.length;i++)
      $[queue[i][0]].apply($,queue[i][1]);
  };
  //创建队列并返回接口方法
  var queue=[];
  return {
    post:function(){
      queue.push(["post",arguments]);
    }
  };
})();
</script>
<script>
//假如这里是A模块的代码
//执行到这里时jQuery并没有加载完成
$.post("?",function(e){
  console.log(e);
});
//假如这个$.post只是A模块的一部分,后面还有很多其它代码
//TODO
</script>
网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^