news 2026/10/2 15:42:22

Vue 模块化核心:搞懂 import/export 与 ES Module 实战避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue 模块化核心:搞懂 import/export 与 ES Module 实战避坑

Vue 项目里,我也数不清自己写过多少次import和export了。从最早用 Vue CLI 搭骨架,到后来天天和setup语法糖打交道,这两个关键字几乎是每天都在敲。但就是这对看起来最基本的语法,我见过太多项目因为用错导致编译报错、循环依赖、变量拿不到值,甚至整个模块解析失败的案例。说实话,Vue 的导入导出本身不复杂,真正容易踩坑的是背后的 ES Module 规范、Vue 单文件组件的编译机制,以及各种工具链对模块解析的差异。这篇就把我在实际项目里积累的关于import、export、export default的经验一次性讲清楚。

这篇内容适合谁?刚接触 Vue 想搞清楚模块化怎么玩的初学者,写了一阵子但被各种报错折磨过的前端开发,还有面试前想系统梳理 ES Module 知识点的朋友。我会从最基本的语法讲起,再深入到 Vue 组件、路由懒加载、公共模块封装这些实战场景,最后把我实际项目中遇到过的几个高频报错和排查思路都列出来,争取让你看完就能少踩几个坑。

1. 先搞明白为什么 Vue 离不开 import 和 export

1.1 模块化不是 Vue 发明的,但 Vue 把模块化用到了极致

很多人一开始接触 Vue 是从main.js开始的,打开文件就是一堆import,然后Vue.createApp,然后App.vue里又是一堆import。但真正问你为什么要这么写,很多人说不出个所以然。

本质上,import和export是 ES Module(ESM)的语法,这是 JavaScript 官方标准里的模块化方案。在 ES Module 出现之前,前端代码组织方式很原始,要么是多个<script>标签按顺序加载,要么用立即执行函数表达式(IIFE)做简单的隔离,再后来有 CommonJS(Node.js 里的require和module.exports)和 AMD(比如 RequireJS)。

Vue 项目选择 ES Module 的原因很直接:语法是语言标准,浏览器原生支持越来越完善,而且配合打包工具(Vite、Webpack)可以做静态代码分析、Tree Shaking(摇树优化,去掉没用的代码)、代码分割这些高级特性。你写的每一个.vue文件,本质上就是一个模块,这个模块把自己的模板、脚本、样式封装起来,通过export暴露给别人,通过import引入别人暴露出来的东西。

打个比方,模块化就像乐高积木。每一块积木(模块)都是独立的,有自己的功能和接口,通过标准化的凸起和凹槽(export和import)拼接起来。你不必关心一块积木内部是什么材料做的,只要知道接口能对上就能用。

1.2 ES Module 的静态分析特性,决定了 Vue 项目里怎么组织代码

ES Module 最核心的一个特性是静态结构,也就是说import和export必须写在模块的顶层,不能写在if判断里,不能写在函数里。这一点和 CommonJS 的require完全不同,require是运行时加载,可以放在任意位置。

为什么 ES Module 要设计成静态的?因为只有静态结构才能让打包工具在编译阶段就分析出模块之间的依赖关系。Vite 之所以启动那么快,就是因为它基于 ESM,浏览器直接加载模块,开发服务器只需要按需转发请求,不需要像 Webpack 那样先把所有模块打包一遍再启动。而 Tree Shaking 能起作用,也是因为export是静态的,打包器能明确知道哪些导出的变量被用到了,哪些是死代码可以删掉。

这个特性直接影响了 Vue 项目的写法。比如动态导入(import()函数形式)就是为了打破静态限制而存在的,可以用在路由懒加载。而如果你写if (condition) { import xxx }这种代码,打包工具直接给你报错。理解了这一点,很多报错信息就好懂了。

2. 导出语法全面拆解:export 和 export default 到底怎么选

2.1 export 命名导出的多种写法与易错点

先看代码。export有两种基本用法,一种是直接加在声明语句前面:

// utils/math.js export const add = (a, b) => a + b export function subtract(a, b) { return a - b }

另一种是先声明再导出:

// utils/math.js const PI = 3.14159 const multiply = (a, b) => a * b export { PI, multiply }

这两种写法是等价的,都是命名导出。所谓命名导出,意思是导入方必须知道导出的具体名字才能引入。比如import { add } from './utils/math',花括号里的名字必须和导出的名字完全一致,否则就报错。

这里有几个容易混淆的细节。第一,你可以在一个模块里写多个export,没有数量限制。第二,export语句导出的不是对象字面量,你不能写export { name: '张三' }这种形式,花括号里只能是声明过的变量名列表。第三,可以用as关键字给导出的变量重命名:

// utils/math.js const add = (a, b) => a + b const subtract = (a, b) => a - b export { add as sum, subtract as difference }

导入端就要用重命名后的名字:

import { sum, difference } from './utils/math'

我用这种重命名最多的场景是两个不同模块里有同名工具函数,导出时统一改名,导入端就不用写别名了,代码干净不少。

2.2 export default 的本质与唯一性原则

export default是另一个导出方式,每个模块只能有一个默认导出。它的设计初衷是为模块提供一个“默认入口”,让导入方不用关心内部到底暴露了哪些命名导出。

// components/MyButton.vue export default { name: 'MyButton', props: { label: { type: String, default: '按钮' } }, mounted() { console.log('MyButton 挂载完成') } }

在 Vue 2 和 Vue 3 的选项式 API 中,.vue文件的<script>部分最常见的写法就是export default { ... },把一个组件选项对象作为默认导出暴露出去。而在 Vue 3 的组合式 API 中,新写法变成了<script setup>,这个语法本质上是编译器的语法糖,背后依然会生成export default的形式,只不过你不需要手动写了。

export default有几个有意思的特性。它形式上可以跟一个匿名的值,所以下面这些写法都是合法的:

export default function () { ... } export default class { ... } export default { ... }

导入端用import xxx from '...'接收,这里的xxx可以是任意合法的变量名,不需要和导出端有任何对应关系。这就是默认导入和命名导入最大的区别,命名导入必须精确匹配名字,默认导入是随便起名。

很多人困惑的是export default和export能不能同时存在。答案是可以。一个模块可以同时有多个命名导出和一个默认导出。比如一个工具文件里既导出了一个自定义指令(默认导出),又导出了几个辅助函数(命名导出),这种情况完全合法。但我需要提醒的是,在实际 Vue 项目里,一个文件尽量不要混用两种导出方式,除非你有明确的模块设计意图。混用会显著增加使用方的认知负担,别人引入时要先搞清楚哪些是默认的哪些是命名的。

2.3 Vue 组件开发中的导出实践:选项式、组合式与 script setup

在真实的 Vue 项目里,导出的写法经历了几个阶段的变化。

Vue 2 时代和 Vue 3 的选项式 API,组件文件的<script>通常是:

