前端模块化(ES6和CommonJS)

前端模块化

对于开发大型的、复杂的项目,模块化显得十分重要。
ES6出现之前,社区主要有CommonJS和AMD两种,前者用于服务器,后者用于浏览器。ES6模块化则可以完全替代它们。模块化的一个特点就是:不会污染全局作用域

ES6模块与CommonJS和AMD的主要区别

ES6模块依赖是在编译时就能确定(方便静态优化,需要处于模块顶层),而后两者只能在运行时才能确定。

  • CommonJS 模块输出的是一个值的拷贝(如果是引用类型,拷贝指针),ES6 模块输出的是值的动态引用(singleton模式)(如果我想让一个ES6模块重复执行怎么做?)。
  • CommonJS 模块存在缓存,需要时要手动清除缓存
  • CommonJS 和 AMD 模块是运行时加载,ES6 模块是编译时输出接口(引用)(import()方法除外)。
  • ES6顶层this指向undefined,CommonJS模块的顶层this指向一个空对象({})

ES6模块化使用

在ES6代码中,文件就是模块,其内部所有变量,外部都无法获取,this指针值为undefined而非global对象

1、export命令
export var firstName = 'Michael';
export function print(){
	console.log("print");
}
export class Person{
}

等价于<=>

var firstName = 'Michael';
var print = function(){
	console.log("print")
}
class Person{
}
export { firstName, print , Person};

对于export输出的变量可以使用as关键字重命名

	function v1(){...}
	export {
		v1 as newName
	}

使用export导出时,需要提供一个接口(变量定义或者对象直接量形式),这个接口是其模块的一个 引用,可以动态更新。

// 报错
var m = 1;
export m;  //相当于export 1
2、import命令
//另一个js文件
import { firstName, lastName, year } from './profile.js';
  • 导入的接口是只读
  • import不能使用表达式和变量
  • import语句是 Singleton 模式
//import命令的提升效果
foo();
import { foo } from 'my_module';  //这一行会提升到文件首部,因为编译阶段就执行了
  • 可以整体加载模块
//整体加载
import * as circle from './circle';
  • 默认导出(可以和常规导出一起存在‘,’隔开)
// export-default.js导出
export default function () { //相当于将这个匿名函数赋值给default变量
  console.log('foo');
}
// import-default.js导入
import customName from './export-default';
  • 模块的转发
export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

//还可以改接口名
export { foo as baz, bar } from 'my_module';
  • 模块的继承
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}

// main.js
import * as math from 'circleplus';
import exp from 'circleplus';
console.log(exp(math.e));
  • 运行时加载
    import()函数可以替代require的动态加载,返回一个promise,参数为模块对象,可以解构出接口
import('./myModule.js')
.then(({export1, export2}) => {
  // ...·
});
3、ES6模块循环引用

因为ES6模块是采用动态引用方式,所以接口是在编译时确定好的,运行时是否有效需要开发人员自己保证。
在这里插入图片描述

CommonJS模块化使用

Node就是采用CommonJS规范。module变量代表当前模块,加载该模块就是加载该模块的module.exports属性。

CommonJS的特点:
  • 运行结果会缓存,需要时需要手动清除缓存
  • 模块加载顺序与代码中引用顺序一致
module对象属性
  • module.id模块的识别符,通常是带有绝对路径的模块文件名。
  • module.filename 模块的文件名,带有绝对路径。
  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。
  • module.parent 返回一个对象,表示调用该模块的模块。
  • module.children 返回一个数组,表示该模块要用到的其他模块。
  • module.exports 表示模块对外输出的值。
module.exports 和 exports

exports就指向module.exports,是个引用

注意:
  • 不要给exports赋值
  • 如果module.exports被赋值为primitive value,则exports无法使用
AMD规范与CommonJS规范

CommonJS规范加载模块是同步的,AMD规范是非同步的

require()方法
  • require一个目录
    如果require发现参数字符串指向一个目录以后,会自动查看该目录的package.json文件,然后加载main字段指定的入口文件。如果package.json文件没有main字段,或者根本就没有package.json文件,则会加载该目录下的index.js文件index.node文件。
  • require.resolve查询模块完整绝对路径(不会加载模块
const mod = require.resolve('./moduleB')
console.log(mod)
//console结果
E:\Code-Files\Front-End-Files\Study-Files\temp\moduleB.js
模块缓存

第一次执行一个模块,Node会缓存module.exports。输出的是一个值的拷贝,所以之后模块内部的变化不会影响输出,可以使用引用来解决这个问题。

//main.js
const mB = require('./moduleB')
setTimeout(() => {
    console.log(mB.label)
}, 1000)

//moduleB.js
let a={
    label:"hello"
}
setTimeout(()=>{
    a.label+=" world"
},500)
module.exports=a;
//输出结果为"hello world"
// 删除指定模块的缓存
delete require.cache[moduleName];
CommonJS模块的循环加载

如果模块A加载B,B又加载A,则B将加载A的不完整版本(A中require(B)的前面已经执行的部分部分),等到B执行完毕接着回来执行A剩余的部分,最后返回。
在这里插入图片描述

模块加载的实现

1、浏览器加载
//指定type=“module”
<script type="module" src="./foo.js"></script>
2、ES6模块加载CommonJS模块

Node的import命令会自动将module.exports当做模块默认输出。共有三种方式引入

// 写法一
import baz from './a';
// baz = {foo: 'hello', bar: 'world'};

// 写法二
import {default as baz} from './a';
// baz = {foo: 'hello', bar: 'world'};

// 写法三
import * as baz from './a';
// baz = {
//   get default() {return module.exports;},
//   get foo() {return this.default.foo}.bind(baz),
//   get bar() {return this.default.bar}.bind(baz)
// }

//由于CommonJS是运行时确定接口,所以禁止一下写法
import { readFile } from 'fs';
3、CommonJS 模块加载 ES6 模块

采用 import() 函数,而不能使用require命令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值