美文网首页
import,export default,exports,re

import,export default,exports,re

作者: 我才是大田田 | 来源:发表于2020-05-21 16:00 被阅读0次

    一、使用

    Require: node 和 es6

    export,export default ,import : es6

    module.exports ,exports : node

    二、node的module

    node中遵循的是commonJs规范。

    CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

    (1)exports
    • exports使用方法
    function sayHi() {
        console.log('hi');
    }
    exports.sayHi = sayHi;
    

    引入

    let sayHiModule = require('./test.js');
    console.log(sayHiModule);
    // { sayHi: [Function: sayHi] }
    
    (2)module.exports
    • module.exports使用方法
    function sayHi() {
        console.log('hi');
    }
    module.exports = sayHi;
    

    引入

    let sayHiModule = require('./test.js');
    console.log(sayHiModule);
    // function sayHi() {
    //    console.log('hi');
    // }
    
    (3)对比现象

    a) 使用exports时,打印出module.exports和exports,二者是相等的

    function sayHi() {
        console.log('hi');
    }
    exports.sayHi = sayHi;
    
    console.log(module.exports);
    // { sayHi: [Function: sayHi] }
    console.log(exports);
    // { sayHi: [Function: sayHi] }
    console.log(module.exports === exports);
    // true
    

    b) 使用module.exports时,打印出module.exports和exports,二者居然又不相等了

    function sayHi() {
        console.log('hi');
    }
    module.exports = sayHi;
    
    console.log(module.exports);
    // [Function: sayHi]
    console.log(exports);
    // {}
    console.log(module.exports === exports);
    // false
    
    (4)原因

    a) Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

    var exports = module.exports;
    

    也就意味着,他俩指向同一个内存区域。

    b) 什么也不做, 先打印出module.exports和exports,会发现他俩都是空对象。

    console.log(module.exports);
    // {}
    console.log(exports);
    // {}
    

    所以(3)中的 a) exports. sayHi=sayHi使这个空对象多了个sayHi的属性。
    而(3)中的 b) module.exports = sayHi;使得module.exports的内存指向了另一个内存区域,所以module.exports变了,而exports还是那个空对象。

    (5)require

    那么require到底用的是哪个?

    其实从上面使用方法的例子里也能看出来,require引的是module.exports指向的那块内存。

    看文档发现require居然还根据路径缓存,见下面的栗子

    let sayHiModule = require('./test.js');
    let sayHiModule2 = require('./test.js').name = 'Jack';
    console.log(sayHiModule);
    // { sayHi: [Function: sayHi], name: 'Jack' }
    

    三、es6的module

    上面提到,commonJs的module是一个对象,运行时加载。

    而es6的module不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入,在编译时就完成模块加载。

    (1)export

    下面2种写法等价。

    // profile.js
    export var firstName = 'Michael';
    export var lastName = 'Jackson';
    export var year = 1958;
    
    // profile.js
    var firstName = 'Michael';
    var lastName = 'Jackson';
    var year = 1958;
    
    export { firstName, lastName, year };
    

    export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。(所以export后必须加上大括号)

    (2)import

    同样import也要对应上export的对外接口。

    // main.js
    import { firstName, lastName, year } from './profile.js';
    

    由于是静态加载,必须在最外层,不能条件加载。

    (3)export default

    export default可以用来解决接口名一一对应的麻烦。

    // 第一组
    export default function crc32() { // 输出
      // ...
    }
    
    import crc32 from 'crc32'; // 输入
    
    // 第二组
    export function crc32() { // 输出
      // ...
    };
    
    import {crc32} from 'crc32'; // 输入
    

    上面的栗子,用了export default,import的时候就可以直接命名,而不需要用{}去对应上export的接口。

    export default其实是输出了一个叫default的变量,所以export default不能接变量声明语句。

    四、babel将es6模块转为commonJs规范

    贴一个小网站,大家可以自己试试。

    (1)export default
    // es6
    export default function() {
      console.log('hi');
    }
    
    // 转完
    "use strict";
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.default = _default;
    
    function _default() {
      console.log('hi');
    }
    
    (2)export
    // es6
    let a = 'hi';
    export {a};
    
    // 转完
    "use strict";
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.a = void 0;
    let a = 'hi';
    exports.a = a;
    
    (3)import
    // es6
    import a from 'a';
    
    // 转完
    "use strict";
    
    var _a = _interopRequireDefault(require("a"));
    
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    
    (4)结论

    a)对于导出:
    babel会把export {a} 变成 exports.a = .....;
    把export default 变成 exports.default = ....;
    并把exports.__esModule 变成 true,表明它是从es6转成commonJs的。

    b)对于引入

    import a from 'a'
    这句本意上是引入a导出对象的default属性。
    而转换后var a = require(./a.js)得到的却是整个对象

    所以babel有个_interopRequireDefault函数,其实说白了就是个if else:
    如果有__esModule,代表obj本来就是从es6转成commonJs的,直接返回obj;
    如果没有__esModule,代表obj本来就是commonJs规范,所以要给它包一层default。

    (5)babel的按需引入插件做了什么(babel-plugin-component)

    a)没有按需的情况下,引入一个button,转成commonJs其实就把库全部引入了

    // es6
    import {Button} from 'element-ui';
    
    // babel转完
    "use strict";
    
    var _elementUi = require("element-ui");
    

    b)加上babel-plugin-component之后,按官方给的用法如下

      // babel配置
      plugins: [
            [
                'component',
                {
                    libraryName: 'element-ui',
                    styleLibraryName: 'theme-chalk'
                }
            ],
      ]
    

    插件就会把

    import { Button } from 'components'

    转成

    var button = require('components/lib/button')
    require('components/lib/styleLibraryName/button.css')

    这样就达到了按需引入。

    五、webpack模块化做了什么

    这里

    参考

    终于讲清楚了nodejs中exports和module.exports的区别

    import、require、export、module.exports 混合使用详解

    CommonJS规范

    相关文章

      网友评论

          本文标题:import,export default,exports,re

          本文链接:https://www.haomeiwen.com/subject/rjgbnhtx.html