跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScriptNode.js大前端

import/export:前端模块化实战 | JS 基础语法与数据操作

综述由AI生成介绍前端 ES Modules 中 import 和 export 的基础用法及最佳实践。通过对比非模块化代码的痛点,阐述了模块化在隔离作用域、明确依赖关系和提升可维护性方面的优势。提供了基于 Vite + Vue 的最小可运行项目示例,包含 utils、api、composables 和 constants 的标准目录结构。最后总结了常见坑点,如循环依赖、魔法数字等,旨在帮助开发者建立清晰的前端工程化思维。

星落发布于 2026/4/5更新于 2026/5/2126 浏览
import/export:前端模块化实战 | JS 基础语法与数据操作

一、先体验一下:没有模块化有多难受

1.1 一个真实的场景

假设你要做一个「用户列表」页面,需要:

  • 格式化日期
  • 调用用户列表接口
  • 显示加载中状态

如果全写在一个文件里,大概是这样的:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
</head>
<body>
    <div id="app"></div>
    <script src="./app.js"></script>
</body>
</html>
// app.js - 所有逻辑全塞在一起,有 100 多行
const API_BASE = '/api';
const list = [];
let loading = false;

function formatDate(date) {
    return new Date(date).toLocaleDateString('zh-CN');
}

 () {
    loading = ;
    ( + )
        .( res.())
        .( {
            
            list = data;
            loading = ;
        });
}


function
getUserList
true
fetch
API_BASE
'/user/list'
then
res =>
json
then
data =>
// 注意:这里 list 是 const,会报错!变量多了容易搞混
false
// 假设还有 10 个其他函数...
// 三个月后你回来改 bug,根本找不到 formatDate 在哪...

问题很快就出来了:

  1. 变量、函数全混在一起,改一处容易影响别处
  2. formatDate 如果别的页面也要用,只能复制粘贴
  3. 文件越来越长,找个函数要翻很久

模块化要解决的,就是这三个问题。

1.2 模块化能做到的三件事
  • 隔离:每个文件有自己的作用域,不互相污染
  • 依赖清晰:用 import 明确「谁用了谁」
  • 可维护:按职责拆文件,找代码、改代码都更轻松

后面我们就用 ES Modules(import / export)来做这件事。


二、import / export 基础

2.1 先搞懂 export:导出

两种常见用法:

方式一:命名导出(一个文件可以导出多个)

// utils/format.js
export function formatDate(date) {
    return new Date(date).toLocaleDateString('zh-CN');
}

export function formatMoney(num) {
    return `¥${num.toFixed(2)}`;
}

方式二:默认导出(一个文件只能有一个 default)

// config.js
export default {
    apiBase: '/api',
    timeout: 5000,
};
  • 命名导出:可以导出很多个,import 时要用同名
  • 默认导出:只有一个,import 时可以随便起名字
2.2 再搞懂 import:引入
// 引入命名导出:名字必须和 export 的一致
import { formatDate, formatMoney } from './utils/format.js';

// 引入默认导出:名字可以随便起
import config from './config.js';
console.log(config.apiBase); // '/api'

// 把整个模块当作对象引入
import * as formatUtils from './utils/format.js';
formatUtils.formatDate(new Date());

记住一个区别:

写法含义
import { foo }引入命名导出,{} 是语法,不是解构
import foo引入默认导出

三、完整的可运行示例

下面是一个最小可运行项目,涵盖:utils、api、constants、composables,可以直接照抄跑起来。

3.1 项目结构
my-project/
├── index.html
├── package.json
├── vite.config.js
└── src/
    ├── main.js # 入口
    ├── constants/
    │   └── index.js # 常量
    ├── utils/
    │   ├── format.js # 格式化
    │   └── index.js # 统一导出
    ├── api/
    │   ├── request.js # 请求封装
    │   ├── user.js # 用户接口
    │   └── index.js # 统一导出
    ├── composables/ # Vue 组合式函数(可复用逻辑)
    │   └── useUserList.js
    └── App.vue # 根组件
