前端模块化

1.模块化编程

1.1 ES5时代

1.1.1 原生代码实现模块化

1) 对象写法

var module1 = new Object({
    _count : 0,
    m1 : function (){
      //...
    },
    m2 : function (){
      //...
    }
  });

2) 立刻执行函数(Immediately-Invoked Function Expression,IIFE) or 匿名闭包

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
      //...
    };
    var m2 = function(){
      //...
    };
    return {
      m1 : m1,
      m2 : m2
    };
  })();

这样可以很好的保护私有变量,通过return来设置公开的方法。缺点也有: 动态添加方法的时候比较麻烦,且无法修改内部私有变量.
3) 放大模式 or 宽放大模式(Loose augmentation)
如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用”放大模式”(augmentation)

var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);

在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用”宽放大模式”

var module1 = ( function (mod){
    //...
    return mod;
  })(window.module1 || {});

测试

var moduleTest = ( function (mod){
var value1=0, privateName='smt'; 


function privateAddTopic(data) {
     // 这里是内部处理代码
     console.log("内部函数");
     console.log(data);
} 
mod.name = privateName;
mod.init=function(){
    this.f1();
    this.f3();
};
mod.f1=function(){
    console.log("f1--hello");
}; 
mod.f2=function(){
    console.log("f2--hello");
};  
mod.f3=function(){
    console.log("f3--hello");
}; 
mod.f4=function(){
    console.log("f4--hello");
}; 
mod.f5=function(data){
    privateAddTopic(data);
};   
  return mod;
})(window.moduleTest || {});


moduleTest.init();
moduleTest.f3();
moduleTest.f5("hello world");
console.log(moduleTest.name);
moduleTest.name = 'zry';  //修改模块中的属性
console.log(moduleTest.name);

1.1.2 CommonJS/AMD/CMD

现代模块的基本思想实现如下:

var myModules = (function Manager() {
        var modules = {}; //定义的模块保存在这个对象里面

        function define(name, deps, impl) {//impl是implement的简写,实现的方法
            for (var i = 0; i < deps.length; i++) {
                deps[i] = modules[deps[i]]; //从保存的对象中获取依赖的模块,注:依赖的模块,肯定已经被define()存放在modules对象中了。
            }

            modules[name] = impl.apply(impl, deps);//如果此时的模块引入别的模块deps,就将deps作为impl实现的方法的参数
        }
        /**
         * [get 通过名字获得模块]
         * @param  {[type]} name [模块名]
         * @return {[type]}      [完整独立模块]
         */
        function get(name) {
            return modules[name];
        }

        return {
            define: define,
            get: get
        };
    })();

    //测试
    myModules.define("bar", [], function() {
        function hello(who) {
            return "hello " + who;
        }
        return {
            hello: hello
        };
    });
    myModules.define("foo", ["bar"], function(bar) {
        var n = 'zry';
        function awe(who) {
            console.log(bar.hello(n).toUpperCase());
        }
        return {
            awe: awe
        };

    });

    var bar = myModules.get("bar");
    var foo = myModules.get("foo");

    foo.awe();//HELLO ZRY

* 1) CommonJS*
node.js的模块系统,就是参照CommonJS规范实现的

var math = require('math');
math.add(2,3); // 5

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。这就催生AMD规范到来的背景。
* 2) AMD*
AMD(Asynchronous Module Definition)是异步模块加载的意思。

require([module], callback);
require(['math'], function (math) {
  math.add(2, 3);
});

主要有两个Javascript库实现了AMD规范:require.js和curl.js
* 3) CMD*

CMD(Custom Module Definition)通用模块加载
引入seajs文件

<script type="text/javascript" src="../../common/jsext/sea-debug.js">script>

seajs 的简单配置

seajs.config({
  base: "../sea-modules/",
  alias: {
    "jquery": "jquery/jquery/1.10.1/jquery.js"
  }
})
// 加载入口模块
seajs.use("../static/hello/src/main");//入口

定义模块:

// 所有模块都通过 define 来定义
define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});

另外可以使用seajs-text加载html文件或者tpl片段,seajs-css加载css文件

<script src="path/to/sea.js">script>
<script src="path/to/seajs-text.js">script>

<script>
define("main", function(require) {

 // You can require `.tpl` file directly
 var tpl = require("./data.tpl")
//或者html
var html =require("./a.html");
$('.some_class').append(html);
})
script>

seajs-css

<script src="path/to/sea.js">script>
<script src="path/to/seajs-css.js">script>

<script>

// seajs can load css file after loading css plugin.
seajs.use("path/to/some.css");
//很多时候可以使用require的方式
require("path/to/some.css");
script>

4.1 ES6时代

//bar.js
function hello(who){
    return "hello "+who;
}

export {hello};
//foo.js
import {hello} from "bar";

var n = "zry";
function awe(){
    console.log(bar.hello(n).toUpperCase());
}

export {awe};
//baz.js
import {bar} from "bar";
import {foo} from "foo";

console.log(bar.hello('smt'));//hello smt

foo.awe();//HELLO ZRY

当然现在需要使用babel转成es5,并且要使用打包工具browserify webpack rollup 等才能直接在现在的浏览器上运行。

参考阅读:

  • Javascript模块化编程(一):模块的写法
  • Javascript模块化编程(二):AMD规范
  • Javascript模块化编程(三):require.js的用法
  • seajs-github
  • seajs官网
  • seajs-text
  • 《你不知到的javascipt》
  • 阮一峰-es6入门

你可能感兴趣的:(前端,javascript,前端,模块化)