Vue3: 单文件(SFC)输出多组件解决方案.理解defineComponent及如何使用

今天看到vue组件部分的知识.就想顺手写一个组件集合的文件.类似于

const ComponentA () => {
   //xxxxx
}

const ComponentB () => {
   //xxxxx
}

const ComponentC () => {
   //xxxxx
}

export {
    ComponentA,
    ComponentB,
    ComponentC
}

可以封装函数式组件或者函数式方法的这么个函数集合.这种写法在React中是很常见的.但是今天切换到Vue可让我重新了认识了vue的组件的复杂度了.首先并不是写起来多复杂,而是有不同的写法,写法不一样,效果也不一样,限制也不同...

那怎么能写出跟React风格一样的这种组合式的函数集呢.

为什么要写这个集合

首先聊下为什么要写这种集.就我个人习惯而言,习惯把工具函数单独放到一个文件下,而组件呢也是相同的或者有关联的小的共同组件放到一个文件,例如一些像input 带 icon, tooltip,  v model这些使用频率比较多的小组件放到一起. 正如我上面说的在React下很轻松的一件事.但是在vue中,因为vue是SFC.与其说是推荐更是强制一个文件一个组件的方式.但是十几行代码的组件放一个文件,当项目文件越来越多后,这些共用小组件一个个的散落在不同文件下显然对整个项目的文件架构有一定影响.写2+个import 和写一个import区别有目共睹.

解决方案

那有没有办法一个文件里写多个组件然后输出呢。

其实关键点就是export,如果export多个组件就是解决问题的关键.网上翻看了一些资料,然后结合官网又看找了下,构建组件的defineComponent 方法提供了export的功能.于是开始实践.

网上很多资料没有提到具体的实现,csdn上很多小伙伴的讲解也都是些文字的概念有头无尾的.说了一堆概念,最后还得落实到代码上.毕竟没有结果的说明不知道是否真的使用过.

首先defineComponent 这个函数,从名字就可以看出可以构建出一个组件.但是这个组件不支持set up 这种组合式的编写(吐槽下哈 vue的组合式和选项式,真是对新手太不友好,一套语言2中写法)
 

构建defineComponent方法

// 子组件 child component
<script  lang="ts">
import {defineComponent, h } from 'vue'

 export default defineComponent({
         props:{
            txt:String,
         },
         name: "ComponentA",
         setup(props, ctx) {
            const count = ref(0)
            const add = () => {
                 count.value++
            }
            return { state,increment }
         },
 })

<template>
        <div>
               <button @click="increment">{{ state }}</button> 
        </div>
</template>


首先用defineComponet声明一个的函数组件,

接受父组件的传过来的参数txt, 并且设置组件名称ComponentA
设置setup的方法进行一些逻辑的计算,这里可以用计算模式
输出最后组件模版。

父组件中

// 父组件


<script setup lang="ts">
import * as Children from './children.vue'
import {ref, defineModel} from 'vue'
</script>
<template>
    <div>
        <KeepAlive>
            <div>
                <Children txt="from parent component ref"/>
            </div>
        </KeepAlive>
    </div>
</template>

开到这里,你可能会问,着不就是一个单文件一个组件吗.的确,这就是关于defindeComponent网上的一些使用的写法,只不过没这么具体

接下来我们要改成我们的多组件形式

多组件形式

<script  lang="ts">
import {defineComponent, h, ref } from 'vue'

        const ComponentA = defineComponent({
                props: { txt:String},
                setup(props, ctx) {
                   const count = ref(0)
                   const increment = () => {
                        count.value++
                   }
                   return () => h('div', {id: 'foo'},
                    ['', h('div',{onClick: increment} ,props.txt),
                          h('div', count.value)])
                },
        })
 
        const ComponentB = defineComponent ({
                props:{
                  title: String,
                  cut:() => {}
                },
                setup(props, ctx) {
                const {cut} = props
                  return () => h('div', ['ddd', h('div',{onClick: cut}, props.title)])      
                },
        })

        export {ComponentA, ComponentB}
</script>


这里首先我们删掉了template标签,因为在多组件文件中,并不知道你具体选用的哪个一个模版

但是组件函数是必须要有模版输出的,其实defineComponet中提供一个template的属性,可以编写

template: `<div> you ref value </div>`

