import ? require ? module ? export ? 这次彻底搞懂!

603 阅读3分钟

前端模块化

模块化是一种将系统分离成独立功能部分的方法,一个模块是为完成一个功能的一段程序或子程序。"模块"是系统中功能单一可替换的部分。

模块化思想是从java上衍生过来的,他将所需要的功能封装成一个类,哪里需要就在哪里调用,JS中没有类的说法,但它引入了这种思想,在js中用对象或构造函数来模拟类的封装实现模块化,而在html上,则使用importrequire

所属规范

require/exports 是 CommonJS/AMD 中为了解决模块化语法而引入的。

import/export 是ES6引入的新规范,因为浏览器引擎兼容问题,需要在node中用babel将ES6语法编译成ES5语法。

关于import在浏览器中被支持的情况如下 8646214-28ccb150c604e16a.png

调用时间

require 是运行时调用,所以理论上可以运作在代码的任何地方。 import 是编译时调用,所以必须放在文件的开头。

原理

require 是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把结果赋值给某个变量。它是普通的值拷贝传递。

import 是解构过程。使用import导入模块的属性或者方法是引用传递。且importread-only的,值是单向传递的。default是ES6 模块化所独有的关键字,export default {} 输出默认的接口对象,如果没有命名,则在import时可以自定义一个名称用来关联这个对象。

require & exports & module.exports

module.exports是真正的接口,exports只不过是它的辅助工具;最终返回给调用的是Module.exports而不是exports。

module.exports 导出的为具体类型的实例化对象,而exports 则不是。

其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。最终返回给调用的是Module.exports而不是exports

注意: 所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。

exports

//导出文件module.js
function outputA(){
    console.log("this is output A");
}

function outputB(){
    console.log("this is output B");
}
`会形成一个对象
{
  outputA:[Function: exports.outputA],
  outputB:[Function: exports.outputB]
}`
exports.outputA = outputA; //通过exports写法
exports.outputB = outputB;//通过exports写法
//导入文件module.js
// 方式1
let mod = require("./mout.js");

// 方式2
let {outputA, outputB} = require("./mout.js");

module.exports

// 导出
module.exports = function(name, age) { 
    this.name = name; 
    this.age = age; 
    this.about = function() { 
        console.log(this.name +' is '+ this.age +' years old'); 
    }; 
}; 
// 导入
var Rocker = require('./rocker.js'); 
var r = new Rocker('Ozzy', 62); 
r.about(); // Ozzy is 62 years old 

exports + module.exports

// 导出
const outputA = 'this is outputA'
const outputB = 'this is outputB'
exports.outputA = outputA
exports.outputB = outputB
module.exports = function(name){
    console.log('name: ', name)
}
// 导入
const someModule = require('./module.js')
someModule.outputA  // 报错,outputA和outputB被覆盖了
someModule('tom')

export & export default

// 导出 module.js
export function test(args) {
  console.log(args);
}
// 定义一个默认导出文件, 一个文件只能定义一次
export default {
  a: function() {
    console.log('export from module');
  }
}

export const name = 'gzc'
// 倒入
import common, { test } from './module.js'
common.a() // 打印 export from module
test(`my name is harry`) // 打印 my name is harry

注意

WX20220707-161258.png

有可能报这个错 Cannot assign to read only property 'exports' of object '#<Object>'

在同一个文件中,导入方式可以两种一起用,但是导出方式要注意,module.exports不能和import同时出现。

总结

  • 通过require引入基础数据类型时,属于复制该变量。

  • 通过require引入复杂数据类型时, 属于浅拷贝该对象。

  • 出现模块之间循环引用时, 会输出已执行的模块, 未执行模块不会输出。

  • CommonJS规范默认export的是一个对象,即使导出的是基础数据类型。