首頁  >  文章  >  web前端  >  了解Node.js中的模組系統

了解Node.js中的模組系統

青灯夜游
青灯夜游轉載
2020-11-24 17:58:153252瀏覽

了解Node.js中的模組系統

相關推薦:《node js教學

#Node.js 的模組

JavaScript 做為一門為網頁添加互動功能的簡單腳本語言問世,在開始並不包含模組系統,隨著JavaScript 解決問題越來越複雜,把所有程式碼寫在一個檔案內,用function 區分功能單元已經不能支撐複雜應用開發了,ES6 帶來了大部分高階語言都有的class 和module,方便開發者組織程式碼

import _ from 'lodash';

class Fun {}

export default Fun;

上面三行程式碼展示了一個模組系統最重要的兩個要素import 和export

  • export用於規定模組的對外介面

  • #import用於輸入其他模組提供的功能

而在ES6 之前,社群出現了許多模組載入方案,最主要的有CommonJS 和AMD 兩種,Node.js 誕生早於ES6,模組系統使用的是類似CommonJS的實現,遵從幾個原則

  • 一個檔案是一個模組,檔案內的變數作用域都在模組內

  • ##使用

    module.exports 物件導出模組對外介面

  • 使用

    require 引入其它模組

# circle.js

const { PI } = Math;

module.exports = function area(r) {
  PI * r ** 2;
};
上面程式碼就實作了Node.js 的一個模組,模組沒有依賴其它模組,導出了方法

area 計算圓的面積

#test.js

const area = require('./circle.js');
console.log(`半径为 4 的圆的面积是 ${area(4)}`);
模組依賴了circle.js,使用其對外暴露的area 方法,計算圓的面積

module.exports

模組對外暴露介面使用module.exports,常見的有兩種用法:為其新增屬性或賦值到新物件


test.js

// 添加属性
module.exports.prop1 = xxx;
module.exports.funA = xxx;
module.exports.funB = xxx;

// 赋值到全新对象
module.exports = {
  prop1,
	funA,
  funB,
};
兩種寫法是等價的,使用時候沒區別

const mod = require('./test.js');

console.log(mod.prop1);
console.log(mod.funA());
還有另外一種直接使用

exports 對象的方法,但是只能對其添加屬性,不能賦值到新對象,後面會介紹原因

// 正确的写法:添加属性
exports.prop1 = xxx;
exports.funA = xxx;
exports.funB = xxx;

// 赋值到全新对象
module.exports = {
  prop1,
	funA,
  funB,
};
require('id')

模組類型

require 使用比較簡單,id 支援模組名稱與檔案路徑兩種型別

模組名稱

const fs = require('fs');
const _ = require('lodash');

範例中的fs、lodash 都是模組名,fs 是Node.js 內建的核心模組,lodash 是透過npm 安裝到

node_modules 下的第三方模組,如果出現重名,優先使用系統內建模組

因為一個專案內可能會包含多個node_modules 資料夾(Node.js 比較失敗的設計),第三方模組查找過程會遵循就近原則逐層上溯(可以在程序中列印

module.paths 查看特定尋找路徑),直到根據NODE_PATH 環境變數找出檔案系統根目錄,具體流程可以參考官方文件

#此外,Node .js 也會搜尋以下的全域目錄清單:

    $HOME/.node_modules
  • $HOME/.node_libraries
  • $PREFIX/lib/node
其中

$HOME 是使用者的主目錄, $PREFIX 是Node.js 裡配置的node_prefix。強烈建議將所有的依賴放在本地的 node_modules 目錄,這樣將會更快地加載,且更可靠

檔案路徑

模組還可以使用檔案路徑加載,這是項目內自訂模組的通用載入方式,路徑可以省略拓展名,會依照.js、.json、.node 順序嘗試

  • '/' 為前綴的模組是文件的絕對路徑,依照系統路徑尋找模組
  • './' 為前綴的模組是相對於目前呼叫require 方法的文件,不受後續模組在哪裡被使用到影響
單次載入& 循環依賴

模組在第一次載入後會被快取到

Module._cache ,如果每次都呼叫 require('foo') 都解析到相同文件,則傳回相同的對象,同時多次呼叫 require(foo) 不會導致模組的程式碼執行多次。 Node.js 根據實際的檔案名稱快取模組,因此從不同層級目錄引用相同模組不會重複載入。

理解的模組單次載入機制方便我們理解模組循環依賴後的現象


a.js

console.log('a 开始');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log('a 结束');

b.js

console.log('b 开始');
exports.done = false;
const a = require('./a.js');
console.log('在 b 中,a.done = %j', a.done);
exports.done = true;
console.log('b 结束');

main.js:

console.log('main 开始');
const a = require('./a.js');
const b = require('./b.js');
console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);
當 main.js 載入 a.js 時,a.js 又載入 b.js,此時,b.js 會試著去載入 a.js

為了防止無限的迴圈會回傳一個 a.js 的 exports 物件的 

未完成的副本 給 b.js 模組,然後 b.js 完成載入,並將 exports 物件提供給 a.js 模組

因此範例的輸出是

main 开始
a 开始
b 开始
在 b 中,a.done = false
b 结束
在 a 中,b.done = true
a 结束
在 main 中,a.done=true,b.done=true

看不懂上面的过程也没关系,日常工作根本用不到,即使看懂了也不要在项目中使用循环依赖!

工作原理

Node.js 每个文件都是一个模块,模块内的变量都是局部变量,不会污染全局变量,在执行模块代码之前,Node.js 会使用一个如下的函数封装器将模块封装

(function(exports, require, module, __filename, __dirname) {
	// 模块的代码实际上在这里
});
  • __filename:当前模块文件的绝对路径
  • __dirname:当前模块文件据所在目录的绝对路径
  • module:当前的模块实例
  • require:加载其它模块的方法,module.require 的快捷方式
  • exports:导出模块接口的对象,module.exports 的快捷方式

回头看看最开始的问题,为什么 exports 对象不支持赋值为其它对象?把上面函数添加一句 exports 对象来源就很简单了

const exports = module.exports;
(function(exports, require, module, __filename, __dirname) {
	// 模块的代码实际上在这里
});

其它模块 require 到的肯定是模块的 module.exports 对象,如果吧 exports 对象赋值给其它对象,就和 module.exports 对象断开了连接,自然就没用了

在 Node.js 中使用 ES Module

随着 ES6 使用越来越广泛,Node.js 也支持了 ES6 Module,有几种方法

babel 构建

使用 babel 构建是在 v12 之前版本最简单、通用的方式,具体配置参考 @babel/preset-env(https://babeljs.io/docs/en/babel-preset-env)

.babelrc

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "node": "8.9.0",
        "esmodules": true
      }      
    }]
  ]
}

原生支持

在 v12 后可以使用原生方式支持 ES Module

  • 开启 --experimental-modules

  • 模块名修改为 .mjs (强烈不推荐使用)或者 package.json 中设置 "type": module

这样 Node.js 会把 js 文件都当做 ES Module 来处理,更多详情参考官方文档(https://nodejs.org/dist/latest-v13.x/docs/api/esm.html)

更多编程相关知识,请访问:编程视频!!

以上是了解Node.js中的模組系統的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除