美文网首页
Node中的模块使用

Node中的模块使用

作者: 薯条你哪里跑 | 来源:发表于2021-08-02 18:07 被阅读0次

    1.概述

    node默认使用的是commonjs模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

    如果要共享只能挂在全局变量global上(不推荐这种做法ヽ(`Д´)ノ):

    global.plat = “PC”;
    

    或者通过模块引用的方式(推荐(๑•̀ㅂ•́)و✧):。

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

    2. 加载

    CommonJS使用require来引入模块,使用exportsmodule.exports来导出模块(module);

    const { read, write } = require('./file');
    

    CommonJS只能在运行时才能确定模块的引用及依赖关系。上述代码其实是先加载file这个整个对象,之后再取上面的属性。

    const file = require('./file');
    const read  =   file.read
    const write  =   file.write
    

    ps: 但是es6是通过export命令直接导出,在编译时就可以判断要加载的方法即我们所说的静态编译。为后续的类型校验和宏提供了可能

    3.module

    Node内部有个Module的构造函数,每个文件中的module都是其的一个实例,module有以下几个属性:

    • id 模块的识别符,通常是带有绝对路径的模块文件名。
    • path 模块的路径,模块文件对应的路径。
    • exports 表示模块对外输出的值。
    • parent 返回一个对象,表示调用该模块的模块,也是Module的一个实例。
    • filename 模块的文件名,带有绝对路径。
    • loaded 返回一个布尔值,表示模块是否已经完成加载。
    • children 返回一个数组,表示该模块要用到的其他模块。
    • paths 返回一个数组,表示模块的搜索路径。
    随便找个文件,打印一下console.log(module)
    Module {
      id: '/Users/pub/f-node-encryption/dist/index.js',
      path: '/Users/pub/f-node-encryption/dist',
      exports: {},
      parent: Module {
        id: '.',
        path: '/Users/pub/f-node-encryption/test',
        exports: {},
        parent: null,
        filename: '/Users/pub/f-node-encryption/test/index.js',
        loaded: false,
        children: [ [Circular] ],
        paths: [
          '/Users/pubf-node-encryption/test/node_modules',
          '/Users/pub/f-node-encryption/node_modules',
          '/Users/pub/node_modules',
          '/Users/node_modules',
          '/node_modules'
        ]
      },
      filename: '/Users/pub/f-node-encryption/dist/index.js',
      loaded: false,
      children: [
        Module {
          id: '/Users/pub/f-node-encryption/dist/src/lib/util.js',
          path: '/Users/pub/f-node-encryption/dist/src/lib',
          exports: [Object],
          parent: [Circular],
          filename: '/Users/pub/f-node-encryption/dist/src/lib/util.js',
          loaded: true,
          children: [Array],
          paths: [Array]
        }
      ],
      paths: [
        '/Users/pub/f-node-encryption/dist/node_modules',
        '/Users/pub/f-node-encryption/node_modules',
        '/Users/pub/node_modules',
        '/Users/node_modules',
        '/node_modules'
      ]
    }
    
    

    node引用模块很简单,在文件内部使用exportsmodule.exports来导出。举个🌰

    // a.js  定义一个TestClass类
    class TestClass {
      constructor(){}
      .....
    }
     module.exports = TestClass
    
    // b.js 引用并实例化
    const  TestClass = require("./a")
    cosnt curTest =  new  TestClass()
    ....
    
    console.log(exports===module.exports)  // 输出true
    

    这里使用module.exports来进行导出,当然也可以使用 exports, 但是!!!我们不能直接给 exports赋值,像这样:

    exports = TestClass
    

    这是不可以的!!!因为使用的exports其实是module.exports的引用,如果直接赋值会使引用断开,此时在b.js内打印TestClass会输出module.exports的值,此时就是一个空对象。

    再次重申,模块导出导出的是module.exports的值!!!

    当然在保留引用的前提下可以进行赋值:

    // a,js
    exports.test = TestClass
    
    // b.js 
    const  TestClass = require("./a")
    cosnt curTest =  new  (TestClass.test)()
    

    4. CommonJS模块的特点。

    主要有以下三点:

    • 所有代码都运行在模块作用域,不会污染全局作用域。
    • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存(delete require.cache[moduleName])。
    • 模块加载的顺序,按照其在代码中出现的顺序。
    关于第二条还想仔细说一下

    举个缓存的🌰:

    // a.js
    var tmp = {name::“Jack”};
    module.exports = tmp; 
    
    // b.js
    const OnceTest = require("./a")
    // 输出OnceTest为 {name:“Jack”}
    console.log("OnceTest:", OnceTest) 
    // 给 OnceTest添加新的属性
    OnceTest.age = 21;   
    // 再次引入相同模块
    const TwiceTest = require("./a")
    // 此时打印TwiceTest,发现里面也有age属性
    console.log("TwiceTest:", TwiceTest)  // 输出{name:“Jack”, age:21} 
    

    如果我想清除缓存那么需要修改下b.js:

    const OnceTest = require("./a")
    console.log("OnceTest:", OnceTest) 
    OnceTest.age = 21;   
    // 先要清除引入模块的缓存
    delete require.cache['/Users/pub/f-node-encryption/a.js']
    // 再次引入相同模块
    const TwiceTest = require("./a")
    // 此时打印TwiceTest,发现已经是从a.js新引入的模块了
    console.log("TwiceTest:", TwiceTest)  // 输出{name:“Jack”} 
    

    详细cache里的内容可以打印require.cache查看。
    这里贴一个清除缓存时存在的问题 传送门

    4.顺便记一下ES中的模块使用

    在ES中的模块使用相对灵活,使用export export detault来导出,使用import来引用。

    我们先看一下exportexport default这两个导出方式有什么不同

    • 在一个文件中,export可以有多个但export default只能有一个。
    • 通过export方式导出,在导入时要加{ },export default则不需要。
    • export能直接导出变量表达式,export default不行。
    // 导出变量
    export const a = 123;
    // 导出方法
    export const getName = function(){}
    // 导出方法
    function getName(){}
    export {getName}
    
    // excport default 导出变量
    const a = 123;
    export default a
    
    // 导入
    // 使用 export 方法导出
    import { dogSay, catSay } from './test'; 
    // 使用export default导出
    import a from './test';  
    // as 集合成对象导出
    import * as testModule from './test'; //as 集合成对象导出
    

    其他细节不再次赘述啦,感兴趣的小伙伴自行学习;
    还有一点需要注意一下,因为import是静态解析的,所以在引入前使用也是不会报错的:

    dogSay()
    import { dogSay, catSay } from './test';
    

    但是 不可以在这样“动态”使用import

    const  moduleName = "dog"
    import { `${moduleName}Say` } from './test';
    // 或者这样
    if(moduleName === "dog"){
      import { dogSay`} from './test';
    }
    

    写的有点凌乱回头在整理一波= =

    相关文章

      网友评论

          本文标题:Node中的模块使用

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