1.  概念简析库、插件、框架、加载项、扩展和控件都是组件。
组件(Component)是一个含义很大的概念,一般是指软件系统的一部分,承担了特定的职责,可以独立于整个系统进行开发和测试,一个良好设计的组件应该可以在不同的软件系统中被使用(可复用)。例如V8引擎是Chrome浏览器的一部分,负责运行javascript代码,这里V8引擎就可以视为一个组件。V8引擎同时也是node.js的javascript解释器,这体现了组件的可复用性。
库(Library)是一系列预先定义好的数据结构和函数(对于面向对象语言来说,是类)的集合,程序员通过使用这些数据结构和函数实现功能。例如Moment.js是一个javascript库,提供了处理时间的一些函数。在js中,插件和库的含义相同,我们也可以说Moment.js是一个插件。
框架(Framework)也是一系列预先定义好的数据结构和函数,一般用于作为一个软件的骨架,但程序真正的功能还需要由开发者实现。框架和库的最大区别在于“控制反转”,当你使用一个库,你会调用库中的代码,而当你使用一个框架,框架会调用你的代码。框架和库是一个有交叉的概念,很多框架都是以库的形式发布的,例如Java的Spring MVC框架,其发布的jar包本身就是一个库。下图来自Library vs. Framework?  ,从调用的角度说明了框架和库的关系:
来自知乎龚世伟的回答 
2.  目标说到js框架和插件,我们可以想到jquery、zepto、requirejs、seajs、art-template、page.js、angularjs、vue等等。这些框架和插件有什么区别?来个表格比较。
js框架和插件比较 框架名称 选择器 DOM操作 事件处理 AJAX 异步处理 动画模块 模块化管理依赖 模板引擎 路由管理 <tr>
    <th>jquery</th>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
</tr>
<tr>
    <th>zepto</th>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
</tr>
<tr>
    <th>requirejs</th>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>√</td>
    <td>×</td>
    <td>×</td>
</tr>
<tr>
    <th>seajs</th>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>√</td>
    <td>×</td>
    <td>×</td>
</tr>
<tr>
    <th>art-template</th>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>√</td>
    <td>×</td>
</tr>
<tr>
    <th>page.js</th>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>×</td>
    <td>√</td>
</tr>
<tr>
    <th>angularjs</th>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
</tr>
<tr>
    <th>vue</th>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
    <td>√</td>
