从零开始打造自己的JavaScript框架——第0章

文章目录
  1. 1. 概念简析
  2. 2. 目标
  3. 3. 环境准备
  4. 4. 异步加载
    1. 4.1. 引入js
    2. 4.2. 引入多个js
    3. 4.3. 有序引入js
    4. 4.4. 管理依赖
  5. 5. 源码分享
  6. 6. 书签

概念简析

库、插件、框架、加载项、扩展和控件都是组件。

组件(Component)是一个含义很大的概念,一般是指软件系统的一部分,承担了特定的职责,可以独立于整个系统进行开发和测试,一个良好设计的组件应该可以在不同的软件系统中被使用(可复用)。例如V8引擎是Chrome浏览器的一部分,负责运行javascript代码,这里V8引擎就可以视为一个组件。V8引擎同时也是node.js的javascript解释器,这体现了组件的可复用性。

库(Library)是一系列预先定义好的数据结构和函数(对于面向对象语言来说,是类)的集合,程序员通过使用这些数据结构和函数实现功能。例如Moment.js是一个javascript库,提供了处理时间的一些函数。在js中,插件和库的含义相同,我们也可以说Moment.js是一个插件。

框架(Framework)也是一系列预先定义好的数据结构和函数,一般用于作为一个软件的骨架,但程序真正的功能还需要由开发者实现。框架和库的最大区别在于“控制反转”,当你使用一个库,你会调用库中的代码,而当你使用一个框架,框架会调用你的代码。框架和库是一个有交叉的概念,很多框架都是以库的形式发布的,例如Java的Spring MVC框架,其发布的jar包本身就是一个库。下图来自Library vs. Framework? ,从调用的角度说明了框架和库的关系:

来自知乎龚世伟的回答

目标

说到js框架和插件,我们可以想到jquery、zepto、requirejs、seajs、art-template、page.js、angularjs、vue等等。这些框架和插件有什么区别?来个表格比较。
















































































































js框架和插件比较
框架名称 选择器 DOM操作 事件处理 AJAX 异步处理 动画模块 模块化管理依赖 模板引擎 路由管理
jquery × × ×
zepto × × ×
requirejs × × × × × × × ×
seajs × × × × × × × ×
art-template × × × × × × × ×
page.js × × × × × × × ×
angularjs
vue

接下来,我们要开发一个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,就是为了解决这两个问题:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。

环境准备

1、安装node,参考nvm项目
2、安装puer,参考超简单工具puer

异步加载

首先,我们来看怎样实现异步加载。js的异步很容易实现,使用回调函数即可。

引入js

js的引入,不是写在页面中,那么,肯定是写在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:jquery
vkjs测试页面

引入jquery的时候,该script是插入到页面最底部的,但是我们在回调函数中依然可以使用$,因为jquery加载完毕后我们才调用回调函数,这时就和页面位置无关了。

引入多个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:jquery
loading:template
页面加载完毕
loaded:template
loaded:jquery
vkjs测试页面

有序引入js

很多时候,我们不止要引入多个js,而且要有顺序地引入js。为了实现有序加载js,又不想使用js回调重重嵌套,所以小编选择使用Promise。具体用法参考Javascript异步编程的4种方法JavaScript Promise 告别异步乱嵌套大白话讲解Promise(一)

未完待续。。。

管理依赖

源码分享

https://github.com/voidking/vkjs.git

书签

从零开始编写自己的JavaScript框架(一)

从零开始编写自己的JavaScript框架(二)

jQuery源码解析(架构与依赖模块)

Query源码解析(架构与依赖模块)对应源码

Sea.js是如何工作的?

sea.js源码(Module.js核心代码)

JS模块加载器加载原理是怎么样的?

如何构建一个微型的CMD模块化加载器

如何实现一个 CMD 模块加载器

Grunt 实例之构建seajs项目

漫谈js自定义事件、DOM/伪DOM自定义事件