Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modules #9

Open
hex11o opened this issue Jun 14, 2021 · 1 comment
Open

Modules #9

hex11o opened this issue Jun 14, 2021 · 1 comment

Comments

@hex11o
Copy link
Owner

hex11o commented Jun 14, 2021

1. 为什么需要模块化

如何管理变量,变量拆分成单独的模块,let有静态作用域,但又让变量无法在另一个function 中引用,一般来说都是在外层声明变量,那此时你就需要注意script的顺序了,不能提前引用

并且脚本之间的相互依赖变得模糊,你不知道哪段脚本使用了哪个变量
第二个就是因为变量暴露,导致代码不稳定性变高

引入模块化后代码的引用依赖变得清晰,能够将代码分解为独立的模块

2. 目前的模块化方案

CJS, AMD, UMD, ESM,语法,目的,模块行为

  • CJS: CommonJS
// import
const doSomething = require("./doSomething.js")

// export
module.exports = function doSomething() {}

node就是使用CJS
引入模块是同步的,引入的是副本
不能在browser执行,需要编译打包, node端

  • AMD: Asynchronous Module Definition 异步模块定义
define(['dep1', 'dep2'], function (dep1, dep2) {
    //Define the module value by returning a value.
    return function () {};
});
// or
define(function (require) {
    var dep1 = require('dep1'),
        dep2 = require('dep2');
    return function () {};
});

引入模块是异步的
在前端执行

  • UMD: Universal Module Definition
(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(["jquery", "underscore"], factory);
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"), require("underscore"));
    } else {
        root.Requester = factory(root.$, root._);
    }
}(this, function ($, _) {
    // this is where I defined my module implementation

    var Requester = { // ... };

    return Requester;
}));

如名,可以处理前后端模块
被打包工具用来作为ESM使用不了时兼容处理不同的环境

  • ESM: ECMAScript Module
    �JS模块化提议
// import
import someThing from "./something.js"
import { a, b } from "./someModule.js"

// export
export default function doSomething () {}

在现代浏览器中支持
同步和异步语法都支持
支持摇树
能直接在HTML中使用

@hex11o
Copy link
Owner Author

hex11o commented Jun 14, 2021

3. ESM是如何工作的

当使用模块化后,变成了一个依赖图,依赖通过import 关联起来,给编译器一个入口,它变通过import声明找到所有的依赖;

但是文件本身并不是浏览器所需要的,需要将它们编译成module records

在这之后module records会生成模块实例module instance,模块实例包含两个部分:代码 code状态 state
code相当于菜谱,是做不了菜的,而state就是原材料, 记录变量的值;

模块构建的步骤:

  1. 初始化Construction:寻找,下载,编译成module record
  2. 实例化Instantiation:存放所有的export变量,也叫linking
  3. 执行Evaluation: 执行代码将变量赋值

    说ESM是异步的可以理解为,引用分为上面的三个步骤,并且步骤可以分开进行,
    ESM说了如何实例化和执行,但并没有说如何初始化获取依赖,是loader获取的依赖,但是获取依赖在不同的规范里是不同的,比如HTML的规范,导致不同的平台有不同的loader

初始化


如果主线程需要等其他依赖获取,那队列就会堆积,如果在浏览器进行,下载部分就会花费很长时间

这是ESM将算法分成几个阶段的一个原因,也是与CJS的一个区别,CJS之所以可以采取同步的方法,因为从本地引入依赖比从网上获取快多了

CJS会在寻找下一个依赖前执行所有的code, 意味着你可以获取到变量值,但是在ESM中,是先构建整个module record,意味着你不能在引用中使用变量,因为这些变量还没有值

但是有些时候很需要动态引入的功能,比如需要根据运营环境切换需要的依赖,就有了动态引入的提案,使用import()的都会作为一个入口来划分处理模块

实例化

  1. 深度遍历,从没有依赖其它的依赖开始,然后设置它们的exports
  2. 遍历imports, 因为import和export在内存上是同一个位置,先处理exports可以保证所有的imports能关联到匹配的exports.

    这与CJS不同,整个exports是复制的;在CJS中,exports的值改变后,require的值是不会改变的,而在ESM中,exports值改变,imports值也会改变,并且imports不能修改exports的值,话虽如此,如果import的是对象,还是可以修改对象里的值

执行

JS引擎执行function外的代码(top-level code)

如何解决循环引用的问题


CJS:
main会执行require声明,会加载counter模块
image
counter模块会使用message, 但是因为还没有在main中,会返回undefined, 我们设置一个setTimeout,让main的执行继续

message会被实例化,但是已经失去关联,counter中的message还是undefined
image
如果引入是绑定的,counter就能获取到正确的值,ESM的三段设计能够处理这个问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant