JS高级—15—模块化(CommonJS和ESModule)

一、模块化概述

 

 

模块化主要是让每个木块有自己的作用域,即使定义为var也不会影响其他模块,因为只在自己作用域有效;

然后模块化也可以暴露接口,引入接口;

<!DOCTYPE html>
<html lang="en">
<head>

    <title>Document</title>
</head>
<body>
    <script src='a.js'></script>
    <script src='b.js'></script>
    <script src='c.js'></script>

    
</body>
</html>

这种不是模块化,a.js里用var定义的变量是可以被b.js改变的;

 

 

 

模块化开发可以很好的解决这个问题;

 

 我们使用es6的module后,如果在html文件中,只需要如下两步即可模块化开发:

1.首先在html中的<script>标签里有一个type=moudule表示我们要引入一个模块;

2.其次有三种方式导入模块:分别是通用导入方式(这个方式针对三种暴露都可)、解构赋值导入方式(这个方式针对三种暴露都可)、简便导入方式(这个简便方式只可以针对默认暴露)

 

 

二、commonjs规范和node实现

 

 (2)exports

 

(3)module.exports和exports区别

导出的其实就是module对象里的exports对象;

(4)require函数

node中每一个文件都是一个模块;

 

 

 

情况三一般就是引入第三方模块,require会去项目里的node_modules加载;

 

 

 

 

(5)模块加载过程

深度优先:先沿着路径找到最深的那个引用,然后返回;

 

 

(6)commonjs缺点:

 

 

三、ES Module规范

import './foo.js' //虽然没有导入任何变量,但是foo.js这个文件已经被引入了,所以foo.js里的代码会都执行一遍;
import {bar} from './foo.js'
import * as bar from './foo.js'//后续通过bar.a bar.b等方式使用;
import bar from './foo.js'
import bar from 'foo.js'  //从node_modules中导入

export {bar}
export default bar
export * from './foo.js'

 

 

 

我们定义了一个foo模块,

在index.html导入的时候,即使咩有使用foo()函数,foo模块的代码也都已经被执行一遍了;

 

 

 

 

 我们将//foo();注释放开:

 

 所以说,我们在阅读vue源码时,看到这里就知道,当我们new Vue时,

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

这些方法,都已经被执行过了,Vue函数的原型上已经被添加了很多方法;

 

 

 

(1)概念

每一个文件就是一个模块,每一个模块都有自己的作用域;所以即使不同的模块都定义了var name =‘kobe’,都不会相互影响;

 

(2)export关键字

 

 (3)import关键字 

通过import导入的值不能被修改,类似const;

 

参考mdn:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import

(4)export和import结合使用

在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;如果我们写了很多个js文件,可以再单独写一个index.js文件作为统一的接口暴露,在这个indexjs里导入所有其他的业务文件,然后再统一暴露给调用者;

有三种方式,第三种最好;

 

 (5)default

默认导出只能有一个;

(6)import函数

单纯的import关键字是静态导入,import()是动态导入;

import xxx from ‘xxx.js’是同步的,只有执行完之后才可以加载底下的代码;

这是一种很耗时的操作,那么有没有一种异步加载的方式?有,import();

 

 

 

 

四、esmodule解析流程

 

 

 

 

 

 

ESmodule:

1.import和import()区别

1.import是静态的,是在编译阶段就会执行的

import()是动态的,是代码运行到此处才会执行

2.

import是同步的,js会在执行完后,线程采取执行其他

import()是异步的,js会把它加入到任务队列里。

2.ESmoudel引入是同一个模块实例,

2.import和reqiure区别

在使用import导入模块时,实际上是在模块内部创建了一个指向被导入模块的引用,而不是直接复制模块中的变量。因此,当不同的文件中使用import导入相同的模块时,它们实际上是共享了同一个模块实例,所以可以访问和修改同一个模块中的变量。

在使用require导入模块时,实际上是将导入模块中的变量直接复制到(可以理解为浅拷贝)当前模块的作用域中。因此,当不同的文件中使用require导入相同的模块时,它们实际上是拥有各自独立的模块实例,彼此之间不会共享模块中的变量。

 

总结下他俩的区别,一个是模块级的引用,一个是浅拷贝;或者说,import是对模块这个对象做了浅拷贝,require是对模块对象里的属性、更深层对象做了浅拷贝;

 

所以,使用import时,在一个文件里引入了模块的基本数据类型值、对象或者函数,并且做了修改的话,那么其他文件引入此模块时,引入的是修改后的模块值;

a.js里导出值,

export const foo = "foo";
export const bar = {
    bar: "bar",
};
export function baz() {
    return "baz";
}

 

b1.js里修改他们,

import { foo, bar, baz } from "./a.js";

console.log("%c [ baz ]-2", "font-size:13px; background:pink; color:#bf2c9f;", baz);
console.log("%c [ bar ]-2", "font-size:13px; background:pink; color:#bf2c9f;", bar);
console.log("%c [ foo ]-2", "font-size:13px; background:pink; color:#bf2c9f;", foo);

bar.bar = "change-bar";
baz.prototype = {
    diyPrototype: "diy-prototype",
};

export default {};

 

我们看到b2.js里,打印的值已经变了。

import { foo, bar, baz } from "./a.js";

// eslint-disable-next-line no-unused-vars
import b1 from "./b1.js";

console.log("%c [ baz ]-2", "font-size:13px; background:pink; color:#bf2c9f;", baz, baz.prototype);
console.log("%c [ bar ]-2", "font-size:13px; background:pink; color:#bf2c9f;", bar);
console.log("%c [ foo ]-2", "font-size:13px; background:pink; color:#bf2c9f;", foo);

 

 

posted @ 2022-05-21 20:52  Eric-Shen  阅读(181)  评论(0编辑  收藏  举报