一、基础使用
1. 创建应用
```javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
```
2. 模板语法
```vue
<template>
<!-- 插值 -->
<p>{{ message }}</p>
<!-- 原始HTML -->
<div v-html="rawHtml"></div>
<!-- 属性绑定 -->
<div :id="dynamicId"></div>
<button :disabled="isDisabled">按钮</button>
<!-- 简写 -->
<div :class="{ active: isActive }"></div>
<div :style="{ color: activeColor }"></div>
<!-- 事件绑定 -->
<button @click="handleClick">点击</button>
<input @keyup.enter="submit">
<!-- 双向绑定 -->
<input v-model="text">
<input v-model.number="age" type="number">
<input v-model.trim="text">
<!-- 条件渲染 -->
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>
<!-- 显示/隐藏 -->
<div v-show="isVisible">显示/隐藏</div>
<!-- 列表渲染 -->
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
<!-- 遍历对象 -->
<div v-for="(value, key) in object" :key="key">
{{ key }}: {{ value }}
</div>
</template>
```
3. 组件定义
```vue
<script setup>
// Composition API (推荐)
import { ref, reactive, computed, watch } from 'vue'
// 响应式数据
const count = ref(0)
const state = reactive({
name: 'Vue 3',
version: '3.0'
})
// 计算属性
const fullName = computed(() => {
return `{state.name} {state.version}`
})
// 方法
const increment = () => {
count.value++
}
// 侦听器
watch(count, (newValue, oldValue) => {
console.log(`count变化: {oldValue} -\> {newValue}`)
})
// 立即执行的侦听器
watch(
() => state.name,
(newValue) => {
console.log(`name变化: ${newValue}`)
},
{ immediate: true }
)
// 侦听多个数据源
watch([count, () => state.name], ([newCount, newName]) => {
console.log(newCount, newName)
})
// 生命周期
import { onMounted, onUpdated, onUnmounted } from 'vue'
onMounted(() => {
console.log('组件已挂载')
})
onUnmounted(() => {
console.log('组件已卸载')
})
</script>
<template>
<button @click="increment">{{ count }}</button>
<p>{{ fullName }}</p>
</template>
```
二、组件通信
1. Props 和 Emits
```vue
<!-- 子组件 Child.vue -->
<script setup>
// 定义props
const props = defineProps({
title: String,
count: {
type: Number,
default: 0,
required: true
}
})
// 定义emits
const emit = defineEmits(['update:count', 'customEvent'])
const handleClick = () => {
emit('update:count', props.count + 1)
emit('customEvent', 'data')
}
</script>
<template>
<h2>{{ title }}</h2>
<p>Count: {{ count }}</p>
<button @click="handleClick">增加</button>
</template>
<!-- 父组件 Parent.vue -->
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const count = ref(0)
const handleCustomEvent = (data) => {
console.log('收到数据:', data)
}
</script>
<template>
<Child
title="子组件"
:count="count"
@update:count="count = $event"
@custom-event="handleCustomEvent"
/>
</template>
```
2. 组件引用和Expose
```vue
<!-- 子组件 MyInput.vue -->
<script setup>
import { ref, defineExpose } from 'vue'
const inputRef = ref(null)
const value = ref('')
const focus = () => {
inputRef.value?.focus()
}
// 暴露给父组件的方法
defineExpose({
focus,
value
})
</script>
<template>
<input ref="inputRef" v-model="value">
</template>
<!-- 父组件 -->
<script setup>
import { ref } from 'vue'
import MyInput from './MyInput.vue'
const inputRef = ref()
const handleFocus = () => {
inputRef.value.focus()
console.log(inputRef.value.value)
}
</script>
<template>
<MyInput ref="inputRef" />
<button @click="handleFocus">聚焦</button>
</template>
```
3. Provide/Inject
```vue
<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
provide('changeTheme', (newTheme) => {
theme.value = newTheme
})
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const changeTheme = inject('changeTheme')
</script>
```
三、响应式系统
1. ref 和 reactive
```javascript
import { ref, reactive, toRefs } from 'vue'
// ref: 用于基本类型
const count = ref(0)
console.log(count.value) // 访问值
// reactive: 用于对象
const state = reactive({
count: 0,
name: 'Vue'
})
// 解构响应式对象
const { count, name } = toRefs(state)
// 现在 count 和 name 都是 ref
// 只读响应式对象
import { readonly } from 'vue'
const readOnlyState = readonly(state)
// 浅层响应式
import { shallowRef, shallowReactive } from 'vue'
const shallow = shallowReactive({ nested: { count: 0 } })
// nested 的变化不会触发更新
```
2. 响应式工具
```javascript
import { isRef, unref, toRef, markRaw } from 'vue'
// 检查是否是 ref
isRef(count) // true
// 获取值(如果是ref则.value,否则返回本身)
const value = unref(maybeRef)
// 将响应式对象属性转为 ref
const countRef = toRef(state, 'count')
// 标记为不可响应式
const rawObject = markRaw({ nested: {} })
```
四、计算属性和侦听器
1. 计算属性
```javascript
import { computed } from 'vue'
// 只读计算属性
const fullName = computed(() => {
return `{firstName.value} {lastName.value}`
})
// 可写计算属性
const writableFullName = computed({
get() {
return `{firstName.value} {lastName.value}`
},
set(newValue) {
const [first, last] = newValue.split(' ')
firstName.value = first
lastName.value = last
}
})
```
2. 侦听器
```javascript
import { watch, watchEffect } from 'vue'
// 基础侦听
watch(count, (newValue, oldValue) => {
// 当count变化时执行
})
// 侦听多个源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
// 当任意一个变化时执行
})
// 深度侦听对象
watch(
() => state.user,
(newUser) => {
console.log(newUser)
},
{ deep: true }
)
// 立即执行并清理副作用
watchEffect((onCleanup) => {
console.log(count.value)
// 清理函数
onCleanup(() => {
console.log('清理')
})
})
// 控制侦听时机
watch(
count,
() => {
console.log('count changed')
},
{ flush: 'post' } // 或 'pre'、'sync'
)
```
五、生命周期
```javascript
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onActivated,
onDeactivated
} from 'vue'
onBeforeMount(() => {
// 挂载前
})
onMounted(() => {
// 挂载后 - 可以访问DOM
})
onBeforeUpdate(() => {
// 更新前
})
onUpdated(() => {
// 更新后
})
onBeforeUnmount(() => {
// 卸载前
})
onUnmounted(() => {
// 卸载后 - 清理工作
})
onErrorCaptured((err, instance, info) => {
// 捕获子组件错误
return false // 阻止错误继续向上传播
})
// KeepAlive组件特有
onActivated(() => {
// 激活时
})
onDeactivated(() => {
// 停用时
})
```
六、自定义指令
```javascript
// 全局指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 局部指令
<script setup>
const vMyDirective = {
mounted(el, binding) {
// binding.value - 指令的值
// binding.oldValue - 之前的值(仅在beforeUpdate和updated中可用)
// binding.arg - 传给指令的参数
// binding.modifiers - 修饰符对象
},
updated(el, binding) {
// 更新时调用
},
beforeUnmount(el) {
// 卸载前清理
}
}
</script>
<template>
<input v-my-directive:arg.modifier="value">
</template>
```
七、内置组件
1. Transition
```vue
<template>
<button @click="show = !show">切换</button>
<Transition name="fade">
<p v-if="show">Hello</p>
</Transition>
<!-- 使用CSS -->
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
</template>
```
2. TransitionGroup
```vue
<template>
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</TransitionGroup>
</template>
```
3. KeepAlive
```vue
<template>
<KeepAlive :include="['ComponentA']" :exclude="['ComponentB']" :max="10">
<component :is="currentComponent" />
</KeepAlive>
</template>
```
4. Teleport
```vue
<template>
<button @click="modalOpen = true">打开模态框</button>
<Teleport to="body">
<div v-if="modalOpen" class="modal">
<p>模态框内容</p>
<button @click="modalOpen = false">关闭</button>
</div>
</Teleport>
</template>
```
5. Suspense
```vue
<!-- 父组件 -->
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<!-- 异步组件 -->
<script setup>
const { data } = await fetch('/api/data').then(r => r.json())
</script>
```
八、组合式函数
```javascript
// 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 }
}
// 在组件中使用
<script setup>
import { useMouse } from './useMouse'
const { x, y } = useMouse()
</script>
<template>
<p>鼠标位置: {{ x }}, {{ y }}</p>
</template>
```
九、TypeScript支持
```vue
<script setup lang="ts">
// 类型定义
interface User {
id: number
name: string
age?: number
}
// 带类型的ref
import { ref } from 'vue'
const user = ref<User>({
id: 1,
name: '张三'
})
// 带类型的props
const props = defineProps<{
title: string
count?: number
users: User[]
}>()
// 带类型的emits
const emit = defineEmits<{
(e: 'update:count', value: number): void
(e: 'select', user: User): void
}>()
// 带类型的computed
import { computed } from 'vue'
const userName = computed<string>(() => user.value.name)
// 带类型的provide/inject
import { provide, inject } from 'vue'
const key = Symbol() as InjectionKey<string>
provide(key, 'value')
const value = inject(key)
</script>
```
十、状态管理(Pinia)
```javascript
// store/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '张三',
age: 25,
token: null
}),
getters: {
// 类似计算属性
isAdult: (state) => state.age >= 18,
// 使用其他getter
greeting: (state) => {
return `Hello, ${state.name}`
}
},
actions: {
// 同步操作
updateName(newName) {
this.name = newName
},
// 异步操作
async login(credentials) {
const response = await api.login(credentials)
this.token = response.token
}
}
})
// 在组件中使用
<script setup>
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
// 直接修改状态
userStore.name = '李四'
// 使用action
userStore.updateName('王五')
// 使用getter
console.log(userStore.isAdult)
// 解构并保持响应式
const { name, age } = storeToRefs(userStore)
</script>
```
十一、路由(Vue Router 4)
```javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/user/:id',
name: 'User',
component: () => import('../views/User.vue'),
props: true, // 将params作为props传递
meta: { requiresAuth: true }
},
{
path: '/about',
component: () => import('../views/About.vue'),
children: [
{
path: 'info',
component: () => import('../views/AboutInfo.vue')
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 导航守卫
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
} else {
next()
}
})
```
```vue
<!-- 在组件中使用 -->
<script setup>
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
// 获取路由参数
console.log(route.params.id)
console.log(route.query.search)
// 导航
router.push('/home')
router.push({ name: 'User', params: { id: 1 } })
router.replace('/login')
router.go(-1)
// 编程式导航
const navigate = () => {
router.push({
path: '/user',
query: { search: 'vue' }
})
}
</script>
<template>
<!-- 路由链接 -->
<router-link to="/">首页</router-link>
<router-link :to="{ name: 'User', params: { id: 1 } }">
用户
</router-link>
<!-- 路由出口 -->
<router-view />
<router-view name="sidebar" /> <!-- 命名视图 -->
</template>
```
十二、实用技巧
1. 动态组件
```vue
<script setup>
import { shallowRef } from 'vue'
import Home from './Home.vue'
import About from './About.vue'
const components = {
Home,
About
}
const currentComponent = shallowRef(Home)
</script>
<template>
<component :is="currentComponent" />
<component :is="components['Home']" />
</template>
```
2. 自定义v-model
```vue
<!-- 子组件 -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const updateValue = (event) => {
emit('update:modelValue', event.target.value)
}
</script>
<template>
<input :value="modelValue" @input="updateValue">
</template>
<!-- 父组件 -->
<template>
<CustomInput v-model="text" />
<!-- 相当于 -->
<CustomInput
:modelValue="text"
@update:modelValue="text = $event"
/>
</template>
```
3. 属性透传
```vue
<!-- 父组件 -->
<template>
<MyButton class="large" @click="handleClick">
按钮
</MyButton>
</template>
<!-- 子组件 MyButton.vue -->
<template>
<button v-bind="$attrs" class="btn">
<slot />
</button>
</template>
<script>
// 禁用属性继承
export default {
inheritAttrs: false
}
</script>
```
4. 性能优化
```javascript
// 1. 使用shallowRef/shallowReactive
import { shallowRef } from 'vue'
const largeObject = shallowRef({ /* 大数据 */ })
// 2. 使用v-once
<template>
<div v-once>{{ staticContent }}</div>
</template>
// 3. 使用v-memo (Vue 3.2+)
<div v-memo="[valueA, valueB]">
<!-- 仅当valueA或valueB变化时更新 -->
</div>
// 4. 使用异步组件
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
```
**快速记忆要点:**
-
**组合式API是核心** - `ref()`, `reactive()`, `computed()`, `watch()`
-
**`<script setup>` 简化代码** - 自动导出,无需`return`
-
**TypeScript友好** - 完整的类型支持
-
**响应式系统改进** - 更精确的依赖追踪
-
**更小的包体积** - Tree-shaking支持更好
-
**更好的性能** - 更快的渲染速度和内存使用
-
**新的内置组件** - `Teleport`, `Suspense`
-
**多个根节点** - Fragment支持