Vue2里的export default到底是什么?怎么用才对?
在Vue2项目里写组件时,“export default”几乎是天天见的代码,但不少刚入门的同学还是搞不清它到底是干啥的、怎么用才对,甚至和其他导出方式混淆,今天就把Vue2里export default的关键知识点拆成问题逐个讲透,不管是组件结构、和其他导出方式的区别,还是避坑技巧,看完心里就有数啦~
Vue2里的export default,本质是在搞啥事儿?
Vue2项目能用上export default,核心是ES6的模块导出语法在起作用,咱们写的每个.vue单文件组件,本质上都是一个独立的JS模块(哪怕里面有template、style,最终也会被工具链转成JS),而export default默认导出”这个模块里的核心内容——对于Vue组件来说,就是导出包含组件选项(像data、methods、props这些)的对象。
举个最直观的例子,一个简单的Vue组件长这样:
<template>
<div>{{ msg }}</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
msg: '你好,Vue2!'
}
}
}
</script>
这里的export default后面跟着的对象,就是把这个组件的“配置信息”对外暴露出去,其他文件想复用这个组件时,用import 自定义名称 from './HelloWorld.vue'就能拿到这个对象,Vue会自动把它处理成可渲染的组件实例。
另外得注意,每个JS模块只能有一个export default,这和“命名导出(export)”不一样(命名导出可以有多个,后面会对比),正因为是“默认”,所以import的时候不用像命名导出那样写大括号,还能自己给导入的内容起名字,灵活性很高。
写Vue2组件时,export default里该塞哪些东西?
在Vue2的组件里,export default后面跟的是组件选项对象,里面能配置的内容特别多,核心的有这些:
name:给组件起个名字,调试的时候(比如Vue DevTools里看组件层级)特别方便,还能实现组件递归(自己调用自己),比如name: 'MyComponent',递归场景下组件内部用<MyComponent />就能调用自己。data:必须是函数!返回一个对象,里面存组件的私有数据,要是写成对象(比如data: { msg: 'xxx' }),多个组件实例会共享同一份数据,一改全改,踩过这个坑的同学肯定懂有多坑…props:定义组件接收父组件传的值,像props: ['parentMsg']或者更严谨的对象形式props: { parentMsg: { type: String, required: true } },负责组件间的数据传递。methods:放组件的方法,比如事件处理函数、业务逻辑函数,像methods: { handleClick() { ... } },方法里的this默认指向组件实例。computed:计算属性,基于已有数据生成新数据,有缓存特性,比如computed: { fullName() { return this.firstName + this.lastName } },依赖不变就不会重复计算。watch:侦听数据变化做操作,比如watch: { msg(newVal, oldVal) { ... } },能深度侦听对象/数组变化(开deep: true)。- 生命周期钩子:像
created(实例创建完,数据观测、事件配置好了,但没挂载DOM)、mounted(DOM挂载完成,能操作DOM了)这些,在特定阶段执行逻辑。
举个更完整的例子,一个带交互的组件:
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
export default {
name: 'Counter',
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
created() {
console.log('组件创建好啦,count初始值是', this.count)
}
}
</script>
这里export default里的name、data、methods、created各司其职,共同撑起组件的功能和逻辑~
export default和export有啥区别?在Vue项目里咋选?
很多同学会把export default和export搞混,其实它们是ES6模块里两种不同的导出方式,区别大着呢:
规则层面:
export default:每个模块只能有一个默认导出,导入时用import 任意名称 from '模块路径',名字自己定,不用管导出时的名字。export(命名导出):一个模块可以有多个命名导出,导入时必须用import { 导出时的名字 } from '模块路径',名字得和导出时对应,除非用as重命名。
Vue项目里的实际用法:
在Vue2开发中,组件文件(.vue)几乎都用export default,因为一个.vue文件对应一个组件,用默认导出最直观,导入时还能随便起名字,
// 导出(HelloWorld.vue里)
export default { name: 'HelloWorld', ... }
// 导入(其他组件里)
import MyHello from './HelloWorld.vue' // 名字改成MyHello也能用
而export(命名导出)更适合工具库、工具函数文件,比如写了个utils.js,里面有多个工具函数:
// utils.js
export function formatTime(time) { ... }
export function deepClone(obj) { ... }
// 其他文件导入
import { formatTime, deepClone } from './utils.js'
// 或者重命名
import { formatTime as ft } from './utils.js'
简单说,Vue组件是“单模块单导出”,用export default;多函数/多常量的工具文件是“单模块多导出”,用export更灵活~
用export default时,哪些坑容易踩?咋避?
写多了组件,总会在export default这儿栽跟头,这些高频坑得提前避:
坑1:data写成对象,不是函数
错误写法:
export default {
data: { // 错!单文件组件里data必须是函数
msg: '错啦'
}
}
后果:多个组件实例会共享同一份data,比如列表里每个项用了这个组件,点一个全变。
解决:改成函数返回对象:
data() {
return {
msg: '对啦'
}
}
坑2:组件选项语法错误,比如少写逗号、钩子名拼错
错误示例:
export default {
name: 'MyComp'
data() { // 这里少了逗号,会报语法错
return { ... }
},
mounted() { ... },
mouted() { ... } // 钩子名mouted拼错,不会执行!
}
后果:要么代码直接报错跑不起来,要么生命周期钩子没触发,逻辑丢了。
解决:写的时候多检查语法,IDE装个Vue插件(比如Volar),能实时提示语法错误;生命周期钩子名记准(像mounted、updated这些)。
坑3:import时路径写错,找不到模块
错误示例:
import MyComp from '../components/MyComp.vue' // 实际文件是MyComponent.vue,路径或文件名错了
后果:浏览器控制台报“Module not found”,组件渲染不出来。
解决:养成好习惯,路径用相对路径时,检查或对不对;文件名大小写也要和实际文件一致(有些系统区分大小写)。
坑4:和Vuex、Vue Router结合时导出不对
比如Vuex模块用命名导出,结果Vuex识别不了:
错误写法(Vuex模块文件):
export const myModule = { // 用命名导出,Vuex默认找export default
state: { ... },
mutations: { ... }
}
后果:Vuex注册模块时读不到这个模块,状态管理失效。
解决:Vuex模块要用export default导出:
export default {
state: { ... },
mutations: { ... }
}
同理,Vue Router的路由组件配置里,组件导入要确保是默认导出的组件~
大型Vue2项目里,export default咋管理更高效?
当项目变大,组件越来越多,export default的写法也得讲究“高效复用、逻辑分层”,分享几个实用思路:
思路1:组件拆分+单一职责,每个组件export default清晰
把大组件拆成小组件,每个.vue文件只负责一块逻辑,export default里的选项也更简洁,比如一个复杂的表单组件,拆成FormItem.vue、FormButton.vue等子组件,每个子组件的export default只关注自己的props、methods,维护起来更轻松。
思路2:用mixins抽离重复逻辑
多个组件有相同逻辑(比如都要处理表单验证、都要监听窗口resize),可以把这些逻辑写到mixins里,mixins本身也是export default一个对象:
// mixins/resizeMixin.js
export default {
data() { return { windowWidth: 0 } },
mounted() {
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
this.windowWidth = window.innerWidth
}
}
}
// 在组件里用mixins
import resizeMixin from './mixins/resizeMixin.js'
export default {
mixins: [resizeMixin], // 自动合并mixins里的选项
name: 'MyComponent',
...
}
这样多个组件能复用resize相关逻辑,不用重复写生命周期和方法~
思路3:结合TypeScript增强类型(Vue2+TS场景)
Vue2对TS的支持需要借助defineComponent,写法变成:
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'MyComp',
data() {
return {
count: 0 // TS能推断count是number类型
}
},
props: {
msg: {
type: String,
required: true
}
}
})
</script>
defineComponent能让TS更智能地推断组件选项的类型,避免props传错类型、方法里this指向错误等问题,大型项目里类型约束能减少很多 bug~
Vue2里有没有不用export default的替代写法?
其实在Vue2里,export default不是唯一的导出方式,还有两种“替代方案”,但各有适用场景:
方案1:用CommonJS的module.exports(webpack支持)
早期Vue项目没用babel处理ES6模块时,会用CommonJS语法:
<template>...</template>
<script>
const MyComponent = {
name: 'MyComp',
data() { ... }
}
module.exports = MyComponent
</script>
导入时用const MyComp = require('./MyComponent.vue'),但现在Vue项目基本都用babel+webpack,支持ES6模块,export default更简洁,所以这种写法逐渐淘汰了。
方案2:用Vue.extend创建组件构造器再导出
Vue.extend是Vue2里创建组件构造器的API,写法:
<template>...</template>
<script>
import Vue from 'vue'
const MyComponent = Vue.extend({
name: 'MyComp',
data() { ... }
})
export default MyComponent
</script>
这种写法和直接export default { ... }的区别是:前者是“组件构造器”,后者是“组件选项对象”,但Vue Loader处理单文件组件时,会自动把export default { ... }的选项传给Vue.extend,所以两种写法最终效果一样。实际开发中更推荐直接写export default对象,代码更少更直观。
看完这些问题,是不是对Vue2里的`export default`从“眼熟但模糊”变成“清晰会用”了?简单总结下:它是ES6默认导出语法在Vue组件里的实践,负责把组件的配置对外暴露,和命名导出分工不同;写的时候要注意`data`必须是函数、选项语法正确这些细节;大型项目里结合拆分、mixins、TS能玩出更多效率花样~下次写组件时,再遇到`export default`就不会犯懵,还能避坑高效写代码啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网




发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。