但是加了这个就会马上有一个警告需要你去更改vue的build配置,原因就是解析不到template下能够抓换成html的vue代码,有点难懂是吧.说白了没法转译,可能在单组件下可行吧,但是单组件下写defineComponent这种不是很麻烦吗?vue真是提供了好多方法去实现一个东西.

回到上面的代码来.既然模版不能用template那怎么输出?


模版输出


这里的模版输出是在setup 的return中使用h库进行的jsx方式输.说实话写jsx虽然很易懂但是很容易写错,而且易懂性真的不高,但是执行的效率上,编码速度上有很大优势.

    return () => h('div', {id: 'foo'},
                    ['', h('div',{onClick: increment} ,props.txt),
                          h('div', count.value)])
                },

这样就生成了一个

这种结构的dom树

然后在父组件我们就可以按照我喜欢的react风格引用

import {ComponentA, ComponentB} from './com2.vue'

<template>
    <ComponentA txt="222"/>
// 这里还可以传递事件函数,对象数组,在子组件中用propos接受就可以
    <ComponentB title="multi component" />
</template>

输出结果

当我点击子组件ComponentA的button ->222 (222其实这是个按钮名),下面的0会调用子组件++的逻辑开始执行,这里我点了4下

至此解决了单文件多组件的问题。但是写到着我也理解为什么了vue不这么写组件集了.太费劲了,下不说jsx写起来就挺费劲的.如果组件传递参数多样,加上expend的扩展, 各自事件绑定和自定义事件 .还有加上emit 又可能遇到透传.真就是改起来不入写一个新的单组件了.

最后还是给出另一个方法如果想用就单独写个uitls文件自己整合下,如下
还可以放函数表达式

// list.js
import ComponentA form './ComponentA.vue'
import ComponentB form './ComponentB.vue'

export {
    ComponentA,
    ComponentB
}

