这是 Vue 3 组合式 API 中非常核心且重要的概念。在 Vue 3中,构造函数 和普通函数在组合式函数(Composables)和组件生命周期中有着天壤之别。
简单来说,关键在于调用时机 和执行上下文(this
)。
核心区别总结
特性 | 构造函数 (以 new 调用) |
普通函数 (直接调用) |
---|---|---|
调用方式 | new ClassName() |
functionName() |
this 指向 |
指向新创建的实例对象 | 默认指向调用者 (严格模式下为 undefined ) |
返回值 | 默认返回新创建的实例 | 返回 return 语句的值,默认为 undefined |
主要目的 | 创建和初始化一个对象实例 | 执行一个过程或计算一个值 |
在 Vue 3 上下文中的具体体现
在 Vue 2 中,我们大量使用 new Vue()
来创建应用实例,并使用 new
来创建组件构造函数。但在 Vue 3 的 Composition API 世界里,我们更多地与普通函数打交道。
1. 应用和组件实例的创建 (构造函数)
Vue 3 应用本身仍然是通过构造函数模式创建的,但这被框架隐藏了。
-
createApp
: 看起来是一个普通函数,但它内部实际上抽象了new
的操作 ,返回一个应用实例 。javascriptimport { createApp } from 'vue' import App from './App.vue' // createApp 函数内部处理了实例化逻辑,返回一个应用实例对象 const app = createApp(App) // 类似于 const app = new VueAppConstructor(App) app.mount('#app')
-
Vue.extend()
(Vue 2) vs 单文件组件 (Vue 3) : 在 Vue 2 中,我们可以用const Component = Vue.extend({...})
创建一个组件构造函数 ,需要new Component()
来实例化。而在 Vue 3 中,我们写的.vue
单文件组件被vue-loader
处理,最终也会被转换成一个用于创建组件虚拟节点的构造函数,但这个过程对开发者是透明的。
2. 组合式函数 (Composables) (普通函数)
这是 Vue 3 的核心特性。组合式函数就是普通函数 ,它们内部使用 Vue 提供的 API(如 ref
, reactive
, onMounted
)。
javascript
// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 这是一个普通函数!绝不是构造函数。
export function useMouse() {
// 在函数内部定义响应式状态
const x = ref(0)
const y = ref(0)
// 在函数内部定义生命周期钩子
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 返回响应式状态
return { x, y }
}
在组件中使用:
vue
<script setup>
import { useMouse } from './composables/useMouse'
// 普通函数调用!每次调用都会创建一套独立的响应式状态和生命周期钩子。
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
关键点:
useMouse
是一个普通函数 ,直接调用useMouse()
。- 每次调用都会执行函数体,创建新的
ref
变量和新的 生命周期钩子,从而实现了状态的逻辑复用与隔离。 - 如果它被设计成构造函数(
new UseMouse()
),用法会变得非常笨重和奇怪,完全违背了组合式 API 的简洁理念。
3. 生命周期钩子 (普通函数)
Vue 3 的生命周期钩子都是普通函数 ,它们必须在同步的setup执行期间被调用。
vue
<script setup>
import { onMounted } from 'vue'
// 正确:普通函数调用,直接注册生命周期回调
onMounted(() => {
console.log('组件挂载完毕!')
})
</script>
你不能写 new onMounted(...)
,这会直接报错。
4. 响应式 API (普通函数)
ref
, reactive
, computed
等都是普通工厂函数,用于创建响应式对象。
javascript
import { ref, reactive } from 'vue'
// 普通函数调用,返回一个响应式引用对象
const count = ref(0)
// 普通函数调用,返回一个响应式代理对象
const state = reactive({ count: 0 })
为什么 Vue 3 更推崇普通函数?
Vue 3 的 Composition API 设计哲学是逻辑关注点分离 和更好的代码组织与复用。
- 更好的类型推断: 普通函数对 TypeScript 的支持极其友好,不需要复杂的泛型和构造函数重载。
- 更小的体积和更好的压缩 : 函数名(如
ref
)可以被压缩工具安全地重命名(mangle),而构造函数名(如Vue
)通常需要保留。 - 更灵活的逻辑组合: 普通函数可以轻松地在任何地方(不仅是组件中)被调用和组合,比如在其他函数内部、条件语句中等,提供了极大的灵活性。
- 更低的认知负担 : 无需理解
new
、prototype
、this
绑定等 JavaScript 构造函数相关的复杂概念,只需要会写函数和变量即可。 - Tree-shaking 友好: 打包工具可以更容易地检测和移除未使用的导出函数,减小最终打包体积。
结论
在 Vue 3 开发中,你的日常绝大部分工作都是在与普通函数 打交道:调用 ref()
、onMounted()
、自己编写 useXXX()
组合式函数。
而构造函数 的概念已经被框架底层封装和抽象(如 createApp
和组件编译),开发者几乎不需要直接使用 new
关键字。这种从"基于构造函数和原型"到"基于普通函数和闭包"的转变,是 Vue 3 组合式 API 现代化和强大功能的基础。