Vue 知识速查表
本页面汇总了 Vue 3 开发中最常用的语法和知识点,方便快速查阅。
基础概念
创建应用
import { createApp } from 'vue'
const app = createApp({
data() {
return { message: 'Hello Vue!' }
}
})
app.mount('#app')
单文件组件 (.vue)
<script setup>
import { ref, computed } from 'vue'
// 逻辑代码
const count = ref(0)
</script>
<template>
<!-- 模板 -->
<p>{{ count }}</p>
</template>
<style scoped>
/* 样式 */
</style>
响应式 API
ref 和 reactive
import { ref, reactive } from 'vue'
// ref - 基本类型
const count = ref(0)
count.value++
// reactive - 对象
const state = reactive({
count: 0,
name: '张三'
})
state.count++
computed
import { computed } from 'vue'
const count = ref(1)
const doubled = computed(() => count.value * 2)
// 可写计算属性
const fullName = computed({
get: () => firstName.value + lastName.value,
set: (value) => { /* ... */ }
})
watch
import { watch } from 'vue'
watch(count, (newValue, oldValue) => {
console.log(`${oldValue} -> ${newValue}`)
})
// 深度监听
watch(state, (newValue) => { /* ... */ }, { deep: true })
// 立即执行
watch(count, (newValue) => { /* ... */ }, { immediate: true })
// 监听多个
watch([count1, count2], ([new1, new2]) => { /* ... */ })
watchEffect
import { watchEffect } from 'vue'
// 自动追踪依赖
watchEffect(() => {
console.log(count.value)
console.log(name.value)
})
模板语法
插值
<!-- 文本 -->
{{ message }}
<!-- 原始 HTML -->
<span v-html="rawHtml"></span>
<!-- JavaScript 表达式 -->
{{ message.split('').reverse().join('') }}
指令
<!-- 绑定属性 -->
<img :src="imageSrc" />
<div :class="{ active: isActive }"></div>
<!-- 绑定事件 -->
<button @click="handleClick">点击</button>
<form @submit.prevent="handleSubmit"></form>
<!-- 双向绑定 -->
<input v-model="message" />
<input v-model.number="age" />
<input v-model.trim="username" />
<!-- 条件渲染 -->
<div v-if="show">显示</div>
<div v-else>隐藏</div>
<div v-show="show">v-show</div>
<!-- 列表渲染 -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
<li v-for="(item, index) in items">{{ index }}. {{ item }}</li>
<!-- 绑定样式 -->
<div :style="{ color: textColor, fontSize: fontSize + 'px' }"></div>
事件修饰符
@click.stop="handleClick" <!-- 阻止冒泡 -->
@click.prevent="handleSubmit" <!-- 阻止默认行为 -->
@click.self="handleClick" <!-- 仅自身触发 -->
@click.once="handleClick" <!-- 只触发一次 -->
@click.ctrl="handleClick" <!-- Ctrl 键 -->
组件
Props
<!-- 父组件 -->
<ChildComponent
:title="title"
:count="count"
required-prop="必填"
:optional-prop="optional"
/>
<!-- 子组件 -->
<script setup>
defineProps({
title: String,
count: {
type: Number,
required: true,
default: 0
},
items: {
type: Array,
default: () => []
}
})
</script>
Emits
<!-- 子组件 -->
<script setup>
const emit = defineEmits(['update', 'delete'])
emit('update', newValue)
emit('delete', id)
</script>
<!-- 父组件 -->
<ChildComponent
@update="handleUpdate"
@delete="handleDelete"
/>
v-model(传统方式)
<!-- 组件支持 v-model -->
<script setup>
defineProps({ modelValue: String })
defineEmits(['update:modelValue'])
</script>
<!-- 使用 -->
<Input v-model="text" />
<!-- 多个 v-model -->
<Input v-model:name="name" v-model:email="email" />
defineModel(Vue 3.4+)
<script setup>
// 简洁的双向绑定
const model = defineModel()
// 带类型
const count = defineModel({ type: Number, default: 0 })
// 命名 v-model
const name = defineModel('name')
// 带修饰符
const [value, modifiers] = defineModel()
if (modifiers.trim) { /* ... */ }
// 值转换
const trimmed = defineModel({
set(v) { return v.trim() }
})
</script>
<template>
<input v-model="model" />
</template>
插槽
<!-- 父组件 -->
<Layout>
<template #header>页眉</template>
<template #default>主内容</template>
<template #footer>页脚</template>
</Layout>
<!-- 子组件 -->
<slot name="header">默认内容</slot>
<slot>默认插槽</slot>
<!-- 作用域插槽 -->
<slot :item="item" :index="index" />
defineOptions(Vue 3.3+)
<script setup>
// 声明组件选项
defineOptions({
inheritAttrs: false,
name: 'MyComponent'
})
</script>
defineSlots(Vue 3.3+)
<script setup lang="ts">
// 类型安全的插槽定义
const slots = defineSlots<{
default(props: { item: Item }): any
header(): any
}>()
</script>
生命周期
组合式 API 钩子
| 选项式 API | 组合式 API |
|---|---|
| beforeCreate | 不需要 |
| created | 不需要 |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('mounted')
})
onUnmounted(() => {
console.log('unmounted')
})
路由
Vue Router 4
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
<!-- 路由视图 -->
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view />
<!-- 编程式导航 -->
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
router.push('/home')
router.back()
</script>
状态管理 (Pinia)
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubled: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
// 使用
import { useCounterStore } from './stores/counter'
const store = useCounterStore()
store.increment()
console.log(store.count)
console.log(store.doubled)
常用工具函数
toRef / toRefs
import { reactive, toRef, toRefs } from 'vue'
const state = reactive({ count: 0, name: '张三' })
// toRef - 保持响应式连接
const count = toRef(state, 'count')
// toRefs - 转换为普通对象
const { count, name } = toRefs(state)
isRef
import { isRef } from 'vue'
isRef(count) // true / false
Vue 3.5 新特性
useTemplateRef
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// 通过字符串 ID 获取模板引用
const inputEl = useTemplateRef('my-input')
onMounted(() => {
inputEl.value?.focus()
})
</script>
<template>
<input ref="my-input" type="text" />
</template>
useId
<script setup>
import { useId } from 'vue'
// 生成 SSR 安全的唯一 ID
const emailId = useId()
const passwordId = useId()
</script>
<template>
<label :for="emailId">邮箱</label>
<input :id="emailId" type="email" />
</template>
onWatcherCleanup
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const controller = new AbortController()
// 注册清理回调
onWatcherCleanup(() => {
controller.abort()
})
fetch(`/api/user/${newId}`, { signal: controller.signal })
})
响应式 Props 解构
<script setup lang="ts">
// 解构 props 并设置默认值(Vue 3.5+)
const {
count = 0,
message = 'hello'
} = defineProps<{
count?: number
message?: string
}>()
// watch 需要使用 getter
watch(() => count, (newVal) => { /* ... */ })
// 传递给 composable 使用 getter
useFeature(() => count)
</script>
Deferred Teleport
<!-- 延迟传送,目标可以在后面渲染 -->
<Teleport defer target="#container">
内容
</Teleport>
<div id="container"></div>
过渡与动画
Transition 过渡类
<!-- 单元素过渡 -->
<Transition name="fade" mode="out-in">
<p v-if="show">内容</p>
</Transition>
/* 六个过渡类:*-enter-from, *-enter-active, *-enter-to */
/* *-leave-from, *-leave-active, *-leave-to */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
Transition 属性
<!-- 过渡模式 -->
<Transition mode="out-in">...</Transition>
<Transition mode="in-out">...</Transition>
<!-- 初始渲染过渡 -->
<Transition appear>...</Transition>
<!-- 显式时长 -->
<Transition :duration="500">...</Transition>
<Transition :duration="{ enter: 300, leave: 500 }">...</Transition>
<!-- 自定义类名 -->
<Transition
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
...
</Transition>
JavaScript 钩子
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
>
<p v-if="show">内容</p>
</Transition>
TransitionGroup 列表过渡
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</TransitionGroup>
/* 进入离开过渡 */
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 移动过渡 */
.list-move {
transition: transform 0.5s ease;
}
/* 离开时绝对定位 */
.list-leave-active {
position: absolute;
}
常用 CSS
scoped
<style scoped>
/* 只作用于当前组件 */
.button { /* ... */ }
</style>
CSS Modules
<style module>
/* 访问 $style.button */
</style>
深度选择器
<style scoped>
/* 穿透子组件 */
:deep(.child-class) { /* ... */ }
/* 穿透伪类 */
:hover { /* ... */ }
</style>
常见问题
访问 DOM
const el = ref(null)
onMounted(() => {
console.log(el.value)
})
</script>
<template>
<div ref="el"></div>
</template>
组件暴露方法
<!-- Child.vue -->
<script setup>
defineExpose({
method: () => console.log('exposed')
})
</script>
<!-- Parent.vue -->
<Child ref="child" />
<script setup>
child.value?.method()
</script>
异步组件
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
快捷命令
# 创建项目
npm create vue@latest
# 安装依赖
npm install
# 开发服务器
npm run dev
# 构建生产版本
npm run build
# 预览构建结果
npm run preview
# 代码检查
npm run lint
参考资源
- Vue 3 官方文档
- Vue Router
- Pinia
- VueUse - Vue 组合式工具集