export default { name: 'UserCard', components: { Avatar, UserInfo }, props: { userId: { type: Number, required: true } }, setup(props) { // 组合式逻辑 return { ... } } }

Vue 3 推出<script setup>之后,写法简化了很多:

<script setup> import { ref, computed } from 'vue' import Avatar from './Avatar.vue' import { formatDate } from '@/utils/date' const props = defineProps({ userId: { type: Number, required: true } }) const userInfo = ref(null) const createdDate = computed(() => formatDate(userInfo.value?.createdAt)) </script>

注意<script setup>语法里,导出的概念被弱化了——组件自动成为默认导出的形式,你引入的其他组件变量直接就能在模板里用,不需要手动注册到components选项里。子组件通过defineProps、defineEmits这些宏来声明对外接口,而不是通过传统的props选项。

这种写法对模块导入导出有了新的要求。在<script setup>中,引入的变量可以在模板中直接使用,但如果你在<script>(非 setup 的普通 script)和<script setup>同时存在时,要注意变量作用域。普通<script>中可以写命名导出(比如给组件挂静态方法),但<script setup>中的内容不能包含export关键字,因为编译器已经默认导出了组件的实例。

我实际项目中遇到过一个坑,在<script setup>里写了一句export const someConstant = 1,结果编译器直接报错,提示script setup不能包含 ESM 的导出。后来我了解到正确做法是把这个常量放到单独的普通<script>块里导出,或者干脆新建一个.js文件来放公共常量,组件里import进来用。

3. 导入语法深度解析:花括号、默认值、整体导入与路径细节

3.1 命名导入、默认导入和混合导入的完整说明

导入是导出的逆操作,但有一些非常容易混淆的点。

默认导入的语法:

import App from './App.vue'

命名导入:

import { ref, computed, watch } from 'vue'

混合导入,同时引入默认导出和命名导出:

import React, { useState, useEffect } from 'react'

在 Vue 项目里,最典型的混合导入发生在某些 Vue 插件或工具库中,既提供了主功能(默认导出),又提供了一些辅助类型或函数(命名导出)。

需要注意的一点是,import { ref } from 'vue'这种写法看起来像解构赋值,但和对象解构完全不是一回事。解构是运行时从对象里取属性,而命名导入是在编译期建立模块之间的静态引用关系。这意味着你不能写import { someVariable } from './module'当someVariable不存在时还期望不报错——打包工具会在编译阶段就告诉你这个导出不存在。这一点和实际项目中的报错直接相关,很多人在import时报错does not provide an export named ...,就是因为模块端没有对应的命名导出。

3.2 整体导入的适用场景和潜在风险

整体导入的语法是import * as xxx from '...',会把模块的所有导出(包括默认导出)打包到一个命名空间对象里。

import * as Utils from './utils' console.log(Utils.add(1, 2)) console.log(Utils.default) // 默认导出要这样访问

整体导入在什么场景下有用?我一般在调试或者临时验证时用,快速看看一个模块到底暴露了哪些东西。另一个场景是写测试用例时,需要 mock 模块的多个导出。

但在正式业务代码里,整体导入要谨慎使用。原因是多方面的:第一,整体导入会让打包工具难以做 Tree Shaking,因为打包工具不知道你会用到命名空间对象里的哪一个属性,只能全部保留;第二,整体导入在代码语义上不够明确,别人看你代码时不知道你具体用了哪些功能。更规范的做法是显式地命名导入你需要的部分。

3.3 副作用导入和路径解析规则,以及最常用的别名 @

还有一种 import 写法,专门用来引入模块的副作用,而不引入任何绑定:

import './styles/main.css' import 'element-plus/dist/index.css'

这种写法在 Vue 项目里非常常见,用来引入全局样式、polyfill、初始化脚本等。你不需要使用模块导出的任何东西,只需要它的执行效果。需要注意的坑是,有些模块在副作用执行时会依赖 DOM 或其他浏览器 API,如果你在服务端渲染(SSR)环境中引入,可能会直接报错。所以做服务端渲染的项目,这些副作用导入通常要做条件判断或者放入客户端入口文件。

关于路径解析,Vue 项目里有两种相对路径和别名路径:

import MyComponent from './components/MyComponent.vue' // 相对路径 import MyComponent from '../../components/MyComponent.vue' // 相对路径,不好维护 import MyComponent from '@/components/MyComponent.vue' // 别名路径

@别名在 Vue CLI 和 Vite 项目中默认指向src目录,这是最常用的路径配置。用别名有两个核心好处:一是即使文件移动位置,只要相对src的路径不变,代码就不用改;二是避免写那种../../../../的多级相对路径,看着就晕头转向。

在 Vite 中配置别名是在vite.config.js里:

import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src') } } })

在 Vue CLI(Webpack)中是在vue.config.js里:

const path = require('path') module.exports = { configureWebpack: { resolve: { alias: { '@': path.resolve(__dirname, 'src') } } } }

配置好之后,你的所有 import 路径都用@/开头,团队协作时大家一眼就能看出模块位于 src 目录下。

3.4 静态导入之外:Vue 路由懒加载中的动态 import

前面提到 ES Module 的import是静态的,不能写在条件里。但有一种特殊的函数式写法——动态import(),它返回一个 Promise,可以在运行时按需加载模块。这在 Vue 路由懒加载里是最常用的:

// router/index.js import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'Home', component: () => import('@/views/Home.vue') }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ] })

这种写法的核心价值是代码分割。打包工具看到import()函数形式,会自动把对应的组件拆成一个独立的 chunk 文件,用户访问/about时才下载About.vue对应的 JS 文件,而不是首次访问首页时就把所有页面组件的代码全部下载下来。这直接决定了大型应用首屏加载的速度。

关于动态 import 我踩过一个坑:组件路径不能用纯变量拼接,至少要有固定的前缀。比如这样写是没问题的:

const viewPath = '../views/' component: () => import(`${viewPath}About.vue`)

因为模板字符串里../views/是固定的字面量,打包工具能静态分析出需要将views目录下的所有.vue文件作为可选的 chunk。但如果你写import(someVariable)且someVariable是运行时才确定的完整路径,打包工具就无法分析,要么报错要么把整个目录都打包进去。面试题里经常问的动态 import 原理,其实核心就是打包工具对静态路径和变量路径的处理差异。

4. 实战拆解:从工具函数到组件库的完整导入导出方案

4.1 场景一:公共工具函数模块怎么设计导出

先分享一个我当时在项目里封装的日期处理模块,展示比较合理的导出设计思路:

// src/utils/date.js export function formatDate(date, format = 'YYYY-MM-DD') { // 实际处理逻辑省略 return formattedString } export function getWeekDay(date = new Date()) { const days = ['日', '一', '二', '三', '四', '五', '六'] return `星期${days[date.getDay()]}` } const dateUtils = { formatDate, getWeekDay } export default dateUtils

这个模块的设计思路是:核心函数用命名导出,让使用者可以按需引入,配合 Tree Shaking 能去掉未使用部分;同时提供一个默认导出,把所有函数聚合到一个对象里,方便某些场景下整体引入。

但期望的调用方式也因此不一样:

// 方式一:单独引入,用哪个引哪个 import { formatDate } from '@/utils/date' formatDate(new Date(), 'YYYY/MM/DD') // 方式二:整体引入,全部挂在一个对象下 import dateUtils from '@/utils/date' dateUtils.formatDate(new Date())

这两种方式在团队协作里容易产生分歧。我的建议是,项目里统一一种风格。如果团队用 Vite 且重视打包体积,推荐都用命名导入。如果项目比较简单,整体导入也足够。关键是要定下来,写进项目的开发规范文档里,避免一会儿这样写一会儿那样写。

4.2 场景二:Vue 组件之间的导入导出怎么组织

在 Vue 组件间做导入导出,最常见的两种形式是:父组件引入子组件,和兄弟组件通过公共库共享逻辑。

先看父组件引入子组件:

<!-- components/Parent.vue --> <script setup> import ChildA from '@/components/ChildA.vue' import ChildB from '@/components/ChildB.vue' import { ref } from 'vue' const parentData = ref('来自父组件的数据') </script> <template> <div class="parent-wrapper"> <ChildA :message="parentData" /> <ChildB @child-event="handleEvent" /> </div> </template>

在<script setup>下引入的子组件,直接可以作为标签在模板中使用。这种导入导出的数据流方向是:父组件通过属性传给子组件(defineProps接收),子组件通过事件告诉父组件(defineEmits触发)。整个过程其实就是模块之间的接口协议。

再看一个稍微复杂的场景:多个组件共享公共逻辑。比如两个页面组件都需要做“金额格式化展示”,我通常会把这个逻辑抽成一个组合式函数:

// src/composables/useFormatPrice.js import { computed } from 'vue' export function useFormatPrice(price) { const formatted = computed(() => { if (price === null || price === undefined) return '--' return `¥ ${Number(price).toFixed(2)}` }) return { formatted } }

在任意组件里这样用:

<!-- components/PriceDisplay.vue --> <script setup> import { useFormatPrice } from '@/composables/useFormatPrice' const props = defineProps({ value: { type: Number, default: 0 } }) const { formatted } = useFormatPrice(props.value) </script> <template> <span class="price-display">{{ formatted }}</span> </template>

这种导出设计比直接写一个工具函数更有价值的地方在于,它把 Vue 的响应式逻辑也封装进去了,调用方拿到的formatted是计算属性,数据变化自动更新视图。

4.3 场景三:按需导入 UI 组件库,别把整个库都打包进去

Vue 项目里最常接触的导入导出场景之一就是 UI 组件库。Element Plus、Ant Design Vue、Naive UI 这些库,全量引入的方式非常简单:

// main.js import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(ElementPlus) app.mount('#app')

全量引入的好处是省心,但代价是会把所有组件都打包进你的产物文件里,几百 KB 的 JS 就出去了。对性能敏感的项目,更推荐按需导入:

// main.js import { createApp } from 'vue' import { ElButton, ElInput, ElDialog } from 'element-plus' import 'element-plus/theme-chalk/el-button.css' import 'element-plus/theme-chalk/el-input.css' import 'element-plus/theme-chalk/el-dialog.css' const app = createApp(App) app.use(ElButton) app.use(ElInput) app.use(ElDialog) app.mount('#app')

但手动按需每个组件都import再app.use,写起来很烦。实际项目中有人写了个本地插件,把用到的组件集中在一个文件里:

// src/plugins/elementPlus.js import { ElButton, ElInput, ElDialog, ElMessage } from 'element-plus' // 这里可以用 app.component 全局注册,也可以按需在页面里单独 import export { ElButton, ElInput, ElDialog, ElMessage }

然后在使用的地方按需引入:

<script setup> import { ElMessage } from '@/plugins/elementPlus' const handleSave = () => { ElMessage.success('保存成功') } </script>

这个模式的好处是把组件库的依赖收口在一个文件,换版本、加组件都只改一处。配合 Vite 也可以使用官方推荐的unplugin-vue-components插件实现全自动按需导入,原理是让你写<el-button>时自动解析并导入对应的组件模块,但插件内部做的工作本质上还是把import和export的链条串接起来。

4.4 场景四:图片等静态资源的导入方式

Vue 项目里引入图片,至少有三种方式,很多人会忽略路径解析和打包产出之间的区别。

第一种,直接在<script>里 import:

<script setup> import grenadeImg from '@/assets/grenade.png' </script> <template> <img :src="grenadeImg" alt="手雷图标" /> </template>

第二种,放在public目录下,直接用绝对路径:

<img src="/images/grenade.png" alt="手雷图标" />

第三种,在 CSS 里用相对路径引用背景图。

第一种模式下,图片会被打包工具处理成一个 URL 或 base64 字符串(取决于文件大小和配置),文件名会带上 hash。热搜词里有一条failed to resolve import "../assets/grenade (1024x128)[frames=8].png",这种报错就是路径写错了或者文件不存在。我在项目里用文件命名带空格和括号时也遇到过类似问题,import时没有对路径做正确的转义处理。后来我的建议是:资源文件的命名一律用小写字母、数字和下划线,不要用空格、括号和中文,能避免大量莫名其妙的路径解析报错。

5. 高频报错与避坑指南:你遇到的 90% 问题都在这

5.1 模块解析失败:import 和 export 出现在错误的位置

热搜词里有一条典型的报错:

Module parse failed: 'import' and 'export' may appear only with 'sourcetype'

这类报错通常不是因为import语法本身写错了,而是打包工具遇到了不认识的语法格式。最典型的情况是:你在一个被当成普通 JS 脚本解析的文件里写了 ES Module 语法,但项目配置(或某个依赖的配置)没有正确处理这个文件。

举例来说,如果你在 Webpack 项目里引用了 node_modules 里一个不支持 ESM 的老库,而这个库的代码里恰好有import/export关键词,Webpack 可能因为没配置transpileDependencies而直接报错。解决办法是找到报错的模块,通过配置将对应的依赖加入转译(Webpack)或optimizeDeps.exclude(Vite)列表。

另一种原因是你把<script setup>的代码粘贴到了普通的<script>标签中,或者反过来。Vue SFC 编译要求<script setup>中不能包含export default之类的导出语句,而普通<script>中你不该写<script setup>专用的宏调用。分清这两者很重要。

5.2 组件导入后不显示或报错的排查心法

组件导入后页面空白,这个问题的排查路径其实很有章法。我碰到过几种最常见的情况,分享一点排查经验:

  • 第一步,打开浏览器控制台,看有没有加载 JS 报错。最常见的错误是Failed to resolve import,这时要检查路径是否写对了,文件是否存在,特别是别名@是否配置成功。
  • 第二步,看 Vue 的 warning 信息。比如明明写了import MyComponent from './MyComponent.vue',但标签里写的是<my-component>,Vue 解析时会把驼峰形式自动转成短横线形式,两者是等价的,不会出问题。真正的问题往往是导入的文件默认导出是空的,或者export default和export混淆了。
  • 第三步,查看网络请求。如果组件对应的 JS 文件没有请求到,更可能是动态 import 的路径规则有问题,打包工具把路由组件拆成了 chunk,但路径拼接失败导致 404。
  • 第四步,如果组件能加载,但内部数据渲染不出来,问题往往不在导入导出,而在组件自身的 props、响应式数据或者生命周期逻辑里。

排查的时候建议打开 Vite 或 Webpack 的 source map,让报错能对应到源码位置,能省很多时间。

5.3 循环依赖:各自引用导致 undefined 的问题

循环依赖在前端项目里不算高频,但一旦遇到就非常隐蔽。看这个例子:

// a.js import { bFunc } from './b' export const aFunc = () => { console.log('aFunc 调用 bFunc', bFunc()) } // b.js import { aFunc } from './a' export const bFunc = () => { return 'bFunc 的结果' }

这个例子里 b 并不真正用到aFunc,但如果情况变成 b 在模块顶层就调用了aFunc,编译执行时可能拿到的是undefined。因为 ES Module 的依赖关系是静态声明的,a.js先加载时会去执行b.js,而b.js顶层代码想用aFunc,此时a.js还没初始化完,aFunc还没赋值,结果自然是 undefined。

解决办法是重构代码,把公共逻辑抽到第三个模块,消除循环依赖。如果实在无法避免,那就把循环依赖中的一方改成运行时才去取另一个模块的值(比如在函数内部import,当然函数内部的静态 import 不行,得用动态import()),不要在最外层立刻使用依赖模块的导出。

5.4 综合排查速查表

我整理了一份我平时排查导入导出问题的速查表,基本覆盖了绝大多数情况:

报错或现象可能原因排查与处理
Failed to resolve import路径拼写错误、文件不存在、别名未配置检查文件路径和文件名大小写,确认@别名配置文件
does not provide an export named 'xxx'导入的变量名和模块的命名导出不一致打开模块文件,确认导出名字,或改用默认导入
Unexpected token 'export'当前环境的 JS 引擎不支持 ESM,或文件被错误解析检查 type="module",检查构建配置的转译范围
组件引入后页面空白默认导出内容为空、组件内 JS 报错、路径错误查看浏览器控制台,逐层定位
[Vue warn] Failed to resolve component组件引入失败或未注册检查 import 路径,确认<script setup>引入后可直接使用
循环依赖导致导出值为 undefined模块之间存在相互引用且初始化顺序有问题抽取出公共模块,或改为动态 import
变量找不到is not defined导入的变量没有在模块作用域内使用在<script setup>里导入的变量可以直接用,普通<script>里要挂载到组件实例才可用于模板

这张表是我平时排查问题的主要工具。遇到报错时先看属于哪一行,再用对应的方法排查,大多数问题十分钟之内能定位到。

5.5 两个容易忽视的体验优化

再补充两个我实际踩过的坑。

第一个是导入路径的扩展名问题。在 Vite 项目里,默认是可以省略.vue和.js扩展名的,但省略.vue后缀有时会造成编辑器或某些工具链的解析歧义。我在团队规范里一般要求:Vue 文件必须带.vue后缀,JS 文件可以省略。这能减少很多 IDE 自动导入时的困惑。

第二个是导入的大小写敏感性。Windows 和 macOS 下文件系统对大小写不敏感,但 Linux 和 CI 环境是敏感的。如果在 Windows 上开发时写的是import UserCard from '@/components/UserCard.vue',实际文件名却是usercard.vue,本地跑没问题,部署到 Linux 上就 404 了。这种坑最容易在项目交接时爆炸,唯一的办法是规范命名,强制所有文件名和导入路径的大小写保持一致。

一些真心话和我的习惯

写了这么多年 Vue,我对导入导出最大的感受是:语法规则记住不难,真正难的是理解背后的模块系统和工具链的逻辑。很多人一开始学着写import就照着抄,报错了才意识到还有默认导出和命名导出的区别,还有路径解析和循环依赖这些概念。这很正常,我刚接触 Vue 时也在export default和export上晕了很久。

现在我在项目里逐渐形成了一个习惯:每个模块的导出方式在文件顶部注释里写明,默认导出还是命名导出,外部使用方大概怎么引入。像这样的注释:

/** * 日期工具函数 * 默认导出:dateUtils(所有工具函数的集合) * 命名导出:formatDate、getWeekDay */

这不算什么高大上的做法,但在团队协作时的作用出乎意料地好,新同事接手代码时基本不用猜。如果你在项目里经常被导入导出相关的报错困扰,不妨也试试这个习惯,把模块的接口设计这件事想得更清楚一点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 15:42:07

Entity、Model、Domain究竟有什么区别?一文讲透领域建模与分层架构

做过几年后端&#xff0c;面试候选人的时候我常问一个问题&#xff1a; Order 这个类&#xff0c;在你的项目里到底代表什么&#xff1f;大部分人会愣一下&#xff0c;然后说“就是订单表映射出来的实体啊”。再追问一句&#xff1a;“那它的状态流转、金额校验这些业务规则放…

作者头像 李华
网站建设 2026/10/2 15:42:05

AI算力全解析:GPU选型、集群搭建与调优实战

从2023年开始&#xff0c;大模型把AI算力这个词从机房拽到了大众视野里。以前GPU在大多数人眼中就是玩游戏用的显卡&#xff0c;现在它成了决定一个团队能不能训练大模型的核心资源。我因为长期做模型部署和高性能计算这块&#xff0c;这几年没少跟GPU打交道&#xff0c;从单卡…

作者头像 李华
网站建设 2026/10/2 15:41:40

给产品接入MCP Server:让AI Agent自动发现并调用你的服务

前阵子给我的小产品补了个很不起眼但影响很深远的接口&#xff1a;一个 MCP server。做完以后&#xff0c;效果很有意思——原本只能通过网页表单和 REST API 被人调用的报价服务&#xff0c;现在能被各种 AI agent 自动发现、自动调用、自动把报价单带回来。放在 2026 年这个节…

作者头像 李华
网站建设 2026/10/2 15:39:50

AI Agent算力底座矩阵:CPU与GPU异构编排实战

1. 从"模型竞赛"到"算力编排"&#xff1a;AI Agent 真正吃的是什么过去两年&#xff0c;大家聊 AI 聊的都是模型本身——参数多大、榜单多高、上下文多长。但真正把 AI Agent 跑起来的人会发现&#xff0c;卡脖子的地方往往不在模型&#xff0c;而在算力怎…

作者头像 李华
网站建设 2026/10/2 15:39:36

Nagios部署实战:用TaoToken统一Key打通告警链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华