Vue3实例怎么玩?从基础到实战这些关键要点得搞懂!
想学Vue3但一涉及“实例”就懵?别慌!不管是刚入门想搞懂基础创建,还是想在实战里玩转组件通信、状态管理,这篇问答把关键知识点拆明白,看完动手写项目更顺~下面从基础区别到实战技巧,一个个问题唠清楚!
Vue3里的“实例”和Vue2有啥不一样?
Vue2里我们写 new Vue({ el: '#app', ... }) 创建根实例,整个应用和根实例强绑定,但Vue3变了——用 createApp 先创建应用实例,再把根组件“挂”上去。
举个简单对比:
Vue2写法:
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
Vue3写法:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
核心区别有俩:
- 架构更“轻”更灵活:Vue3的
createApp返回的app是“应用实例”,能独立管理组件、指令、插件,比如做微前端时,不同应用实例互不干扰; - 根组件更纯粹:Vue2里根实例又当应用又当组件,Vue3把“应用”和“根组件”拆开,
App.vue就是普通根组件,由应用实例去挂载,职责更清晰。
怎么用createApp创建Vue3实例?
分三步,像搭积木一样:
引入createApp
先从vue包导入createApp这个工具函数:
import { createApp } from 'vue'
关联根组件
把你的根组件(比如App.vue)传给createApp,生成应用实例:
import App from './App.vue' const app = createApp(App)
挂载到DOM
用app.mount(选择器)把应用渲染到页面里,比如页面有<div id="app"></div>,就写:
app.mount('#app')
额外技巧:应用实例还能搞这些操作——
- 注册全局组件:
app.component('MyButton', MyButton) - 注册全局指令:
app.directive('focus', { mounted(el) { el.focus() } }) - 使用插件:
app.use(MyPlugin)
举个完整小例子,页面要显示“Hello Vue3”:
App.vue内容:
<template> <h1>Hello Vue3</h1> </template>
main.js(入口文件):
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
浏览器打开后,#app位置就会渲染出<h1>Hello Vue3</h1>,这就是最基础的Vue3实例创建流程~
组合式API怎么和Vue3实例配合?
Vue3的组合式API(比如ref、reactive、setup),是在组件内部和实例联动的,核心是让逻辑更聚合。
用setup做“逻辑入口”
在组件里写<script setup>(语法糖),它会自动关联到Vue实例的渲染逻辑,比如做个计数器:
<template>
<button @click="count++">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0) // ref让数字变成响应式
</script>
这里ref(0)创建响应式数据,setup里的变量/方法会自动暴露给模板用,不用像Vue2那样在data、methods里拆分。
生命周期钩子咋用?
组合式API里的钩子要导入用,比如组件挂载后执行逻辑:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log('组件挂载到页面啦!')
})
</script>
这些钩子是和当前组件的Vue实例生命周期绑定的,组件挂载/更新/卸载时,钩子会自动触发。
对比选项式API,组合式好在哪?
假设做一个“用户信息卡片”,选项式API得把data、methods、computed拆到不同配置项,逻辑分散;组合式API可以把“获取用户信息”“格式化时间”这些逻辑包在一个函数里,复用性更强。
<script setup>
import { ref, computed } from 'vue'
import { formatTime } from './utils' // 假设外部工具函数
const user = ref({ name: '小明', createTime: 1699999999 })
const formattedTime = computed(() => formatTime(user.value.createTime))
function updateName() {
user.value.name = '大明'
}
</script>
所有和“用户信息”相关的逻辑都在setup里扎堆,维护起来更顺,这就是组合式API和Vue3实例配合的优势~
做个TodoList,Vue3实例怎么处理数据和交互?
实战最能练手!做个“添加任务、标记完成、删除任务”的TodoList,拆解步骤:
需求 & 结构设计
页面要有:输入框+添加按钮、任务列表(每项显示标题+完成按钮+删除按钮)。
用响应式数据存任务
在根组件(比如App.vue)的setup里,用reactive存任务列表:
<script setup>
import { reactive } from 'vue'
const todos = reactive([
{ id: 1, title: '学习Vue3实例', done: false },
{ id: 2, title: '写个Demo', done: false }
])
</script>
reactive让对象/数组变成响应式,数据变了页面自动更。
实现“添加任务”
加个输入框和按钮,绑定v-model和点击事件:
<template>
<input v-model="newTask" placeholder="输入新任务" />
<button @click="addTodo">添加</button>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.title }}
<button @click="toggleDone(todo.id)">完成</button>
<button @click="deleteTodo(todo.id)">删除</button>
</li>
</ul>
</template>
<script setup>
import { reactive, ref } from 'vue'
const newTask = ref('') // 输入框的响应式数据
const todos = reactive([...]) // 同上
function addTodo() {
if (!newTask.value) return
todos.push({
id: Date.now(), newTask.value,
done: false
})
newTask.value = '' // 清空输入框
}
</script>
标记完成 & 删除任务
写两个方法修改todos:
<script setup>
// ...前面的代码
function toggleDone(id) {
const target = todos.find(todo => todo.id === id)
if (target) {
target.done = !target.done
}
}
function deleteTodo(id) {
todos.splice(todos.findIndex(todo => todo.id === id), 1)
}
</script>
样式优化(可选)
给完成的任务加删除线,用v-bind:class:
<li v-for="todo in todos" :key="todo.id" :class="{ done: todo.done }">
{{ todo.title }}
<!-- 按钮 -->
</li>
<style scoped>
.done {
text-decoration: line-through;
opacity: 0.5;
}
</style>
整个流程里,Vue3实例通过createApp挂载根组件,根组件用组合式API管理响应式数据和方法,实现交互逻辑,只要数据变了,Vue的响应式系统会自动更新DOM,不用手动操作DOM,这就是Vue3实例+响应式的威力~
Vue3实例里的组件通信咋搞?
组件通信是绕不开的坎,Vue3里常用这几种方式:
父传子:props
父组件给子组件传数据,子组件用defineProps接收。
父组件(TodoList.vue):
<template>
<TodoItem
v-for="todo in todos"
:key="todo.id"
:task="todo"
/>
</template>
<script setup>
import TodoItem from './TodoItem.vue'
import { reactive } from 'vue'
const todos = reactive([...]) // 任务列表
</script>
子组件(TodoItem.vue):
<template>
<div>{{ task.title }}</div>
<button @click="markDone">完成</button>
</template>
<script setup>
const props = defineProps(['task']) // 接收父组件的task
</script>
子传父:emit
子组件触发事件,父组件监听,比如子组件点“完成”,父组件更新任务状态。
子组件(TodoItem.vue):
<script setup>
const props = defineProps(['task'])
const emit = defineEmits(['toggle']) // 声明要触发的事件
function markDone() {
emit('toggle', props.task.id) // 把任务id传给父组件
}
</script>
父组件(TodoList.vue):
<template>
<TodoItem
...
@toggle="handleToggle"
/>
</template>
<script setup>
function handleToggle(id) {
// 找到对应任务,切换done状态
const target = todos.find(todo => todo.id === id)
if (target) target.done = !target.done
}
</script>
跨层级通信:provide / inject
如果组件嵌套很深(比如爷爷→爸爸→孙子),props层层传递太麻烦,用provide(祖先组件提供数据)和inject(后代组件注入数据)。
祖先组件(比如App.vue):
<script setup>
import { provide, reactive } from 'vue'
const theme = reactive({ mode: 'light' }) // 全局主题
provide('theme', theme) // 把theme提供出去
</script>
深层子组件:
<script setup>
import { inject } from 'vue'
const theme = inject('theme') // 注入theme
console.log(theme.mode) // 能拿到'light'
</script>
这三种方式覆盖了大部分场景,按需选择就行~
Vue3实例性能优化有哪些关键技巧?
Vue3本身性能比Vue2强(比如响应式基于Proxy),但写代码时注意这些细节,能更丝滑:
“自动提升”
Vue3会自动检测不依赖响应式数据的DOM节点,把它们“提升”到渲染函数外,减少重复渲染。
<template>
<div class="logo">固定Logo</div> <!-- 静态内容,只渲染一次 -->
<div>{{ dynamicMsg }}</div> <!-- 动态内容,数据变了才更 -->
</template>
不用你手动做啥,Vue3编译时自动优化~
按需导入,减少包体积
Vue3支持树摇(Tree Shaking),没用到的API不会打包,所以导入时要“按需拿”:
// 好的写法:用啥导啥
import { ref, onMounted } from 'vue'
// 不好的写法:导入整个vue(虽然能跑,但包更大)
import Vue from 'vue'
减少响应式追踪开销
如果数据很大(比如长列表、大对象),但只需要“顶层响应式”,用shallowRef或shallowReactive:
shallowRef:只有.value赋值时触发更新,内部属性变化不触发;shallowReactive:只有对象顶层属性变化触发更新,深层属性不管。
举个例子,加载一个很大的图片列表,用shallowRef:
import { shallowRef } from 'vue'
const imageList = shallowRef([])
// 赋值时触发更新
imageList.value = await fetchImages()
// 内部push不会触发更新(适合一次性替换数据的场景)
imageList.value.push(newImage) // 没用,不会更DOM
合理用<Teleport>优化渲染 要“跳出”父组件容器(比如弹窗、下拉菜单),用<Teleport>渲染到body下,避免父组件样式干扰,也能减少不必要的渲染层级。
<template>
<button @click="showModal = true">打开弹窗</button>
<Teleport to="body">
<div class="modal" v-if="showModal">
我是弹窗内容
</div>
</Teleport>
</template>
实例挂载后想改根组件,咋操作?
有时候项目运行中,想动态替换根组件(比如AB测试、动态加载主题组件),Vue3也能搞:
先unmount,再mount新组件
应用实例的mount是可以多次调用的,但要先把之前的卸载掉:
import { createApp } from 'vue'
import AppA from './AppA.vue'
import AppB from './AppB.vue'
const app = createApp(AppA)
app.mount('#app')
// 后来想换AppB
app.unmount() // 先卸载原来的
createApp(AppB).mount('#app') // 挂载新的
用动态组件+<component>
在根组件里用<component :is="currentApp" />动态切换:
<template>
<component :is="currentApp" />
</template>
<script setup>
import { ref } from 'vue'
import AppA from './AppA.vue'
import AppB from './AppB.vue'
const currentApp = ref(AppA)
// 某个事件触发切换
function switchApp() {
currentApp.value = AppB
}
</script>
这种方式更灵活,适合不需要完全替换应用实例,只是根组件内切换场景~
Vue3的“实例”不再是Vue2那种“大而全”的根实例,而是用createApp生成更灵活的应用实例,配合组合式API让逻辑更聚合,实战里处理数据、通信、性能都有对应的技巧,把这些问题吃透,写项目时思路会更顺,遇到需求也知道从哪下手~ 要是还有具体场景卡壳,评论区随时喊我唠!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