3.2 完整代码(可直接复制)

1. package.json

{
  "name": "module-demo",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite"
  },
  "dependencies": {
    "vue": "^3.4.0",
    "axios": "^1.6.0"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "@vitejs/plugin-vue": "^5.0.0"
  }
}

2. index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户列表 - 模块化示例</title>
</head>
<body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
</body>
</html>

3. src/constants/index.js

// 常量:放那些「写死不变」的值,以后改起来只改这一处
export const API_BASE = '/api';
export const TIMEOUT = 10000;

// 订单状态:用常量代替魔法数字 0、1、2、3
export const ORDER_STATUS = {
    PENDING: 0,
    PAID: 1,
    SHIPPED: 2,
    COMPLETED: 3,
};

export const ORDER_STATUS_TEXT = {
    0: '待支付',
    1: '已支付',
    2: '已发货',
    3: '已完成',
};

4. src/utils/format.js

// 纯函数:同样的输入,一定得到同样的输出,不依赖外部状态
export function formatDate(date, options = {}) {
    if (!date) return '-';
    return new Date(date).toLocaleDateString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        ...options,
    });
}

export function formatMoney(num) {
    if (num == null || isNaN(num)) return '¥0.00';
    return `¥${Number(num).toFixed(2)}`;
}

export function formatPhone(phone) {
    if (!phone || phone.length !== 11) return phone;
    return `${phone.slice(0, 3)}****${phone.slice(7)}`;
}

5. src/utils/index.js

// 统一导出:别人只需要 import from '@/utils' 就能拿到所有工具
export { formatDate, formatMoney, formatPhone } from './format.js';

6. src/api/request.js

import axios from 'axios';
import { API_BASE, TIMEOUT } from '../constants/index.js';

// 创建一个配置好的 axios 实例,所有请求都走它
const request = axios.create({
    baseURL: API_BASE,
    timeout: TIMEOUT,
});

// 响应拦截器:统一处理错误和数据结构
request.interceptors.response.use(
    (res) => res.data, // 直接返回 data,调用方少写一层
    (err) => {
        if (err.response?.status === 401) {
            console.warn('未登录,请先登录');
            // 实际项目:跳转登录页
        }
        return Promise.reject(err);
    }
);

export default request;

7. src/api/user.js

import request from './request.js';

// 每个函数对应一个接口,参数和返回值一目了然
export function getUserList(params = {}) {
    return request.get('/user/list', { params });
}

export function getUserDetail(id) {
    return request.get(`/user/${id}`);
}

8. src/api/index.js

export * from './user.js';
// 以后有 order.js,再加一行:export * from './order.js';

9. src/composables/useUserList.js

import { ref, watch } from 'vue';
import { getUserList } from '../api/index.js';

// 把「拉列表 + loading + error」抽成组合式函数,任何组件都能复用
export function useUserList(params = {}) {
    const list = ref([]);
    const loading = ref(false);
    const error = ref(null);

    async function fetchList() {
        loading.value = true;
        error.value = null;
        try {
            const res = await getUserList(params);
            list.value = res.data || res;
        } catch (err) {
            error.value = err.message;
        } finally {
            loading.value = false;
        }
    }

    // 当 params 变化时重新请求
    watch(() => [params.page, params.pageSize], () => fetchList(), {
        immediate: true,
    });

    return { list, loading, error, fetchList };
}

10. src/App.vue

<template>
    <div v-if="loading">加载中...</div>
    <div v-else-if="error">出错了:{{ error }}</div>
    <div v-else style="padding: 20px">
        <h1>用户列表</h1>
        <ul>
            <li v-for="user in list" :key="user.id">
                {{ user.name }} - 注册时间:{{ formatDate(user.createdAt) }}
            </li>
        </ul>
    </div>
</template>

<script setup>
import { useUserList } from './composables/useUserList.js';
import { formatDate } from './utils/index.js';

const { list, loading, error } = useUserList({ page: 1, pageSize: 10 });
</script>

