Skip to content

Latest commit

 

History

History
225 lines (211 loc) · 5.54 KB

模块化.md

File metadata and controls

225 lines (211 loc) · 5.54 KB

Module

解析模块的层次依赖关系

如一个工程的文件结构如下:

project/
├── css/
   └── main.css
├── js/
   ├── require.js
   └── modlues/
     ├── a.js
     ├── b.js
     └── c.js
└── index.html

而这里几个模块的依赖关系是:

            > a.js -> b.js
index.html -|
            > c.js

// a.js
require("./js/test/b.js");

// b.js
console.log("i am b");

// c.js
console.log("i am c");

我们要从 index.html 中利用 require.js 获取这一连串的依赖关系,一般采用的方式就是正则匹配
如下:先拿到 function 的代码,然后正则匹配出第一层的依赖关系,接着加载匹配到关系的代码,继续匹配。

// index.html
<script type="text/javascript" src="./js/require.js"></script>
<script type="text/javascript">
    function test(){
        var a = require("./js/modlues/a.js");
        var c = require("./js/modlues/c.js");
    }

    // toString 方法可以拿到 test 函数的 code
    start(test.toString());
</script>

整个函数的入口是 start,正则表达式为:

var r = /require\((.*)\)/g;

var start = function(str){
    while(match = r.exec(str)) {
        console.log(match[1]);
    }
};

由此我们拿到了第一层的依赖关系:

["./js/modlues/a.js", "./js/modlues/c.js"]

接着要拿到 a.js 和 b.js 的文件层次依赖,之前我们写了一个 require 函数,
这个函数可以拿到脚本的代码内容,不过这个 require 函数要稍微修改下,递归去查询和下载代码。

var cache = {};
var start = function(str){
    while(match = r.exec(str)) {
        console.log(match && match[1]);
        // 如果匹配到了内容,下载 path 对应的源码
        match && match[1] && require(match[1]);
    }
};

var require = function(path){
    var xhr = new XMLHttpRequest(), res;
    xhr.open("GET", path, true);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            res = xhr.responseText;
            // 缓存文件
            cache[path] = res;
            // 继续递归匹配
            start(res);
        }
    }
    xhr.send();
};

历程一

  1. 最早我们这样写代码
function foo(){
    //...
}
function bar(){
    //...
}

总结:

  • Global 被污染,很容易命名冲突
  1. 简单封装:Namespace 模式
var MYAPP = {
    foo: function(){},
    bar: function(){}
}

MYAPP.foo();

总结:

  • 减少 Global 上的变量数目
  • 本质是对象,一点都不安全
  1. 匿名闭包 :IIFE 模式
var Module = (function(){
    var _private = "safe now";
    var foo = function(){
        console.log(_private)
    }

    return {
        foo: foo
    }
})()

Module.foo();    // safe now
Module._private; // undefined

总结:

  • 函数是 JavaScript 唯一的 Local Scope
  1. 再增强一点 :引入依赖
var Module = (function($){
    var _$body = $("body");     // we can use jQuery now!
    var foo = function(){
        console.log(_$body);    // 特权方法
    }

    // Revelation Pattern
    return {
        foo: foo
    }
})(jQuery)

Module.foo();

总结:

  • 这就是模块模式,也是现代模块实现的基石

历程二 SCRIPT LOADER

只有封装性可不够,我们还需要加载  

  1. 问题:
body
    script(src="zepto.js")
    script(src="jhash.js")
    script(src="fastClick.js")
    script(src="iScroll.js")
    script(src="underscore.js")
    script(src="handlebar.js")
    script(src="datacenter.js")
    script(src="deferred.js")
    script(src="util/wxbridge.js")
    script(src="util/login.js")
    script(src="util/base.js")
    script(src="util/city.js")
    script(src="util/date.js")
    script(src="util/cookie.js")
    script(src="app.js")
  • 难以维护
  • 依赖模糊
  • 请求过多 2.解决方案:Script Loader 如何工作?
$LAB.script("framework.js").wait()
    .script("plugin.framework.js")
    .script("myplugin.framework.js").wait()
    .script("init.js");

3.基于文件的依赖管理

$LAB
.script( [ "script1.js", "script2.js", "script3.js"] )
.wait(function(){ // wait for all scripts to execute first
    script1Func();
    script2Func();
    script3Func();
});

历程三 MODULE LOADER

历程四 COMMONJS

  1. 特点 同步/阻塞式加载

历程五 AMD/CMD 浏览器环境模块化方案

1.AMD(Async Module Definition) RequireJS 对模块定义的规范化产出
2.CMD(Common Module Definition) SeaJS 对模块定义的规范化产出
3.AMD vs CommonJS a. 书写风格

// Module/1.0
var a = require("./a");  // 依赖就近
a.doSomething();

var b = require("./b")
b.doSomething();
// AMD recommended style
define(["a", "b"], function(a, b){ // 依赖前置
    a.doSomething();
    b.doSomething();
})

b. 执行时机

历程六 BROWSERIFY/WEBPACK

历程七 ES6

  1. 浏览器加载 1.1 <script>标签打开defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
    1.2 defer 和 async 区别 defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染
  • defer是“渲染完再执行”
  • async是“下载完就执行”