</tr>
接下来,我们要开发一个js框架,类似于requirejs和seajs,实现模块化管理依赖的功能。
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
1 2 3 4 5 6 <script  src ="1.js" > </script > <script  src ="2.js" > </script > <script  src ="3.js" > </script > <script  src ="4.js" > </script > <script  src ="5.js" > </script > <script  src ="6.js" > </script > 
这段代码依次加载多个js文件,这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
详情请参考Javascript模块化编程(三):require.js的用法 。
我们要编写的vkjs,就是为了解决这两个问题:
3.  环境准备1、安装node,参考nvm项目 。超简单工具puer 。
4.  异步加载首先,我们来看怎样实现异步加载。js的异步很容易实现,使用回调函数即可。
4.1.  引入jsjs的引入,不是写在页面中,那么,肯定是写在js中。参考JAVASCRIPT 装载和执行 和Preload Javascript ,我们可以写出如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ;(function  (     var  vk_config = {         root : '/' ,         path : {             'jquery' : 'lib/jquery/jquery.min.js'          }     };     var  vk = {         loadjs : function (script_filename,callback ) {             script_doc = document .getElementById (script_filename);             if (script_doc){                 return ;             }             var  script = document .createElement ('script' );             script.setAttribute ('id' , script_filename);             script.setAttribute ('type' , 'text/javascript' );             script.setAttribute ('src' , vk_config.root  + vk_config.path [script_filename]);             document .getElementsByTagName ('body' )[0 ].appendChild (script);             console .log ('loading:' +script_filename);             script.onload  = script.onreadystatechange  = function (                 console .log ('loaded:' +script_filename);                 callback ();             }         }     };     window .vk  = vk; })(); 
在页面中使用的时候,调用loadjs方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html > <html  lang ="zh" > <head >     <meta  charset ="UTF-8" >      <title > vkjs</title >  </head > <body >     <h2 > vkjs测试页面</h2 >  <script  src ="../src/vk.js" > </script > <script >     vk.loadjs ('jquery' ,function (         console .log ($('h2' ).html ());     });      console .log ('页面加载完毕' ); </script > </body > </html > 
查看控制台,我们可以看到执行顺序:
1 2 3 4 loading: jquery页面加载完毕 loaded: jqueryvkjs测试页面 
引入jquery的时候,该script是插入到页面最底部的,但是我们在回调函数中依然可以使用$,因为jquery加载完毕后我们才调用回调函数,这时就和页面位置无关了。
4.2.  引入多个js引入单个js,是比较容易的。而在开发时,常常需要引入多个js,如果多次调用loadjs方法的话,非常不友好。那么,怎样引入多个js文件?同样的,我们需要先加载js文件,等到所有js文件加载完成,调用回调函数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 ;(function  (     var  vk_config = {         root : '/' ,         path : {             'jquery' : 'lib/jquery/jquery.min.js' ,             'layer' : 'lib/layer/layer.js' ,             'template' : 'lib/art-template/dist/template.js'          }     };     var  vk = {         loadjs : function (script_filename,callback ) {             script_doc = document .getElementById (script_filename);             if (script_doc){                 return ;             }             var  script = document .createElement ('script' );             script.setAttribute ('id' , script_filename);             script.setAttribute ('type' , 'text/javascript' );             script.setAttribute ('src' , vk_config.root  + vk_config.path [script_filename]);             document .getElementsByTagName ('body' )[0 ].appendChild (script);             console .log ('loading:' +script_filename);             script.onload  = script.onreadystatechange  = function (                 console .log ('loaded:' +script_filename);                 callback ();             }         },         count_js : 0 ,         use : function (ids,callback ){             var  that = this ;             if  (!Array .isArray (ids)) {                 ids = [ids];             }             that.count_js  = ids.length ;             for (var  i=0 ;i<ids.length ;i++){                 (function (i ){                     script_doc = document .getElementById (ids[i]);                     if (script_doc){                         return ;                     }                     script = document .createElement ('script' );                     script.setAttribute ('id' , ids[i]);                     script.setAttribute ('type' , 'text/javascript' );                     script.setAttribute ('src' , vk_config.root  + vk_config.path [ids[i]]);                     document .getElementsByTagName ('body' )[0 ].appendChild (script);                     console .log ('loading:' +ids[i]);                     script.onload  = script.onreadystatechange  = function (                         console .log ('loaded:' +ids[i]);                          that.count_js --;                         if (that.count_js  == 0 ){                             callback ();                         }                     }                 })(i);             }         }     };     window .vk  = vk; })(); 
在页面使用的时候,调用use方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!DOCTYPE html > <html  lang ="zh" > <head >     <meta  charset ="UTF-8" >      <title > vkjs</title >  </head > <body >     <h2 > vkjs测试页面</h2 >  <script  src ="../src/vk.js" > </script > <script >     vk.use (['jquery' ,'template' ],function (         console .log ($('h2' ).html ());     });      console .log ('页面加载完毕' ); </script > </body > </html > 
查看控制台,我们可以看到执行顺序:
1 2 3 4 5 6 loading: jqueryloading: template页面加载完毕 loaded: templateloaded: jqueryvkjs测试页面 
4.3.  有序引入js很多时候,我们不止要引入多个js,而且要有顺序地引入js。为了实现有序加载js,又不想使用js回调重重嵌套,所以郝同学选择使用Promise。具体用法参考Javascript异步编程的4种方法 、JavaScript Promise 告别异步乱嵌套 、大白话讲解Promise(一) 。
未完待续。。。
4.4.  管理依赖5.  源码分享https://github.com/voidking/vkjs.git 
6.  书签从零开始编写自己的JavaScript框架(一) 
从零开始编写自己的JavaScript框架(二) 
jQuery源码解析(架构与依赖模块) 
Query源码解析(架构与依赖模块)对应源码 
Sea.js是如何工作的? 
sea.js源码(Module.js核心代码) 
JS模块加载器加载原理是怎么样的? 
如何构建一个微型的CMD模块化加载器 
如何实现一个 CMD 模块加载器 
Grunt 实例之构建seajs项目 
漫谈js自定义事件、DOM/伪DOM自定义事件