前端那些事

vuePress-theme-reco chenpeng    2020 - 2021
前端那些事 前端那些事

Choose mode

  • dark
  • auto
  • light
首页
文章目录
  • Browser
  • CSS
  • ES6
  • JavaScript
  • Network
  • TypeScript
  • Vue
  • Vue3
  • Webpack
标签
时间轴
GitHub
author-avatar

chenpeng

85

Article

25

Tag

首页
文章目录
  • Browser
  • CSS
  • ES6
  • JavaScript
  • Network
  • TypeScript
  • Vue
  • Vue3
  • Webpack
标签
时间轴
GitHub
  • ES6-模块化

    • 模块加载方案比较
    • ES6 模块加载与 CommonJS 模块加载的原理

模块加载方案比较

vuePress-theme-reco chenpeng    2020 - 2021

模块加载方案比较

chenpeng 2020-11-30 ES6模块化

# 1.CommonJS

# 1.1 特点

  • 一个文件就是一个模块,具备独立的作用域,不会污染全局
  • 模块同步加载、执行,第一次加载的时候全部执行,然后缓存下来,后面执行直接使用缓存
  • __dirname 代表当前模块文件所在的文件夹路径
  • filename 代表当前模块文件所在的文件夹路径+文件名

# 1.2 模块使用方式

  • require 加载模块

  • module.exports 导出模块

# 1.3 举例

function add(x, y) {
    console.log(x + y);
}

module.exports = {
    add
};

const common = require('./common');

common.add(1, 2); // 3
1
2
3
4
5
6
7
8
9
10
11

# 2.ES6 加载模块

# 2.1 特点

  • 默认严格模式(this 指向 undefined)
  • 变量只在本模块作用域内有效
  • 同一个模块加载多次,只执行一次

# 2.2 模块使用方式

  • import 加载模块
  • export default 默认导出,如果没有 default,加载的时候需要带上 {}

# 2.3 举例

// add.js
export default function add(x, y) {
    console.log(x + y);
}

// 在网页中引入模块
<script type="module">
    import add from "./add.js";
    add(1, 2);
</script>

// 在模块中引入
import add from "./add.js";
add(2, 3); // 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3.AMD

# 3.1 特点

  • 异步并行加载,不阻塞 DOM 渲染
  • 提前执行(预执行),在模块使用之前就执行完毕

# 3.2 模块使用方式

  • define(callback):该函数接收一个回调函数,用来定义模块
  • require([module], callback):该函数接收两个参数,一个是要加载的模块名称数组,一个是回调函数,用来加载模块
  • requirejs.config(obj):对 Require.js 进行配置

# 3.3 举例

需要引入 require.js

<script src="js/require.js" data-main="js/main"></script>
1

data-main 用于指定程序的主模块,这个文件会被 require.js 第一个加载

// 定义模块index.js
define(['index'], function(){
    function person(){
        console.log('jack');
    }
    return {
        person: person
    };
});
// 加载模块
require(['index'], function (module){
  module.person();
});
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.CMD

# 4.1 特点

  • 异步并行加载,不阻塞 DOM 加载
  • 按需执行(需要的时候才执行)

# 4.2 模块使用方式

  • require:加载模块
  • seajs.config(obj):用来对 Sea.js 进行配置
  • seajs.use(callback):用来在页面中加载一个或多个模块
  • define(callback):用来定义模块
  • require.async:用来在模块内部异步加载一个或多个模块
  • module.exports 与 exports 类似:都是对外提供接口

# 4.3 举例

// 定义模块  a.js
define(function(require, exports, module) {
  //引入模块
  var $ = require('jquery.js')
  //对外提供 price 属性  
  exports.price= 200;  
});
// 加载模块
seajs.use(['a'],function(a){
   console.log(a.price)//200
}
1
2
3
4
5
6
7
8
9
10
11

# 5.区别

  1. CommonJS 模块输出的是一个值的拷贝(CommonJS 的内部变化不会影响这个值),ES6 模块输出的是值的引用

    common.js 模块加载完成后,它的内部变化就影响不到输出的 count 了,从 CommonJS 中加载的变量会被缓存

    let count = 1;
    function counter(){
        count++;
    }
    
    module.exports = {
        count,
        counter
    };
    
    const common = require('./common');
    
    common.counter();
    console.log(common.count); // 1
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    而 ES6 模块的运行机制与 CommonJS 的不一样,JS 引擎对脚本进行静态分析时,遇到模块加载命令 import,就会生成一个只读引用,等到脚本真正运行时,再根据这个只读引用,到被加载的模块里面取值,从 ES6 模块中加载的变量不会被缓存

    let count = 1;
    function counter() {
        count++;
    }
    export {count, counter}
    
    import {count, counter} from "./es6.js";
    counter();
    console.log(count); // 2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. CommonJS 是运行时加载,ES6 模块是编译时输出接口

    CommonJS 一个模块就是一个对象,加载一个模块就是加载这个模块的所有方法,然后读取需要的方法;而 ES6 模块在编译时就完成加载,引用的时候只加载需要的方法,其他方法不加载

    const { stat, exists, readFile } = require('fs');
    
    1

    等同于

    const _fs = require('fs');
    stat = _fs.stat;
    exists = _fs.exists;
    readFile = _fs.readFile;
    
    1
    2
    3
    4

    上述代码的实质是加载整个 fs 模块(即加载 fs 的所有方法),生成一个对象 _fs,然后再从这个对象上读取3个方法。这种加载方式称为运行时加载,只有在运行时才能得到这个对象,导致没办法在编译阶段做静态优化

    import { stat, exists, readFile } from 'fs';
    
    1

    上述代码的实质是从 fs 模块加载3个方法,其他方法不加载。这种加载方式称为编译时加载或者静态加载,即 ES6 可以在编译时就完成模块加载,效率比 CommonJS 加载模块的方式高

  3. CommonJS 模块的顶层 this 指向当前模块,ES6 模块的顶层 this 指向 undefined

# 6.模块间如何保持独立

在 nodejs 中,所有的 js 文件在node中执行前,都会被包装进

define(require, exports, module, _filePath, _fileName)
1

该函数会返回 exports 对象,通过 exports 向外暴露属性