了解模块化开发的历史

传统模块化

<!DOCTYPE html>
<html lang="en">
<body>
</body>
<!--
// module1.js
var a = [1,2,3,4,5].reverse();
// module2.js
var b = a.concat([6,7,8,9]);
// module3.js
var c =b.join('-')
//index.js
console.log(a,b,c)
-->
<!-- 顺序加载 ,并公用一个作用域-->
<script src="./module1.js"></script>
<script src="./module2.js"></script>
<script src="./module3.js"></script>
<script src="./index.js"></script>
</html>

模块化解决的问题

  1. 加载顺序
  2. 全局污染 -> 立即执行函数创建模块的独立作用域

立即执行函数

  • ES5没有解决顺序加载的问题
// module1.js
var module1 = (function(){
    // 模块独立作用域
    var a = [1,2,3,4,5].reverse();
    return {a}
})()
// module2.js
var module2 = (function(module1){
    var b = module1.a.concat([6,7,8,9]);
    return {b}
})(module1)
// module3.js
var module3 = (function(module2){
    var c =module2.b.join('-')
    return {c}
})(module2)

// 解决了变量全局污染和模块以依赖的问题
// index.js
;(function(m1,m2,m3){
    console.log(m1.a,m2.b,m3.c)
})(module1,module2,module3)

// 也可以不写参数
// 但注入参数,方法内这个参数是局部变量,它的变化不会影响全局
// 符合纯函数的概念,否则就是全局变量了
;(function(){
    console.log(module1.module2,m2.module3,m3.c)
})()
  • 插件化
;(function(doc){
    var Calculator = function(){
        this.resultDom = document.getElementsByClassName('result')[0]
        // ……
        this.init()
    }
    Calculator.prototype.init = function(){
        this.bindEvent()
    }
    Calculator.bindEvent = function(){
        // 添加事件 加 减
    }
    Calculator.onBtnClick = function(event){
        this.resultDom.innerHTML = this.calcuate('plus',2,4)
    }
    Calculator.calcuate = function(field,val1,val2){
        switch(field){
            case 'plus':
                return val1 + val2;
            case 'minus':
                return val1 - val2
        }
    }
    window.Calculator = Calculator;
})(document)
// 使用
new Calculator();

CommonJS规范->nodeJs中使用

  • require 引入模块
  • module.exports 导出模块
    Node 应用由模块组成,采用 CommonJS 模块规范,
    前端的webpack也是对CommonJS原生支持的。
    每个文件就是一个模块,有自己的作用域。
    在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
    在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。
// 定义
// es1.js
var name = "Tom";
var age = 16;
function say(name,age){
   console.log(name+"is"+age+"years old")
}

// 语法1
module.exports.name = name;
module.exports.age = age;
module.exports.say = say;

// 语法2
exports.name = name;
exports.age = age;
exports.say = say;

// 语法3
module.exports = {
  name,
  age,
  say
}

// 语法4
module.exports = {
  name : "Tom",
  age : 16,
  say(name,age){
   console.log(name+"is"+age+"years old")
  }
}

// 引用
var module = require('./es1.js')
consolo.log(module.name)          //Tom
consolo.log(module.age)           //16
module.say(name,age)              //Tom is 16 years old
  • CommonJS每当require引入一个js文件,都会创建这个js文件的实例
  • 缓存机制
  • 浏览器不兼容CommonJS,它依赖于node 需要webpack打包
  • require把引入的js文件变成一个立即执行函数
  • exports 与module.exports 的区别:exports 是对 module.exports 的引用,不能直接给exports 赋值,直接赋值无 效,结果是一个空对象, module.exports 可以直接赋值
  • 一个文件不能写多个module.exports ,如果写多个,对外暴露的接口是最后一个module.exports
  • 模块如果没有指定使用module.exports 或者exports 对外暴露接口时,在其他文件就引用该模块,得到的是一个空对象{}

AMD

AMD 即 Asynchronous Module Definition,中文名是“异步模块定义”的意思。

  • define(moduleName,[moudule],factory) 定义模块
  • require([module],callback) 引入模块 前置依赖->所有模块加载完后才执行回调,避免了异步模块加载顺序的问题

在浏览器环境,要从服务器端加载模块,就必须采用非同步模式,因此浏览器端一般采用AMD规范。
AMD是一个在浏览器端模块化开发的规范,而AMD规范的实现,就是require.js。
特点:依赖必须提前声明好。

