import和require的区别

    技术2023-06-15  80

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

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

    一、CommonJs

    1.1 概述

    Nodejs 是 CommonJS 规范的主要实践者,在 CommonJs 里每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。CommonJs 提供了四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。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 直接赋值一个变量,这样会切断 exports 和 module.exports 之间的联系。

    如果有 exports 和 module.exports,exports 就会失效,只会输出 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 的模块功能主要由两个命令构成:export 和 import。 export 命令用于规定模块的对外接口。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 指向当前模块 }
    Processed: 0.036, SQL: 9