js模块化
# js模块化
**JavaScript本身是不支持模块化的,只不过后来一些社区的大佬制定了一个模块规范,主要分为两种,一个是用于服务器的CommonJs,另一种用于客户端的AMD(比如requireJs)、CMD(比如seaJs)。后来ES6提供了通用的模块化方案**
## 1.AMD(requireJs)
在ES6出现之前,JS不像其他语言同样拥有“模块”这一概念,于是为了支持JS模块化,出现了各种各样的语言工具,如ReuqireJS。
```
<!--require.html-->
<!DOCTYPE html>
<html>
<head>
<title>requirejs学习</title>
</head>
<body>
<script src='./js/require.js'></script>
<script src="./js/config.js"></script>
<script>
require(['people'], function (people) {
people.say('Neol');
})
require(['cat'], function (people) {
people.say();
})
</script>
</body>
</html>
```
```
// ./js/config.js
require.config({
//默认情况下从这个文件开始拉去取资源
baseUrl:'./js/',
path: {
'jquery': './js/jquery.js'
}
});
```
```
// ./js/people.js
define(['jquery'], function() {
return {
say: function say(name){
console.log('I am ' + name, $);
}
}
});
```
```
// ./js/cat.js
define([], function() {
return {
say: function say(){
console.log('喵喵喵~');
}
}
});
```
特点:
* js、jquery可以直接使用
## 2.CMD(seaJs)
SeaJS 是一个适用于 Web 浏览器端的模块加载器。使用 SeaJS,可以更好地组织 JavaScript 代码。
```
<!--seajs.html-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>seajs入门实例</title>
</head>
<body>
<div id="hello">132456798</div>
</body>
<script src="./js/sea.js"></script>
<script src="./js/config.js"></script>
<script>
// 加载入口文件
seajs.use(['./js/hello'], function(hello) {
$('#hello').click(function(){
hello()
})
});
</script>
</html>
```
```
// config.js
seajs.config({
base: './js/',
path: {
'jquery': './js/jquery.js'
}
});
```
```
// hello.js
define(function(require, exports, module) {
require('jquery');
module.exports = function () {
$('#hello').toggle('slow');
}
});
```
特点:
* js、jquery可以直接使用
## 3.es6模块化
```
// 1.js
export const a = 1;
export const b = 2;
// 2.js
import {a, b} from './1.js';
```
```
// 1.js
const a = 1;
const b = 2
export {a, b}
// 2.js
import {a, b} from './1.js';
// 3.js
import * as obj from './1.js';
```
```
// 1.js
const a = 1;
export default a;
// 2.js
import obj from './1.js';
```
```
// 1.js
import('./2.js').then(res =>{
console.log(res);
});
```
特点:
* 每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;
* 每一个模块内声明的变量都是局部变量, 不会污染全局作用域;
* 模块内部的变量或者函数可以通过export导出;
* 一个模块可以导入别的模块
* export能有多个,但是export default只能有一个。
缺点:需要支持es6,或者使用babel.js转换。
结论:用在有webpack可以配置babel的项目,不用在jquery项目。
## 4.浏览器中的 ES6 module 实现
```
<script type="module">
import { alert_msg } from './utils.js'
alert_msg('123456789')
</script>
// 使用 `nomodule` 属性向后兼容
<script nomodule src="fallback.js"></script>
```
```
// utils.js
export function alert_msg (text) {
alert(text)
}
```
特点:
* 浏览器加载 ES6 模块,也使用`<script>`标签,但是要加入`type="module"`属性;type="module" 等同于打开了`<script>`标签的`defer`属性
* 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见
* 模块脚本自动采用严格模式,不管有没有声明`use strict`
* 模块之中,可以使用`import`命令加载其他模块(`.js`后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用`export`命令输出对外接口
* 模块之中,顶层的`this`关键字返回`undefined`,而不是指向`window`。也就是说,在模块顶层使用`this`关键字,是无意义的
* 同一个模块如果加载多次,将只执行一次
* 利用顶层的`this`等于`undefined`这个语法点,可以侦测当前代码是否在 ES6 模块之中
缺点:兼容性不足
![image.png](./upload/201912/11/1576045913604281624.png)
结论:不要用
## 5.CommonJS
根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。所以,定义一个模块就是写一个新的js文件,但是最后要将文件的内容exports出来。接下来我们看一下如何定义模块和加载模块。
```
//定义一个module.js文件
var A = function() {
console.log('我是定义的模块');
}
//导出这个模块
//1.第一种返回方式 module.exports = A;
//2.第二种返回方式 module.exports.test = A
//3.第三种返回方式 exports.test = A;
exports.test = A;
//再写一个test.js文件,去调用刚才定义好的模块,这两个文件在同一个目录下
var module = require("./module"); //加载这个模块
//调用这个模块,不同的返回方式用不同的方式调用
//1.第一种调用方式 module();
//2.第二种调用方式 module.test();
//3.第三种调用方式 module.test();
module.test();
//接下来我们去执行这个文件,前提是你本地要安装node.js,不多说了,自己百度安装。
//首先打开cmd, cd到这两个文件所在的目录下,执行: node test.js
node test.js
//输出结果:我是定义的模块
```
特点:
* 模块化:模块化就是将不同功能的函数封装起来,并提供使用接口,他们彼此之间互不影响。
* 不会阻塞页面:RequireJS,会在相关的js加载后执行回调函数,这个过程是异步的,所以它不会阻塞页面。
* 按需加载:平时我们写html文件的时候,在底部可能会引用一堆js文件。在页面加载的时候,这些js也会全部加载。使用require.js就能避免此问题。举个例子,比如说我写了一个点击事件,放到了一个js文件里,并在html引用,在不使用require.js的情况下,页面加载它跟着加载,使用后则是什么时候触发点击事件,什么时候才会加载js。
缺点:
* 只能用在node服务端。
[.zip](./upload/201912/11/15760622351371730206.zip)