优点:异步加载,不阻塞页面的加载,能并行加载多个模块
缺点:不能按需加载,必须提前加载所需依赖

// 定义
// 独立模块 es1.js
define({
    module1: function() {},
    module2: function() {},
});
// 等价写法
define(function () {
    return {
        module1: function() {},
        module2: function() {},
    };
});

// 非独立模块 只有先加载这两个模块,新模块才能正常运行 es2.js
define(['es1', 'es2'], function(m1, m2) {
       return {
        method: function() {
            m1.methodA();
            m2.methodB();
        }
    };
});

// 使用
require.config({
    paths:[
        es1:'es1路径',
        es2:'es2路径',

    ]
})
require(['es1', 'es2'], function ( es1, es2) {
    es1.module1()
    es1.module2()
    es2.mothod()
});

CMD

阿里巴巴开发的 通用模块定义
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。
CMD规范整合了CommonJS和AMD规范的特点。
在 Sea.js 中,所有 JavaScript 模块都遵循 CMD模块定义规范。

  • 与AMD的本质不同
    • 支持动态引入依赖文件。
    • 依赖就近 按需加载
      https://www.zhangxinxu.com/sp/seajs/
// 定义
// 使用 exports 直接向外提供接口。
define(function(require, exports) { 
    // 对外提供 name属性
    exports.name = 'Tom'; 
    // 对外提供 say 方法
    exports.say= function(name) {
        console.log("hello"+name)
    };
});
// 使用 return 直接向外提供接口。
define(function(require) {  
    return {
        name : 'Tom',    
        say: function(name) {
            console.log("hello"+name)
        }
    };
});
// 使用 module.exports 直接向外提供接口。
define(function(require, exports, module) { 
    module.exports = {
        name: 'Tom', 
        say: function(name) {
            console.log("hello"+name)
        }
    };
});


// 使用
define(function (require) {
    var m1 = require('./module1')
    console.log(m1.name)      // Tom
    m1.say(m1.name)           // Hello Tom
})

// require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。
define(function(require, exports, module) {  
    // 异步加载一个模块
    require.async('./module1', function(a) {
        a.doSomething();
    }); 

    // 异步加载多个模块,在加载完成时,执行回调
    require.async(['./module2', './module3'], function(b, c) {
        b.doSomething();
        c.doSomething();
    });
})

ES6模块化->import的静态和动态

  • import module from ‘模块路径’;导入模块

  • export module; 导出模块

  • ES6 模块export、import命令可以出现在模块的任何位置,但是必须处于模块顶层。如果处于块级作用域内,就会报错

  • 静态导入

// 静态导入
/***
 * utils文件
 */
export function plus(a,b){}
export function minus(a,b){}
// =======
function plus(a,b){}
function minus(a,b){}

// 第一种导出 她不是一个对象,而是一个模块类型
export {
    plus,minus
}
// use
// 1.
import {plus,minus} from './utils';
// 2. 重命名
import {plus as add ,minus} from './utils';
// 3. 命名空间就是一个对象
import * as utils from './utils'
console.log(utils) //打印出的是模块
const {plus,minus} = utils;

// 第二种导出,将一个对象作为模块导出,此时不能使用结构
export default{
    plus,minus
}
// use
import utils from './utils'
console.log(utils) //打印出的是对象
const {plus,minus} = utils;

//第三种导出 也可以直接导入 副作用的导入,导入即执行,通常用来导入样式文件
import './utils'

//第四种导出  模块导出和对象导出混用

export default{
    a:1,
    B:2
}
export function plus(a,b){}
export function minus(a,b){}
// export {
//     plus,minus
// }

//1. 默认导出的必须第一个声明
import  utils,{plus,minus} from './utils';
const {a,b} = utils;
const res1 = plus(a,b);
const res2 = minus(a,b);

// 2.
import  utils,* as calc from './utils';
const {a,b} = utils;
const {plus,minus} = calc;

  • 动态导入
// 动态导入 import()
// import关键字,不是调用方法
// 运行时加载,无法静态分析
// 1.
import('./utils').then(module=>{
    const {a,b}  = module.default;
    const {plus,minus} = module;
})
// 2. 
(async ()=>{
    const module = await import('./utils');
    // const {default:utils,plus,minus} = await('./utils');
    // const {default:{a,b},plus,minus} = await('./utils');

    console.log(module);
})()

对比

  1. CommonJS中export中是值的拷贝,ES6是引用
  2. CommonJS运行时加载模块,ES6是编译阶段加载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值