最近在研究 js 中 module 的用法,發現在使用 ES Module 的方式 import module 時,有時候會需要 extension name,但我在工作上 import 的時候卻從來沒加過 extension name,好奇什麼時候會需要加 extension name,於是有了這篇文章的誕生。
通常會分為以下三種情境
Table of Contents
1. 使用 Webpack 打包
如果專案是使用 webpack 打包的話,可能就會不需要在 import 的時候加上 extension name:
import Button from 'components/Button'
原因如 webpack 的文件說明:
- If the path has a file extension, then the file is bundled straightaway.
- Otherwise, the file extension is resolved using the
[resolve.extensions](<https://webpack.js.org/configuration/resolve/#resolveextensions>)
option, which tells the resolver which extensions are acceptable for resolution e.g..js
,.jsx
.
只要有在 webpack 的 config 中的 resolve.extensions 有設定要 resolve 的檔案類型,那麼在 import 這些檔案類型時,就不需要加上 extension name,webpack 的 config example:
// webpack.config.js
module.exports = {
resolve: {
extensions: ['.js', '.jsx']
}
}
這也是為什麼我在工作上使用 react import 其他的 js 檔時都不需要加上 extension name。
2. 在 Node.js 或是 Browser 中直接使用 ES Module
這裡的直接使用指的是不使用任何工具來打包(ex: webpack, gulp 等等),也就是假設 local 有安裝 Node.js 的情況下,可以直接開一個專案來測試 import module:
2.1 npm init
根據 Node.js 的文件,在沒有特別設定 module type 的情況下,會將檔案視為使用 CommonJS module,為了使用 ES Module,會需要將 package.json 的 type 設定為 module:
{
"name": "js-module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"author": "",
"license": "ISC"
}
2.2 export module
新增一個檔案來 export 某個變數
// car.js
export const brand = 'skoda'
2.3 import module
在另一個檔案 import 剛剛宣告的 module
// index.js
import { brand } from './car'
console.log(brand)
2.4 執行 index.js
直接執行這個檔案的話,會發現 Node.js 會噴錯:找不到這個 module
$ node index.js
node:internal/process/esm_loader:94
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/jimmy/Desktop/js-module/car' imported from /Users/jimmy/Desktop/js-module/index.js
需要在 import module 的 path 加上 extension name Node.js 才能找得到這個 module
// index.js
import { brand } from './car.js'
console.log(brand)
再一次執行 index.js
$ node index.js
skoda
3. 在 Node.js 中使用 CommonJS
要在 Node.js 中使用 CommonJS 的方式來處理 module 的話,可以將 package.json 的 type 改為 commonjs:
{
"name": "js-module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "commonjs",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"author": "",
"license": "ISC"
}
3.1 path name 沒有 extension name
如果 require 的 path 中沒有特別標注 extension name 的話, Node.js 會根據以下順序來找對應的檔案:
- .js
- .json
- .node
也就是說,如果要 require 的 module 是 js 檔的話,可以省略 .js
Node.js 也可以找得到對應的檔案:
// car.js
const brand = 'skoda'
module.exports = brand
// index.js
const brand = require('./car')
console.log(brand)
$ node index.js
skoda
3.2 path name 有 extension name
有 extension name 的話,Node.js 就會直接找對應的檔案,所以當然也可以 work:
// index.js
const brand = require('./car.js')
console.log(brand)
$ node index.js
skoda
4. 結論
- 使用 webpack: 有在 config 的 resolve.extensions 裡設定要 resolve 的檔案種類,在 import 這些檔案種類時,就可以不需要加上 extension name
- 不 compile,直接使用 ES Module:需要加上 extension name
- 在 Node.js 使用 CommonJS:不加 extension name 的話,會按照 .js, .json, .node 的順序來解析檔案
5. 參考資料
When do we need to add file extension when importing JavaScript modules?
Modules: CommonJS modules | Node.js v21.7.3 Documentation
Module Resolution | webpack