JS中import/export關鍵詞的使用解析
在JavaScript中,import/export是ES6(ES2015)引入的模塊化核心語法,核心作用是實現代碼的模塊化拆分與複用——export用於將模塊內的變量、函數、類等暴露給外部,import用於引入其他模塊暴露的內容。這一語法替代了CommonJS(require/module.exports)和AMD等模塊化方案,成為現代JS(瀏覽器/Node.js)模塊化開發的標準,讓代碼結構更清晰、依賴關係更明確。
一、模塊化的核心概念
- 模塊:一個獨立的JS文件就是一個模塊,模塊內的變量、函數默認是私有的,僅能在模塊內部訪問;
- 導出(export):將模塊內的私有成員暴露出去,供其他模塊引入;
- 導入(import):引入其他模塊暴露的成員,在當前模塊中使用;
- 模塊作用域:模塊內的代碼不會污染全局作用域,不同模塊的同名變量互不衝突。
二、export的基礎用法
export有兩種核心形式:命名導出(Named Export)和默認導出(Default Export),可根據場景靈活選擇。
1. 命名導出(多個/精準導入)
命名導出允許一個模塊暴露多個成員,導入時需使用與導出完全一致的名稱,適合暴露多個相關變量/函數。
// 模塊文件:utils.js
// 方式1:聲明時直接導出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export class Calculator {
multiply(a, b) {
return a * b;
}
}
// 方式2:先聲明,後統一導出(推薦,便於管理)
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
export { subtract, divide };
// 方式3:重命名導出(解決命名衝突)
export { divide as safeDivide }; // 導出divide,外部用safeDivide接收
2. 默認導出(單個/簡化導入)
一個模塊只能有一個默認導出,導入時可自定義名稱,無需與導出名稱一致,適合暴露模塊的核心功能(如單個類、核心函數)。
// 模塊文件:axios.js(模擬)
// 方式1:聲明時直接默認導出
export default function request(url) {
console.log(`請求URL:${url}`);
}
// 方式2:先聲明,後默認導出
class Request {
get(url) { /* 邏輯 */ }
}
export default Request; // 覆蓋上面的默認導出(一個模塊僅一個默認導出)
3. 混合導出(命名+默認)
一個模塊可同時包含命名導出和默認導出,兼顧“核心功能”和“輔助功能”的暴露。
// 模塊文件:userService.js
// 默認導出:核心類
export default class UserService {
getUserId() { return 1001; }
}
// 命名導出:輔助常量/函數
export const USER_STATUS = {
ACTIVE: 1,
INACTIVE: 0
};
export function formatUserName(name) {
return name.trim();
}
三、import的基礎用法
import需與export對應,支持命名導入、默認導入、整體導入等多種形式,且必須寫在模塊的頂層作用域(不能在函數/條件語句內,ES2022後支持動態導入)。
1. 命名導入(匹配命名導出)
導入命名導出的成員,名稱需與導出一致,可通過as重命名避免衝突。
// 導入utils.js的命名導出
import { PI, add, subtract, safeDivide } from './utils.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
console.log(safeDivide(6, 2)); // 3(對應原divide函數)
// 重命名導入(解決當前模塊命名衝突)
import { add as sum } from './utils.js';
console.log(sum(4, 5)); // 9
2. 默認導入(匹配默認導出)
導入默認導出的成員,可自定義名稱,無需與導出名稱一致。
// 導入axios.js的默認導出(自定義名稱)
import request from './axios.js';
request('https://api.example.com'); // 輸出:請求URL:https://api.example.com
// 導入userService.js的默認導出(核心類)
import UserApi from './userService.js';
const userApi = new UserApi();
console.log(userApi.getUserId()); // 1001
3. 混合導入(命名+默認)
同時導入模塊的默認導出和命名導出,默認導出在前,命名導出包裹在{}中。
// 導入userService.js的默認+命名導出
import UserApi, { USER_STATUS, formatUserName } from './userService.js';
console.log(USER_STATUS.ACTIVE); // 1
console.log(formatUserName(' 張三 ')); // 張三
4. 整體導入(導入模塊所有導出)
用* as 別名導入模塊的所有導出成員,統一掛載到一個對象上,適合批量導入。
// 導入utils.js的所有導出
import * as utils from './utils.js';
console.log(utils.PI); // 3.14159
console.log(utils.add(1, 2)); // 3
console.log(utils.safeDivide(8, 2)); // 4
// 導入userService.js的所有導出(默認導出掛載在default屬性)
import * as userModule from './userService.js';
const userApi = new userModule.default(); // 默認導出
console.log(userModule.USER_STATUS); // 命名導出
5. 僅執行模塊(無導入成員)
若模塊僅需執行初始化邏輯(如全局配置),可直接導入模塊,無需接收成員。
// 導入config.js,僅執行其內部代碼(如初始化全局配置)
import './config.js';
四、動態導入(import()函數)
ES2022引入的import()函數突破了“import必須在頂層”的限制,支持異步動態導入模塊,返回Promise對象,適合按需加載(如路由懶加載、條件加載)。
1. 基礎動態導入
// 異步加載utils.js
async function loadUtils() {
try {
const utils = await import('./utils.js');
console.log(utils.add(3, 4)); // 7
} catch (error) {
console.error("模塊加載失敗:", error);
}
}
// 點擊按鈕時加載模塊(按需加載)
document.getElementById('loadBtn').addEventListener('click', loadUtils);
2. 動態導入默認導出
動態導入的默認導出需通過.default訪問。
async function loadRequest() {
const module = await import('./axios.js');
const request = module.default; // 獲取默認導出
request('https://api.example.com');
}
五、import/export的核心應用場景
1. 前端項目模塊化拆分
在React/Vue等前端框架中,用import/export拆分組件、工具函數、接口服務,實現代碼解耦。
// 組件文件:Button.js
export default function Button(props) {
return `<button>${props.text}</button>`;
}
// 頁面文件:Index.js
import Button from './Button.js';
import { formatTime } from './utils/date.js';
import { fetchData } from './api/home.js';
// 渲染頁面
function render() {
const time = formatTime(new Date());
const btn = Button({ text: '點擊請求' });
document.body.innerHTML = `
<div>${time}</div>
${btn}
`;
}
render();
2. Node.js模塊化開發
Node.js從v14.13.0開始默認支持ES模塊(需將文件後綴改為.mjs,或在package.json中設置"type": "module")。
// package.json
{
"type": "module" // 聲明當前項目使用ES模塊
}
// 工具模塊:math.mjs
export const sum = (a, b) => a + b;
// 主模塊:index.mjs
import { sum } from './math.mjs';
console.log(sum(10, 20)); // 30
3. 第三方庫導入
現代前端項目中,導入npm第三方庫本質也是基於import/export(打包工具如Webpack/Rollup會處理兼容)。
// 導入完整庫(默認導出)
import React from 'react';
import Vue from 'vue';
// 導入命名導出(精準導入,減小打包體積)
import { useState, useEffect } from 'react';
import { createApp } from 'vue';
4. 模塊重導出(中轉導出)
在index.js中統一中轉導出子模塊的成員,形成“模塊入口”,簡化導入路徑(別名導出)。
// 子模塊:utils/date.js
export function formatDate() { /* 邏輯 */ }
// 子模塊:utils/number.js
export function formatNumber() { /* 邏輯 */ }
// 入口模塊:utils/index.js(中轉導出)
export { formatDate } from './date.js';
export { formatNumber } from './number.js';
// 重導出並改名
export { formatNumber as formatNum } from './number.js';
// 其他模塊導入(僅需指向入口)
import { formatDate, formatNum } from './utils/index.js';
六、使用注意事項
1. 模塊路徑規則
- 相對路徑:必須以
./或../開頭(如./utils.js、../api/user.js); - 絕對路徑:瀏覽器中指向完整URL(如
https://cdn.example.com/utils.js),Node.js中指向系統路徑; - 第三方庫:直接寫庫名(如
react、axios),由打包工具/Node.js解析。
2. 避免循環導入
模塊A導入模塊B,模塊B又導入模塊A,會導致“循環依賴”,可能出現變量未初始化的問題,需通過重構代碼(如提取公共模塊)避免。
// 錯誤示例:循環導入
// a.js
import { bFunc } from './b.js';
export const aFunc = () => bFunc();
// b.js
import { aFunc } from './a.js';
export const bFunc = () => aFunc(); // 執行時aFunc可能為undefined
3. 瀏覽器環境的模塊加載
瀏覽器中使用ES模塊,需給<script>標籤添加type="module"屬性,否則會報錯。
<!-- 正確:加載ES模塊 -->
<script type="module" src="./index.js"></script>
<!-- 錯誤:未聲明module,無法識別import/export -->
<script src="./index.js"></script>
4. 導出/導入的不可變性
import導入的是模塊導出成員的引用,而非副本:
- 基本類型:導入後無法修改(只讀);
- 引用類型(對象/數組):可修改其內部屬性(不推薦,破壞模塊封裝)。
// module.js
export let num = 10;
export const obj = { name: "張三" };
// main.js
import { num, obj } from './module.js';
num = 20; // 報錯:Uncaught TypeError: Assignment to constant variable.
obj.name = "李四"; // 可修改(不推薦)
console.log(obj.name); // 李四
總結
import/export是ES6模塊化標準語法,核心實現代碼拆分與複用,替代CommonJS/AMD,支持瀏覽器和Node.js;export分命名導出(多個,精準導入)和默認導出(單個,簡化導入),可混合使用;import需與導出匹配,支持命名導入、默認導入、整體導入,import()函數實現異步動態導入;- 核心場景:前端組件拆分、Node.js模塊化、第三方庫導入、模塊中轉導出,是現代JS開發的基礎;
- 注意事項:避免循環導入、瀏覽器需聲明
type="module"、導入的基本類型只讀。