js - CommonJs和ES6 module的使用和区别

1,先说区别

阮一峰老师在 ES6 入门 中提到 ES6 模块与 CommonJS 模块有一些重大的差异:

它们有三个重大差异:

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

2,CommonJS

commonjs 规范应用于 nodejs 应用中,在 nodejs 应用中每个文件就是一个模块,拥有自己的作用域,文件中的变量、函数都是私有的,与其他文件相隔离。

CommonJS模块的特点如下:

  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。
  • 同步的方式加载模块。

使用如下

  • 通过require函数进行导入
  • 通过export或者module.exports导出

CommonJS块暴露的方式有三种:


1,第一种暴露方式: exports.方法 || 属性

let name = 'Eula';
let obj = {
    name:'Eula',
    age:18
}
function say(){
    console.log("我是尤菈");
}

// 第一种暴露方式  exports.方法  ||  属性
exports.name = name
exports.obj = obj
exports.say = say;

require接收:

const receive = require('./src/utils.js')
console.log("接收到的",receive);
接收到的 { name: 'Eula', obj: { name: 'Eula', age: 18 }, say: [Function: say] }

2,第二种暴露方式: module.exports.方法 || 属性

let name = 'Eula';
let obj = {
    name:'Eula',
    age:18
}
function say(){
    console.log("我是尤菈");
}
// 第二种暴露方式: module.exports.方法  ||  属性
module.exports.name= name;
module.exports.obj=obj;
module.exports.say = say;

require接收:

const receive = require('./src/utils.js')
console.log("接收到的",receive);
接收到的 { name: 'Eula', obj: { name: 'Eula', age: 18 }, say: [Function: say] }

3,第三种暴露 : module.exports = {方法 , 属性}

直接暴露出去一个对象,对面接收的也是一个对象;

let name = 'Eula';
let obj = {
  name: 'Eula',
  age: 18,
};
function say() {
  console.log('我是尤菈');
}
// 第三种 : module.exports = {方法 , 属性}  直接暴露出一个对象
module.exports = {
  name,
  obj,
  say,
};

接收:

const receive = require('./src/utils.js')
console.log("接收到的",receive);
接收到的 { name: 'Eula', obj: { name: 'Eula', age: 18 }, say: [Function: say] }

注意:
Module.exports = {} 第三种方式和前两种多少有一点点区别;如果说 我们三种方式同时使用 或者说 只要使用第三种方式;那么他会覆盖前两种方式的暴露;

3,ES6 module

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。
其模块功能主要由两个命令构成:exportexport defaultimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

使用如下

  • 通过import进行导入
  • 通过export(具名导出)或者export default(默认导出)

ES6 module 导出方式有两种:

1,第一种导出方式:export逐个导出 或 export { }直接导出一个对象

新建一个html文件和utils.js:

1,把export 逐一写在属性或对象或方法前面;

// 1,把export 逐一写在属性或对象或方法前面
export let name = 'Eula';
export let obj = {
  name: 'Eula',
  age: 18,
};
export function say() {
  console.log('我是尤菈');
}

2,或者直接写到一个对象中一起导出;

//2,或者直接写到一个对象中一起暴露
let name = 'Eula';
let obj = {
  name: 'Eula',
  age: 18,
};
function say() {
  console.log('我是尤菈');
}
export { name, obj,say }; // 一起暴露

接收上面导出的值: 接收的时候也有两种方式,逐个接收和整体起别名接收一个对象;

<!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>modules</title>
</head>
<body>
    <script type="module">
      // 第一种接收方式 :逐个接收
       import { name, obj, say } from './utils.js';
       console.log('name:', name);
       console.log('obj:', obj);
       console.log('say:', say);
       
	  //  第二种接收方式 :整体导出起别名 receive接收的是一个对象
      import * as receive from './utils.js';
      console.log('receive:', receive);
    </script>
</body>
</html>

注意:给script标签添加属性type为module,告诉其加载的不是一个普通的js文件,而是一个js模块;
如果直接在浏览器打开这个html文件,会报跨域问题;这是由于设置为type设置为module类型之后,就出现了跨域问题。

当你在某盘符位置下直接打开一个html文件(script标签中引入了某地的某个js文件),则在浏览器地址栏呈现如下:file:///F:/vscode/css3%E8%BF%9B%E9%98%B6/html5-3d-game-scene/indexw.html则会出现跨域问题。而http、https等协议支持跨域请求。

以解决办法就是通过搭建本地一个服务器去进行资源的问题来解决跨域问题。

我这里使用了 VScode中的Live Serve插件打开就可以了;
在这里插入图片描述

2,第二种导出方式:export default (默认导出)

export default { }

默认导出一个对象;不支持对象解构语法;接收的时候也是接收的一个对象;

let name = 'Eula';
let obj = {
  name: 'Eula',
  age: 18,
};
function say() {
  console.log('我是尤菈');
}
// 默认导出
export default  {
  name,
  obj,
  say,
};

接收:

<!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>modules</title>
  </head>
  <body>
    <script type="module">
      // 默认导出接收方式
      import receive from './utils.js'
      console.log("receive:",receive);
    </script>
  </body>
</html>

当使用默认导入时,你可以在 import 语句后面进行任意命名。

比如 import Banana from ‘./Button.js’,如此你能获得与默认导出一致的内容。

相反:对于具名导入,导入和导出的名字必须一致。这也是为什么称其为 具名导出的原因!

通常,文件中仅包含一个组件时,人们会选择默认导出,而当文件中包含多个组件或某个值需要导出时,则会选择具名导出。

注意: Es Module 默认开启了严格模式。即便你没有在文件的开头添加 "use strict"

既然开启了严格模式,严格模式限制有哪些?

主要有以下限制:

  1. 变量必须声明后再使用
  2. 函数的参数不能有同名属性,否则报错
  3. 不能使用 with 语句
  4. 不能对只读属性赋值,否则报错
  5. 不能使用前缀 0 表示八进制数,否则报错
  6. 不能删除不可删除的属性,否则报错
  7. 不能删除变量 delete prop,会报错,只能删除属性 delete global[prop]
  8. eval 和 arguments 不能被重新赋值 arguments
  9. 禁止 this指向全局对象
  10. 增加了保留字(implements、interface、let、package、private、protected、public、static、yield)

End