11. src/main.js

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

12. vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
    plugins: [vue()],
});
3.3 怎么运行
cd my-project
npm install
npm run dev

浏览器打开 http://localhost:5173 即可(接口需自行 mock 或接真实后端)。


四、这一套结构在解决什么问题

4.1 utils:通用纯函数
  • 职责:格式化、校验、防抖节流等和业务无关的函数
  • 原则:尽量纯函数,不依赖接口、路由、业务状态
  • 示例:formatDate、formatMoney、isValidPhone
4.2 api:请求层
  • 职责:封装 axios/fetch,按业务拆接口文件
  • 原则:只负责发请求、返回数据,不写业务判断
  • 示例:getUserList、getUserDetail
4.3 composables:可复用逻辑
  • 职责:把「请求 + loading + error」等可复用逻辑抽成组合式函数
  • 原则:只在多处复用时抽,不要过度抽象
  • 示例:useUserList、useDebounce
4.4 constants:常量与配置
  • 职责:状态码、业务码、环境配置等
  • 原则:只放常量,不放逻辑
  • 示例:ORDER_STATUS、API_BASE

五、常见坑

坑建议
utils 里写业务逻辑工具函数只做通用处理,不依赖具体业务
api 层写业务判断api 只管请求和错误,业务逻辑在组件/composable 里
魔法数字状态码、业务码用常量,如 ORDER_STATUS.PENDING
watch 依赖写错watch 的监听源要写对,否则会多请求或漏更新
ref 忘记 .value在 script 里访问 ref 要加 .value,模板中自动解包
循环依赖保持单向依赖:constants → api → composables → 组件

六、小结

模块化的目的就三点:职责清晰、依赖明确、好维护。

先按 utils、api、composables、constants 这几类把代码分好,再按项目规模慢慢细化,不必一上来就设计得很复杂。

目录

  1. 一、先体验一下:没有模块化有多难受
  2. 1.1 一个真实的场景
  3. 1.2 模块化能做到的三件事
  4. 二、import / export 基础
  5. 2.1 先搞懂 export:导出
  6. 2.2 再搞懂 import:引入
  7. 三、完整的可运行示例
  8. 3.1 项目结构
  9. 3.2 完整代码(可直接复制)
  10. 3.3 怎么运行
  11. 四、这一套结构在解决什么问题
  12. 4.1 utils:通用纯函数
  13. 4.2 api:请求层
  14. 4.3 composables:可复用逻辑
  15. 4.4 constants:常量与配置
  16. 五、常见坑
  17. 六、小结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 Flask 和 Python 的智慧养老服务系统设计与实现
  • 面向数据工程的 SQL 与 Python 代码自动生成:6 款大模型深度评测
  • YOLOv8 草莓成熟度检测数据集与系统实战
  • 小白转行 Python 数据分析的职业规划与学习路径
  • 微信小程序跳转外部链接:WebView 与复制链接方案
  • 钉钉 Webhook 集成与 @ 用户通知实战指南
  • Python 虚拟环境搭建与 PyCharm 配置实战
  • 网络安全行业前景深度解析与入行路径建议
  • LLaMA-Factory 微调 Qwen3-VL 详细流程
  • 微信小程序 WebView 与内嵌网页双向通信指南
  • C++11/14/17 特性整理与 Effective Modern C++ 条款实践(一)
  • Spring Cloud Nacos 服务注册与配置中心实战
  • Python 深度学习环境搭建:Anaconda、PyTorch GPU 版与 PyCharm 配置
  • Llama 3.1 本地部署实战指南:基于 Ollama 的快速上手
  • Rust 异步编程实战:构建高性能 WebSocket 服务
  • JavaScript 变量声明:var、let、const 核心区别与实战指南
  • 前端核心面试题深度解析:从原理到实战
  • 火影忍者主题网页设计与实现
  • Neo4j 5.26 版本下载安装与配置指南
  • Direct3D 融合技术与透明效果实现

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online