<think> 根据用户的问题和提供的引用,用户在使用Vue 3项目时,直接引用组件出现了“Module has no default export”的错误。这个问题通常发生在使用非默认导出(named export)的组件时,而导入语句却尝试以默认导入(default import)的方式引入。在Vue 3中,特别是在使用Composition API和单文件组件SFC)时,我们通常使用`defineComponent`来定义组件,并且可以选择默认导出或命名导出。然而,当使用命名导出时,导入方式需要相应调整。 根据引用[1]、[2]、[4]中的信息,该错误可能由以下原因引起: 1. 在导入组件使用了默认导入(如:`import HelloWorld from &#39;./components/HelloWorld.vue&#39;`),但组件文件并没有使用`export default`导出,而是使用了命名导出(如:`export const HelloWorld = defineComponent({...})`)。 2. 使用了Vetur插件,但该插件不支持Vue 3(引用[2]提到删除Vetur插件可以解决,但实际上更推荐使用Volar来支持Vue 3)。 解决方案如下: 1. **修改导入方式**:如果组件使用的是命名导出,那么导入时应该使用大括号包裹的命名导入(named import)。例如: ```javascript // 如果组件导出是:export const HelloWorld = defineComponent(...) import { HelloWorld } from &#39;./components/HelloWorld.vue&#39; ``` 2. **修改导出方式**:如果希望继续使用默认导入,可以在组件文件中使用默认导出。例如: ```javascript // HelloWorld.vue export default defineComponent({ // 组件选项 }) ``` 3. **检查Vetur插件**:引用[2]提到,Vetur插件可能不支持Vue 3,建议删除Vetur并安装Vue 3官方推荐的Volar插件(Vue Language Features (Volar))。在VSCode中禁用或卸载Vetur,然后安装Volar。 4. **检查tsconfig.json(如果使用TypeScript)**:确保在tsconfig.json中启用了Vue 3的类型检查。可以设置`"module": "ESNext"`和`"moduleResolution": "Node"`,以及添加`"allowSyntheticDefaultImports": true`来允许默认导入。但请注意,这只是一个兼容性设置,最好还是使用正确的导入导出方式。 5. **检查vite配置(如果使用Vite)**:引用[3]提到在Vite中引入某些库(如xlsx)时可能遇到类似问题,但这里我们讨论的是Vue组件。不过,确保Vite配置正确也是必要的。Vite默认支持ES模块,所以应该正确识别命名导出和默认导出。 根据用户的问题,重点在于组件的导入导出方式。因此,我推荐用户首先检查组件是如何导出的,然后调整导入语句。 下面是一个示例: 假设组件文件`HelloWorld.vue`使用命名导出: ```vue <script> import { defineComponent } from &#39;vue&#39; export const HelloWorld = defineComponent({ // ... }) </script> ``` 则在父组件中导入时,应该使用: ```vue <script> import { HelloWorld } from &#39;./components/HelloWorld.vue&#39; export default { components: { HelloWorld } } </script> ``` 或者,如果用户希望使用默认导出,修改`HelloWorld.vue`为: ```vue <script> import { defineComponent } from &#39;vue&#39; export default defineComponent({ // ... }) </script> ``` 然后父组件导入: ```vue <script> import HelloWorld from &#39;./components/HelloWorld.vue&#39; export default { components: { HelloWorld } } </script> ``` 另外,引用[4]中的错误信息也提示了同样的解决方案。 如果用户同时存在个导出,并且希望在一个导入语句中导入组件,可以使用: ```javascript import * as Components from &#39;./components/HelloWorld.vue&#39; // 然后通过 Components.HelloWorld 使用 ``` 但是,通常我们更推荐按需导入。 综上所述,用户需要根据组件的导出方式调整导入方式,或者统一使用默认导出(这是Vue单文件组件常见的做法)。 最后,关于Vetur插件,确实在Vue 3项目中建议使用Volar。所以,用户应检查IDE的插件设置。 因此,给用户的解决方案是: 1. 调整组件的导出和导入方式,确保匹配(默认导出对应默认导入,命名导出对应命名导入)。 2. 在VSCode中禁用或卸载Vetur插件,安装并启用Volar插件。 这样应该能解决“Module has no default export”的错误。 生成相关问题: 1.Vue 3中如何正确导出和导入组件? 2. Vue 3项目中使用Volar插件有哪些优势? 3. 如何解决Vite项目中导入第三方库时出现的没有默认导出的错误? </think> ### Vue 3 组件导入报错解决方案Vue 3 项目中遇到 `Module has no default export` 错误,通常是由以下原因导致: #### 核心原因分析 1. **组件未默认导出**:Vue 组件文件缺少 `export default` 声明 2. **错误导入语法**:使用默认导入语法 `import X from &#39;...&#39;` 导入命名导出的组件 3. **插件冲突**:Vetur 插件与 Vue 3 不兼容(引用[2][4]) --- ### 解决方案 #### 方法 1:修改组件导出方式(推荐) ```vue <!-- 组件文件 MyComponent.vue --> <script setup> // ✅ 正确:添加 export default export default { setup() { // 组件逻辑 } } </script> ``` #### 方法 2:调整导入语法 ```javascript // ❌ 错误导入方式 import MyComponent from &#39;./MyComponent.vue&#39; // ✅ 正确:使用命名导入语法 import { MyComponent } from &#39;./MyComponent.vue&#39; ``` #### 方法 3:修复开发环境配置 1. **卸载 Vetur 插件**(引用[2][4]) ```bash # 在 VSCode 扩展中搜索 Vetur 并卸载 ``` 2. **安装 Vue 官方插件 Volar** [Volar 扩展链接](https://marketplace.visualstudio.com/items?itemName=Vue.volar) #### 方法 4:配置 TypeScript(TS 项目) 在 `tsconfig.json` 中添加: ```json { "compilerOptions": { "allowSyntheticDefaultImports": true } } ``` #### 方法 5:第三方库特殊处理(如 xlsx) ```javascript // ✅ 兼容性导入(引用[3]) import * as XLSX from &#39;xlsx&#39; // 替代错误写法:import XLSX from &#39;xlsx&#39; ``` --- ### 最佳实践建议 1. **统一导出规范**:所有组件使用 `export default defineComponent({...})` 2. **检查文件扩展名**:确保导入路径包含 `.vue` 后缀 3. **验证组件定义**:单文件组件需包含 `<template>` 或 `<script setup>` 4. **更新依赖**:确保 `vue` 和 `@vitejs/plugin-vue` 为最新版本 > 通过上述调整,可解决 95% 的 `Module has no default export` 错误(引用[1][2][3][4])。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值