乐趣区

import和require的区别

关于 importrequire 的不同,其实可以理解成 CommonJs 和 ES Module 的区别。这两者都是前端模块化的规范。

我们在 node 里使用的是 CommonJs,在前端页面的时候,用的是 ES Module,这两者的区别,还是很容易混淆的,所以整理一下 CommonJs 和 ES Moudule 的相关知识点,把这里好好的整理一下。

一、CommonJs

1.1 概述

Nodejs 是 CommonJS 规范的主要实践者,在 CommonJs 里每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。CommonJs 提供了四个重要的环境变量为模块化的实现提供支持:moduleexportsrequireglobal。CommonJS 规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。

var x = 5;
var addX = function (value) {return value + x;};

module.exports.x = x;
module.exports.addX = addX;

而使用 require 方法来引入并加载模块

var example = require('./example.js');

console.log(example.x); // 5
console.log(example.addX(1)); // 6

1.2 CommonJS 模块的特点

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

关于变量:

1、module 对象是 node 里的 Module 构造函数的实例,代表当前模块。具有以下属性:

  • module.id 模块的识别符,通常是带有绝对路径的模块文件名。
  • module.filename 模块的文件名,带有绝对路径。
  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。
  • module.parent 返回一个对象,表示调用该模块的模块。
  • module.children 返回一个数组,表示该模块要用到的其他模块。
  • module.exports 表示模块对外输出的值。

2、exports

NodeJs 为每个模块提供一个 exports 变量,指向 module.exports在使用的时候,可以直接给 exports 添加属性,就会指向 module.exports。但是不能给 exports 直接赋值一个变量,这样会切断 exportsmodule.exports 之间的联系。

如果有 exportsmodule.exportsexports 就会失效,只会输出 module.exports 的,因为 module.exports 被重新赋值了。

3、global

这是一个全局变量声明方式,就可以全局用这个 warning 变量了。

global.warning = true;

4、require

require 命令用于加载模块文件。

require 命令的基本功能是,读入并执行一个 JavaScript 文件,然后返回该模块的 exports 对象
加载规则:

  • 如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。
  • 如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。
  • 如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于 Node 的系统安装目录中),或者一个位于各级 node_modules 目录的已安装模块(全局安装或局部安装)。
  • 如果参数字符串不以“./“或”/“开头,而且是一个路径,比如 require(‘example-module/path/to/file’),则将先找到 example-module 的位置,然后再以它为参数,找到后续路径。
  • 如果指定的模块文件没有发现,Node 会尝试为文件名添加.js、.json、.node 后,再去搜索。.js 件会以文本格式的 JavaScript 脚本文件解析,.json 文件会以 JSON 格式的文本文件解析,.node 文件会以编译后的二进制文件解析。
  • 如果想得到 require 命令加载的确切文件名,使用 require.resolve() 方法。

require 函数及其辅助方法主要如下。

  • require(): 加载外部模块
  • require.resolve():将模块名解析到一个绝对路径
  • require.main:指向主模块
  • require.cache:指向所有缓存的模块
  • require.extensions:根据文件的后缀名,调用不同的执行函数

关于缓存:

在多次加载某个相同的模块时,如果前面的模块已经操作了,后面调用时,拿到就不再是最初时的数据了,就是经过前面操作过后的数据了。

所有缓存的模块保存在 require.cache 之中,如果想删除模块的缓存,可以像下面这样写。

// 删除指定模块的缓存
delete require.cache\[moduleName\];

// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {delete require.cache\[key\];
})

注意:缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require 命令还是会重新加载该模块。

1.3 模块的加载机制

CommonJS 模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

二、ES Module

2.1 概述

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。所以 ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

2.2 ES Module 的特点:

ES6 的模块功能主要由两个命令构成:exportimportexport 命令用于规定模块的对外接口。import 命令用于输入 其他模块提供的功能。

  • ES6 模块必须用 export 导出
  • export 必须与模块内部的变量建立一一对应关系

1、export 命令

  • 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用 export 关键字输出该变量。
  • export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

2、import 命令

  • import 命令输入的变量都是只读的
  • import 命令具有提升效果
  • import 是静态执行,所以不能使用表达式和变量
  • import 语句是 Singleton 模式,如果多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。

3、export default 命令

  • export default 就是输出一个叫做 default 的变量或方法
  • export default 所以它后面不能跟变量声明语句

三、CommonJs 和 ES Module 的区别

  • ES6 模块输出的是值的引用,CommonJS 模块输出的是一个值的拷贝
  • ES6 模块是编译时输出接口,CommonJS 模块是运行时加载。
  • ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。而 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。
es6 {export :‘可以输出多个,输出方式为 {}’,export default :‘只能输出一个,可以与 export 同时输出,但是不建议这么做’,解析阶段确定对外输出的接口,解析阶段生成接口,模块不是对象,加载的不是对象,可以单独加载其中的某个接口(方法),静态分析,动态引用,输出的是值的引用,值改变,引用也改变,即原来模块中的值改变则该加载的值也改变,this 指向 undefined

}

  

commonJS {

 module.exports = … :‘只能输出一个,且后面的会覆盖上面的’,exports. … :‘可以输出多个’,运行阶段确定接口,运行时才会加载模块,模块就是对象,加载的是该对象,加载的是整个模块,即将所有的接口全部加载进来,输出的是值的拷贝,即原来模块中的值改变不会影响已经加载的该值,this 指向当前模块

}
退出移动版