# JS模块化

# 1、why模块化

在原来的开发中,我们一个HTML文件往往会引入无数多个js文件,例如这样

image-20231213224425970

而在JS中,声明的变量往往是全局变量,项目中的JS文件可能是多个人开发的,比如你的同事。多个JS文件中可能就会存在变量名字重复从而导致GLOBAL全局污染,出现不可预料的问题。

# 2、简单的模块化

利用闭包,IIFE实现模块化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 使用立即执行函数实现模块化
        (function(){
            var msg = 'Hello JsModule'
            function sayHello(){
                console.log('Hi' + msg)
            }
            function sayOther(){
                console.log('Other' + msg)
            }
            window.Module = {
                sayHello: sayHello,
                sayOther: sayOther
            }
        })()
    </script>
    <script>
        Module.sayHello()
        Module.sayOther()
    </script>
</body>
</html>

缺陷:

​ 1、模块依然挂载在window上,依旧可能会造成全局污染

​ 2、模块之间可能存在依赖关系,而且对依赖关系的先后顺序很重要

# 3、CommonJS

相关博客:https://www.cnblogs.com/jzhishu/p/13599570.html (opens new window)

nodejs采用的模块化标准,commonjs使用方法require来引入模块,这里require()接收的参数是模块名或者是模块文件的路径,如果是模块名的话,require会到node_modules中去找对应名称的模块来加载

const _ = require("lodash");

这里就引入了一个名为lodash的模块,那么一个模块应该如何对外提供接口呢? commonjs提供两种方式对外暴露接口

// 第一种module.exports
const name = "张三";
module.exports = {
    getName(){
        return name
    }
}

// 第二种
const name = "张三"
module.exports.getName = function(){
    return name;
}

// 第三种(简写方式)
const name = "张三"
exports.getName = function(){
    return name;
}

其实本质上,模块对外暴露的就是exports这个对象,module.exports =这种写法,相当于直接给exports对象赋值,而module.exports. name这种写法其实就是给exports对象上添加了一个名为"getName"的方法。

示例:

// module2.js
let count = 1;

let m2 = function(){
    console.log("m2fun()")
}

module.exports = {
    count,
    // m2
}
module.exports.m2 = m2
// module1.js
const m2 = require('./module2.js')

console.log(m2.count)
m2.m2()

let m1 = function(){
    console.log('m1fun()')
}

module.exports = {
    m1
}
// app.js
const m1 = require('./module1.js')
m1.m1()
// 运行app.js的结果
1
m2fun()
m1fun()

require('')相当于直接运行了这个文件

一个文件统一导出方式注意点

1、在一个文件中不能多次 module.exports ,原因很简单,后面的exports会覆盖前面的exports

2、在一个文件中可以多次 module.exports.name或者exports. name , 因为这是在给exports的对象 添加键值对

一个文件混合导出方式注意点

1、在一个文件中多次module.exports.name或者exports. name之后,不能再module.exports={},因为直接赋值对象会把前面的所有覆盖

2、在module.exports={}之后,不能使用简写方式 exports.name,但是可以使用module.exports.name 【原因下面讲解】

在CommonJS规范中,你可以理解为了使用者方便简写,ComonJs会在每一个文件最上面添加这样的代码:

module.exports = {};//方便理解 一开始是空对象
let exports = module.exports;
console.log(module.exports===exports)//true

在JS中,对象类型的赋值都是赋的内存地址,所以当你重新给module.exports赋值对象的时候,相当于切断了exports和module.exports的联系。

CommonJs在浏览器端不能使用,因为不支持require语法,但是能使用browserify编译,具体使用请搜索browserify,参考地址https://ruanyifeng.com/blog/2015/05/commonjs-in-browser.html (opens new window)

# 4、AMD(RequireJS)

AMD是专门为浏览器端提供的模块化,异步

略,详情自己Google

# 5、CMD

CMD也是专门为浏览器端提供的模块化,异步

略,详情自己Google

# 6、ES6

es6提出了新的模块化方案,这个方案应该也是现在最流行的。通过关键字export value来暴露模块,通过import moduleName from path来引入模块,是不是看起来很简单?但是其实这里还有很多细节

浏览器端是不能直接运行的,需要先用babel将es6语法转译成es5(把import转译成了require),然后再使用打包工具打包,最后在页面中引入

# 多次暴露

// moduleA.mjs
// 暴露一个变量
export let name = "张三"

// 暴露一个方法
export function getName(){
    return name;
}

export function setName(newName){
    name = newName;
}

// main.mjs
import {name, getName, setName} from "./moduleA";

console.log(name); // 张三
setName("李四");
console.log(getName()); // 李四

这种方式在import时只能用解构赋值,使用下面的方式,会输出undefined

import moduleA from "./moduleA"

console.log(moduleA); // undefined;在node环境下运行会报错

那如果模块分别暴露的方法有很多怎么办呢,这时候结构赋值不是要写很多个方法?其实还可以这样引入

import * as moduleA from "./moduleA";

console.log(moduleA.name); // 张三
moduleA.setName("李四");
console.log(moduleA.getName()); // 李四

# 默认暴露

es6还提供了一种暴露方法叫默认暴露,默认暴露即export default value这里的value可以是任何值。这种方式在import时可以直接赋值一个整体对象。

// moduleA.mjs
export default {
    name : 张三,
    setName(newName){
        this.name = newName;
    },
    getName(){
        return this.name;
    }
}

// main.mjs
import moduleA from "./moduleA"

console.log(moduleA);  // { name: '张三', setName: [Function: setName], getName: [Function: getName] }
最近更新: 4/10/2024, 3:06:16 PM