1. 程式人生 > >前端學習 node 快速入門 系列 —— 模組(module)

前端學習 node 快速入門 系列 —— 模組(module)

>其他章節請看: > >[前端學習 node 快速入門 系列][1] ## 模組(module) ### 模組的匯入 #### 核心模組 在 [初步認識 node][初步認識 node] 這篇文章中,我們在讀檔案的例子中用到了 `require('fs')`,在寫最簡單的伺服器的例子中用到了 `require('http')`,除了 fs 和 http,node 提供了很多核心模組,例如:path(路徑)、os(作業系統)、events(事件)、url 等等。 如果我們需要使用**核心模組**的功能,就使用 `require(模組名)` 方法進行引入。 #### 第三方模組 在 [npm][npm] 一文中,我們知道了如何用 npm 下載包。如果我們需要使用**第三方的模組**,也可以像引入核心模組那樣。請看示例: ```javascript // 首先得下載包。後續不再提醒 $ npm install underscore let _ = require('underscore') console.log(_.VERSION) console.log(typeof _.clone) console.log(typeof _.find) /* 輸出: 1.12.0 function function */ ``` *注*:由於核心模組和第三方模組(npm 下載的包)都是通過**模組名**載入,所以它們的名字不會相同。也就是說 node 不允許第三方模組佔用 node 核心模組的名字。 require() 方法載入第三方模組有這麼一套**規則**: 1. 找到執行檔案所屬目錄的 node_modules 資料夾 1. 找到 node_modules/第三方模組名/package.json 1. 找到 main 欄位指向的入口檔案 - 載入入口檔案 1. 找不到 package.json 或找不到 main 欄位,又或者找不到 main 指向的檔案,就載入 index.js 1. 以上都失敗,則去上一級找 node_modules 目錄,找不到又去上一級,上一級,直到根目錄都沒有找到,就報錯處理 我們用 underscore 這個第三方模組驗證一下上面的規則。請看示例: 首先準備環境,匯入 underscore。 ```javascript // 進入專案(D:\實驗樓\node-study\test2) // 快速生成 package.json $ npm init -y // 下載 underscore $ npm install underscore // 建立 test2/index.js let _ = require('underscore') console.log(_.VERSION) // 執行 $ node index 1.12.0 function ``` 現在模組已經正常匯入。 我們首先驗證一下:載入的是否是 main 欄位指向的檔案。具體步驟請看: ```javascript // 修改 package.json 中的 main 欄位。目錄是:test2\node_modules\underscore\package.json { "main": "underscore.js", // 改為 "main": "a.js", } // 新建 a.js 檔案。目錄是:test2\node_modules\underscore\a.js exports.name = 'ph' // 修改 index.js 內容如下 let _ = require('underscore') console.log(_) $ node index { name: 'ph' } ``` 重置 package.json 中 main 欄位,將 underscore 模組的主入口檔案改為 a.js,最終我們拿到的 underscore 確實是我們返回的 `{ name: 'ph' }`。 接著將 a.js 改為 index.js,執行 `node index`,輸出的還是 `{ name: 'ph' }`。說明 require() 找不到 main 指向的入口檔案 a.js,於是就去找 index.js。 最後將 node_modules 資料夾剪下至 D 盤根目錄中,執行 `node index` 仍舊輸出 `{ name: 'ph' }`。 #### 自定義模組 在 node 中,每個檔案都被視為一個**單獨的模組**。 瀏覽器可以通過 script 標籤執行多個 js 檔案,但 node 只能執行**一個檔案**,不過我們可以通過 require() 方法載入其他 js 檔案,js 檔案又載入其他 js 檔案,如此迴圈,最終就形成了一個大大的模組。請看示例: ```javascript // index.js 內容 let m = require('./a') // {1} console.log(m.sum(1,2)) // a.js 內容 let sum = (v1, v2) => v1 + v2 module.exports.sum = sum // 執行 $ node index 3 ``` index.js 中載入了 a.js。相當於運行了 2 個檔案。 如果將 `require('./a')` 改成 `require('a')`(行{1}),執行則會報錯:Error: Cannot find module 'a'。因為 `require('a')` 會直接去核心模組和第三方模組中找,結果又沒有找到對應的 a 模組。只有傳入 require(id) 方法中的 id 以 **'/'、'./'或'../'開頭**,才會從自定義模組中找。 通常 '/' 很少會用到。請看示例: ```javascript // D:\1.js console.log('hello') // D:\實驗樓\node-study\index.js require('/1') $ node index hello ``` 執行 `require('/1')` 會到檔案的根目錄(D 盤)中找 1.js。如果別人用你的專案,還得在根目錄下也存一份同樣的 1.js 檔案嗎?這是很困難的。 到現在,我們已經知道 require() 方法能匯入模組,也能執行模組。其實它還有一個**重要特性**:優先從快取中載入,重複匯入也只會執行一次。 ```javascript // 1.js var a = 1 let two = require('./2') let twoCopy = require('./2') console.log(a) console.log(`two a = ${two.a}`) console.log(`twoCopy a = ${twoCopy.a}`) // 2.js var a = 2 console.log(`我是 2.js`) // {1} exports.a = a; // 執行 $ node 1 我是 2.js 1 two a = 2 twoCopy a = 2 ``` 在 1.js 中匯入了兩次 2.js,但只輸出了一次 `我是 2.js`(行{1})。 請接著看: ```javascript // index.js require('./a') require('./b') // a.js require('./b') // b.js console.log('hello') $ node index hello ``` 模組 b.js 同樣被匯入了兩次,但也只執行了一次。 ### 模組的匯出 每個檔案都有一個 **module** 的變數。就像 require() 方法,無需載入就可以直接使用。請看示例: ```javascript // index.js console.log(typeof require) console.log(module) // 執行 $ node index function Module { id: '.', ... exports: {}, // {1} ... } ``` 我們看到 module 裡面有一個 **exports** 的物件(行{1})。 如果我們的模組需要對外提供介面,就可以使用 **module.exports**。請看示例: ```javascript // index.js let m = require('./1') console.log(`name = ${m.name} age = ${m.age}`) // 1.js let name = 'ph' let age = 'age' module.exports.name = name // {1} $ node index name = ph age = undefined ``` 模組 1.js 只匯出了 name 屬性,所以 index.js 只能讀取到 name ,而讀不到 age。 module.exports 還提供了一個**快捷方式**:直接使用 exports。例如將 `module.exports.name = name`(行{1}) 改成 `exports.name = name` 效果也是一樣的。 *注*:由於 exports 是 module.exports 的引用,就像任何變數一樣,如果將新值分配給 exports,則它不再繫結到 module.exports。請看示例: ```javascript // index.js let m = require('./1') console.log(m) // 1.js module.exports = 'ph' // {1} $ node index ph ``` 如果將 `module.exports = 'ph'`(行{1}) 換成 `exports = 'ph'`,輸出結果是:`{}`,說明匯出失敗。 ### 模組的作用域 node 中沒有全域性作用域,只有**模組作用域**。內部訪問不了外部,外部訪問不了內部。請看示例: ```javascript // 1.js var a = 1 var a = 2 console.log(a) ``` 執行 `$ node 1` 輸出 2。稍微改一下: ```javascript // 1.js var a = 1 require('./2') console.log(a) // 2.js var a = 2 ``` 再次執行 `$ node 1`,這次輸出的是 1。說明 2.js 中的 變數 `a` 沒能影響到 1.js 中的變數 `a`。繼續: ```javascript // 1.js require('./2') console.log(a) // 2.js var a = 1 ``` 再次執行 `$ node 1`,輸出報錯資訊 `ReferenceError: a is not defined`,說明 1.js 不能訪問 2.js 中的變數。這就是外部訪問不了內部。 >其他章節請看: > >[前端學習 node 快速入門 系列][1] [1]: https://www.cnblogs.com/pengjiali/p/14494587.html '前端學習 node 快速入門 系列' [初步認識 node]: https://www.cnblogs.com/pengjiali/p/14496123.html '初步認識 node' [npm]: https://www.cnblogs.com/pengjiali/p/14509928.ht