Vue 是什么 & 版本差异
Vue 是一个渐进式前端框架,专注于“视图层 + 组件化 UI”。
常见版本:
Vue 2:Options API(
data / methods / computed / watch)+new Vue()Vue 3:推荐使用 Composition API(
setup()/ref/reactive)+createApp,性能更好。
建议:新项目全部用 Vue 3 + Composition API +
<script setup>。
1. “最小可运行”示例
1.1 直接 CDN 引入(练习/小 Demo)
<div id="app">
<h1>{{ title }}</h1>
<button @click="count++">Clicked {{ count }} times</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const title = ref('Hello Vue 3')
const count = ref(0)
return { title, count }
}
}).mount('#app')
</script>
关键点:
createApp({ setup() { ... } }).mount('#app')在
setup里定义响应式变量ref,return 出去给模板用。
1.2 单文件组件(SFC) + <script setup>
典型 HelloWorld.vue:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button @click="count++">Clicked {{ count }} times</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
msg: { type: String, default: 'Hello Vue 3' }
})
const count = ref(0)
</script>
<style scoped>
.hello { padding: 16px; }
</style>
要点:
<script setup>:语法糖,自动帮你做setup()和export default。defineProps在<script setup>中直接用。<style scoped>:样式只作用于当前组件。
2. 响应式系统:ref / reactive / computed / watch
2.1 ref:基本类型 + DOM 引用
import { ref } from 'vue'
const count = ref(0)
count.value++ // JS 内部用 .value
// 模板里用 {{ count }} 即可(会自动 .value)
适合:数字、字符串、布尔、对象也可以。
2.2 reactive:对象/数组响应式
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'Alice', age: 18 }
})
state.count++
state.user.name = 'Bob'
适合:复杂对象状态。
不要对
reactive对象再ref()一次。
2.3 computed:计算属性(带缓存)
import { ref, computed } from 'vue'
const firstName = ref('Ada')
const lastName = ref('Lovelace')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
当依赖的
firstName/lastName变时才重新计算。模板里直接
{{ fullName }}。
2.4 watch / watchEffect:监听变化
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log('count change:', oldVal, '->', newVal)
})
// 会自动收集依赖
watchEffect(() => {
console.log('count now:', count.value)
})
常见用法:
watch:精确监听某个值,适合异步请求、防抖等。watchEffect:自动依赖收集,适合简单打印/调试/副作用。
3. 模板语法 & 常用指令
3.1 插值 & 表达式
<p>{{ msg }}</p>
<p>{{ count + 1 }}</p>
<p>{{ user.name.toUpperCase() }}</p>
限制:
只能写简单表达式,不要写 if/for/赋值。
3.2 常用指令(v- 开头)
3.2.1 条件渲染
<div v-if="isLogin">已登录</div>
<div v-else>未登录</div>
<div v-show="isOpen">这个只是 display: none 切换</div>
区别:
v-if:真的插入/销毁 DOM,有开销。v-show:只切换 CSSdisplay,频繁切换建议用v-show。
3.2.2 列表渲染 v-for
<li v-for="(item, index) in list" :key="item.id">
{{ index }} - {{ item.name }}
</li>
必须写
:key,通常用唯一 id。可以遍历对象:
(value, key, index) in obj
3.2.3 事件绑定 v-on / @
<button @click="increment">+1</button>
<button @click="incrementBy(10)">+10</button>
<button @click="onClick($event)">with event</button>
const increment = () => { count.value++ }
const incrementBy = (n) => { count.value += n }
const onClick = (e) => { console.log(e.target) }
修饰符:
<form @submit.prevent="onSubmit">...</form>
<input @keyup.enter="search" />
.stop阻止冒泡.prevent阻止默认.enter、.esc等键盘修饰
3.2.4 属性绑定 v-bind / :
<img :src="imageUrl" :alt="title" />
<button :class="{ active: isActive }">Btn</button>
<div :style="{ color: activeColor, fontSize: size + 'px' }"></div>
v-bind 对象语法:
<div v-bind="someAttrs"></div>
4. 表单与 v-model
4.1 基本用法
<input v-model="username" placeholder="用户名" />
<textarea v-model="remark"></textarea>
<input type="checkbox" v-model="checked" />
<select v-model="selected">
<option value="A">A</option>
<option value="B">B</option>
</select>
4.2 修饰符
<input v-model.trim="name" />
<input v-model.number="age" />
<input v-model.lazy="keyword" />
.trim:自动去两端空格.number:自动转数字.lazy:改为change事件触发(默认是input)
4.3 组件上的 v-model(父 ↔ 子)
子组件 MyInput.vue:
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>
<script setup>
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
</script>
父组件:
<MyInput v-model="username" />
5. 组件系统:Props / Emits / 插槽
5.1 Props:父传子
子组件:
<script setup>
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 }
})
</script>
<template>
<h3>{{ title }}: {{ count }}</h3>
</template>
父组件:
<MyCard title="User" :count="users.length" />
5.2 Emits:子传父事件
<script setup>
const emit = defineEmits(['save', 'cancel'])
function onSave() {
emit('save', formData)
}
</script>
<template>
<button @click="onSave">Save</button>
<button @click="$emit('cancel')">Cancel</button>
</template>
父组件:
<EditForm @save="handleSave" @cancel="closeDialog" />
5.3 插槽(slots)
父组件:
<Card>
<template #header>
<h3>标题</h3>
</template>
正文内容
<template #footer>
<button>确定</button>
</template>
</Card>
子组件 Card.vue:
<template>
<div class="card">
<header><slot name="header">默认头部</slot></header>
<main><slot>默认正文</slot></main>
<footer><slot name="footer" /></footer>
</div>
</template>
6. 生命周期(Vue 3 + Composition API)
常用钩子(在 setup 内使用):
import { onMounted, onUnmounted, onUpdated } from 'vue'
onMounted(() => {
console.log('组件挂载完成')
})
onUpdated(() => {
console.log('组件更新')
})
onUnmounted(() => {
console.log('组件销毁')
})
更多:
onBeforeMountonBeforeUpdateonBeforeUnmountonErrorCapturedonActivated/onDeactivated(配合<keep-alive>)
7. 路由:vue-router(只记最常用)
7.1 安装 & 创建 Router
npm install vue-router
// 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: () => import('../views/User.vue') },
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
7.2 在 main.js 挂载
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
7.3 在模板中使用
<!-- App.vue -->
<template>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
<RouterView />
</template>
在组件中获取路由信息:
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.params.id)
function goHome() {
router.push('/')
}
8. 全局状态管理:Pinia(推荐)
8.1 安装 & 挂载
npm install pinia
// main.js
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
8.2 定义一个 Store
// stores/useCounterStore.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
在组件里用:
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/useCounterStore'
const counter = useCounterStore()
const { count, doubleCount } = storeToRefs(counter)
const inc = () => counter.increment()
</script>
<template>
<p>count: {{ count }}</p>
<p>double: {{ doubleCount }}</p>
<button @click="inc">+1</button>
</template>
9. 与后端交互:axios 简单封装
npm install axios
// api/request.js
import axios from 'axios'
const service = axios.create({
baseURL: '/api', // 根据开发环境配置代理
timeout: 10000,
})
// 简单拦截器(可加 token)
service.interceptors.response.use(
(res) => res.data,
(err) => Promise.reject(err)
)
export default service
使用:
// api/user.js
import request from './request'
export function getUser(id) {
return request.get(`/users/${id}`)
}
export function login(data) {
return request.post('/login', data)
}
组件内:
const user = ref(null)
onMounted(async () => {
user.value = await getUser(123)
})
10. DOM 操作 & 模板 ref
有时要直接操作 DOM 或子组件实例:
<template>
<input ref="inputEl" />
<ChildComp ref="childRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
import ChildComp from './ChildComp.vue'
const inputEl = ref(null)
const childRef = ref(null)
onMounted(() => {
inputEl.value.focus() // DOM 元素
console.log(childRef.value) // 子组件暴露的实例
})
</script>
在子组件中通过 defineExpose 暴露方法:
<script setup>
function doSomething() { ... }
defineExpose({ doSomething })
</script>
11. 组合式函数(Composables):复用逻辑
代替 Vue 2 的 mixins。
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const inc = () => count.value++
const dec = () => count.value--
return { count, inc, dec }
}
组件中:
import { useCounter } from '@/composables/useCounter'
const { count, inc, dec } = useCounter(10)
12. 自定义指令(高级一点)
// 全局注册(main.js)
app.directive('focus', {
mounted(el) {
el.focus()
}
})
使用:
<input v-focus />
13. 常见项目结构(Vue 3 + Vite)
src/
main.js # 入口
App.vue # 根组件
router/
index.js # 路由配置
stores/ # Pinia store
views/ # 页面级组件
components/ # 通用组件
composables/ # 组合式函数 (useXXX)
api/ # axios 封装 & 接口
assets/ # 静态资源
14. 快速对照表(Vue 2 → Vue 3)
Vue 3 快速自查清单(核心知识一页通)
(Composition API + <script setup>)
1. 项目最小模板
<template>
<h1>{{ msg }}</h1>
<button @click="count++">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const msg = 'Hello Vue'
const count = ref(0)
</script>
2. 响应式(核心)
2.1 ref() —— 基础类型
const count = ref(0)
count.value++
模板里不需要 .value:
<p>{{ count }}</p>
2.2 reactive() —— 对象/数组
const state = reactive({
user: { name: 'Alice' },
list: []
})
state.user.name = 'Bob'
2.3 computed() —— 计算属性
const double = computed(() => count.value * 2)
2.4 watch() —— 监听变化
watch(count, (newVal, oldVal) => {
console.log(newVal)
})
2.5 watchEffect() —— 自动依赖收集
watchEffect(() => {
console.log(count.value)
})
3. 模板指令(核心)
3.1 v-if / v-else
<p v-if="isLogin">已登录</p>
<p v-else>未登录</p>
3.2 v-show(频繁切换更快)
<p v-show="open">内容</p>
3.3 v-for
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
3.4 v-bind / :
<img :src="imgUrl" />
<div :class="{ active: isActive }"></div>
3.5 v-on / @
<button @click="doIt">Click</button>
<button @keyup.enter="search">Search</button>
4. v-model(双向绑定)
4.1 基础
<input v-model="name" />
4.2 修饰符
<input v-model.trim="name" />
<input v-model.number="age" />
<input v-model.lazy="text" />
4.3 组件上的 v-model(父 ↔ 子)
子组件:
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
父:
<MyInput v-model="username" />
5. 组件通信
5.1 Props(父 → 子)
子组件:
const props = defineProps({
title: String,
count: Number
})
父组件:
<MyComp title="A" :count="10" />
5.2 Emits(子 → 父)
子组件:
const emit = defineEmits(['save'])
emit('save', data)
父组件:
<MyForm @save="onSave" />
5.3 插槽(slot)
父:
<Card>
<template #header>头</template>
内容
</Card>
子:
<slot name="header" />
<slot />
6. 生命周期(Composition API)
onMounted(() => {})
onUnmounted(() => {})
onUpdated(() => {})
onBeforeMount(() => {})
onBeforeUpdate(() => {})
onBeforeUnmount(() => {})
7. 路由(vue-router)
7.1 定义
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/user/:id', component: User }
]
})
7.2 在 main.js
app.use(router)
7.3 在组件里获取路由
const route = useRoute()
const router = useRouter()
router.push('/login')
console.log(route.params.id)
8. Pinia(Vuex 替代)
8.1 定义 Store
export const useCounter = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++ }
}
})
8.2 使用
const counter = useCounter()
counter.increment()
9. Axios(最常用)
import axios from 'axios'
const api = axios.create({ baseURL: '/api' })
api.get('/user/1').then(res => {
console.log(res)
})
10. 模板 ref(DOM 引用)
<input ref="inputEl" />
<script setup>
const inputEl = ref(null)
onMounted(() => inputEl.value.focus())
</script>
11. 子组件方法暴露
子组件:
<script setup>
function doSomething() {}
defineExpose({ doSomething })
</script>
父组件:
<Child ref="child" />
<script setup>
const child = ref(null)
child.value.doSomething()
</script>
12. 组合式函数(复用逻辑)
export function useCounter() {
const count = ref(0)
const inc = () => count.value++
return { count, inc }
}
13. 条件 class / style
<div :class="{ active: ok, disabled: !ok }"></div>
<div :style="{ color: color, fontSize: size + 'px' }"></div>
14. 异步组件(懒加载)
const Dialog = defineAsyncComponent(() => import('./Dialog.vue'))
15. KeepAlive 缓存页面
<keep-alive>
<RouterView />
</keep-alive>
16. Teleport(传送到指定 DOM)
<Teleport to="body">
<Modal />
</Teleport>
17. 自定义指令
注册:
app.directive('focus', {
mounted(el) { el.focus() }
})
使用:
<input v-focus />
18. 常用项目结构(推荐)
src/
api/ # 后端接口
assets/
components/
composables/ # useXxx 组合函数
views/
router/
stores/ # pinia
App.vue
main.js
19. 性能优化快速清单
使用
v-show替代v-if(频繁切换)使用
key提升列表性能使用
computed而不是watch大组件切分成小组件
路由懒加载
() => import(...)使用
KeepAlive缓存页面
评论