前端模块化的那些事

151 阅读4分钟

ES模块化

特点:

  • 用于服务端和浏览器

  • 输出的是值的引用

  • import命令是异步加载

  • 静态编译:从fs模块加载三个方法,其他方法不加载,在编译时就完成模块加载

    // ES6模块
    import { stat, exists, readFile } from 'fs';
    
  • 自动采用严格模式(不管是否加上“use strict”)

  • ES6模块中顶层的this指向undefined

export和import

// script1.js
export const A = 1;
export const B = 3;
export const C = 4;

// 等价于
// const A = 1;
// const B = 3;
// const C = 4;

// export {
//     A,
//     B,
//     C
// }
// script2.js
import * as constants from './script1.js';
console.log(constants.A); // 1
console.log(constants.B); // 3

// 等价于
// import {A, B} from './script1.js';
// console.log(A);
// console.log(B)

注意点

  • exportimport命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错

  • import命令输入的变量都是只读的,不允许在加载模块的脚本里改写接口,但可以改写加载对象的属性

  • import命令具有提升效果,会提升到整个模块的头部,首先执行这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。以下代码不会报错:

    foo();
    import { foo } from 'my_module';
    
  • 多次执行同一个import会合并,只执行一遍

    import {A} from './script1.js';
    import {B} from './script1.js';
    
    // 等同于
    import {A, B} from './script1.js';
    

export default

  • export导入时需要知道变量名,export default表示将导出的变量赋值给名为default的变量,导入时不需要知道变量名,可以随意命名,不需要使用{}解构赋值
  • 一个模块只能有一个export default
  • export default后面不能跟着声明语句:export default var a = 100会报错,export default 100可以
// script1.js
let a = 100;
export default a;  // 导出一个值与a相同,名字叫做default的变量
// script2.js
import A from './script1.js'
console.log(A)

import ()

  • 支持动态加载
  • 返回一个Promise,加载模块成功以后,这个模块会作为一个对象,当作then方法的参数
  • import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载
  • 使用场合:
    • 按需加载
    • 条件加载
    • 动态的模块路径
import('./testMoudle.js')
.then(({export1, export2}) => {
  // ...·
});

加载规则

  • 浏览器加载ES6模块,也使用script标签,但是加入type="module"属性,正常sciprt标签是type="application/javascript"可以省略
  • 浏览器对于带有type="module"<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性

ES模块与CommonJS区别:

  • CommonJS 模块输出的是一个值的拷贝(被缓存),ES6 模块输出的是值的引用
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载
  • ES6 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块,这是两者的一个重大差异

CommonJS规范

  • 主要用于服务器

  • 输出的是一个值的拷贝

  • require()是同步加载模块

  • CommonJS规范是node专用,.mjs文件总是以 ES6 模块加载,.cjs文件总是以 CommonJS 模块加载,.js文件的加载取决于package.json里面type字段的设置

  • 运行时加载:下面代码先整体加载fs模块,生成一个对象(_fs),然后再从这个对象上读取三个方法

    // CommonJS模块
    let { stat, exists, readfile } = require('fs');
    
    // 等同于
    let _fs = require('fs');
    let stat = _fs.stat;
    let exists = _fs.exists;
    let readfile = _fs.readfile;
    
  • Module构造函数

    • module.id 模块的识别符,通常是带有绝对路径的模块文件名
    • module.filename 模块的文件名,带有绝对路径
    • module.loaded 返回一个布尔值,表示模块是否已经完成加载
    • module.parent 返回一个对象,表示调用该模块的模块
    • module.children 返回一个数组,表示该模块要用到的其他模块
    • module.exports 表示模块对外输出的值
    function Module(id, parent) {
      this.id = id;
      this.exports = {};
      this.parent = parent;
      // ...
    }
    
  • CommonJS导出的值会被缓存

    // script1.js
    var num = 1;
    function change() {
      num++;
    }
    module.exports = {
      num,
      change
    };
    
    // script2.js
    var { num } = require('./script1.js')
    var { change } = require('./script2.js')
    
    console.log(num) // 1
    change()
    console.log(num) // 1
    
  • exports变量:指向module.exports变量,不能直接修改exports指向

    exports.num = num
    exports.change = change
    
    // 报错,将exports指向对向,切断了与module.exports联系
    exports = {
    	num,
        change
    }
    
  • 循环加载:CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值

    // a.js
    exports.done = false;
    var b = require('./b.js');
    console.log('在 a.js 之中,b.done = %j', b.done);
    exports.done = true;
    console.log('a.js 执行完毕');
    
    // b.js
    exports.done = false;
    var a = require('./a.js');
    console.log('在 b.js 之中,a.done = %j', a.done);
    exports.done = true;
    console.log('b.js 执行完毕');
    
    // main.js
    var a = require('./a.js');
    var b = require('./b.js');
    console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
    
    // 输出
    在 b.js 之中,a.done = false
    b.js 执行完毕
    在 a.js 之中,b.done = true
    a.js 执行完毕
    在 main.js 之中, a.done=true, b.done=true
    

AMD规范

  • 主要用于浏览器
  • 异步加载

参考es6.ruanyifeng.com/#docs/modul…

关注公众号《小前日记 》获取源码

IMG_5155.JPG