1.组合式API
标准写法
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
2.响应式变量
2.1 ref函数定义响应式变量
<script setup name="App">
import { ref } from 'vue'
let username = ref('张三');
const onChangeUsername = () => {
username.value = '李四';
}
</script>
<template>
<div>用户名:{{ username }}</div>
<button @click="onChangeUsername">修改用户名</button>
</template>
2.2 reactive函数定义响应式变量
<script setup name="App">
import { reactive } from 'vue'
let author = reactive({username: "张三", age: 18})
let books = reactive([
{name: '水浒传',author: '施耐庵'},
{name: '三国演义',author: '罗贯中'}
])
const onModifyBookName = () => {
books[0].name = '红楼梦'
}
const onUpdateUsername = () => {
author.username = "李四"
}
</script>
<template>
<h1>用户名为:{{author.username}}</h1>
<button @click="onUpdateUsername">修改用户名</button>
<table>
<thead>
<tr>
<th>书名</th>
<th>作者</th>
</tr>
</thead>
<tbody>
<tr v-for="book in books" :key="book.name">
<td>{{ book.name }}</td>
<td>{{ book.author }}</td>
</tr>
</tbody>
</table>
<button @click="onModifyBookName">修改书名</button>
</template>
重新给reactive函数定义的响应式变量赋值,需要通过Object.assign来实现:
Object.assign(books, [{name: '红楼梦',author: '曹雪芹'}])
3.事件绑定
v-on
<script setup name="App">
import { reactive, ref } from "vue"
let count = ref(0);
const subtract = function (value) {
count.value -= value;
}
</script>
<template>
<p>{{ count }}</p>
<button v-on:click="count += 1">加</button>
<button v-on:click="subtract(10)">减10</button>
</template>
4. 双向绑定 v-model
<script setup>
import {ref} from "vue";
let username = ref("");
let category = ref(0);
</script>
<template>
<div>
<input v-model="username" />
<p>用户名为:{{username}}</p>
</div>
<div>
<select v-model="category">
<option value="1">Python</option>
<option value="2">前端</option>
</select>
<p>分类为:{{category}}</p>
</div>
</template>
5.计算属性
computed
<script setup>
import {ref, computed} from "vue";
let width = ref(0);
let height = ref(0);
let area = computed(() => {
return width.value*height.value;
})
</script>
<template>
<label for="length">长:</label>
<input type="number" name="height" v-model="height">
<label for="width">宽:</label>
<input type="number" name="width" v-model="width">
<label for="area">面积:</label>
<input type="number" name="area" :value="area" readonly>
</template>
6.监听属性
watch
6.1 ref函数
<script setup>
import {ref, watch} from "vue";
let person = ref({
username: '张三',
age: 18
})
const onUpdatePerson = () => {
person.value = {'username': '李四', "age": 20};
}
watch(person, (newValue, oldValue) => {
console.log(newValue);
})
</script>
<template>
<div>
<p>用户名:{{ person.username }},年龄:{{ person.age }}</p>
<button @click="onUpdatePerson">修改person</button>
</div>
</template>
6.2 reactive函数
<script setup>
import {ref, reactive, watch} from "vue";
let university = reactive({
name: '清华大学',
year: 1911
})
const updateUniversityName = () => {
university.name = '北京大学'
}
// 1. 监听一个子属性的变化
watch(() => univertisy.name, (newValue, oldValue) => {
console.log('new name: ', newValue);
})
// 2. 监听所有子属性的变化
watch(university, (newValue, oldValue) => {
console.log(newValue);
})
</script>
<template>
<div>
<div>大学名称:{{ university.name }}</div>
<button @click="updateUniversityName">更换大学名称</button>
</div>
</template>
7.生命周期
生命周期函数代表的是Vue实例,或者是Vue组件,在网页中各个生命阶段所执行的函数。生命周期函数可以分为创建阶段、挂载阶段、更新阶段以及卸载阶段。
- 创建阶段:
setup - 挂载阶段:
onBeforeMount、onMounted - 更新阶段:
onBeforeUpdate、onUpdated - 卸载阶段:
onBeforeUnmount、onUnmounted
<template>
<h2>count为:{{ count }}</h2>
<button @click="onUpdateCount">更新count</button>
</template>
<script setup>
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// 数据
let count = ref(0)
// 方法
function onUpdateCount() {
count.value += 1
}
console.log('setup')
// 生命周期钩子
onBeforeMount(() => {
console.log('挂载之前')
})
onMounted(() => {
console.log('挂载完毕')
})
onBeforeUpdate(() => {
console.log('更新之前')
})
onUpdated(() => {
console.log('更新完毕')
})
onBeforeUnmount(() => {
console.log('卸载之前')
})
onUnmounted(() => {
console.log('卸载完毕')
})
</script>
8.自定义组件
8.1 自定义属性
<script setup name="Person">
// Person组件
import {defineProps} from "vue";
// 1. 使用数组的形式
//const props = defineProps(['username'])
// 2. 使用对象的形式
const props = defineProps({
gender: {
type: String,
default: '男'
},
username: String,
age: Number,
body: {
height: Number,
weight: Number
}
})
</script>
<template>
<p>
用户名:{{ props.username }},
年龄:{{ props.age }},
身高:{{ props.body.height }},
体重:{{ props.body.weight }}
</p>
</template>
<script setup>
import Person from "./components/Person.vue"
</script>
<template>
<Person :age="18" username="张三" :body="{height: 180, weight: 140}"></Person>
</template>
8.2 自定义事件
<script setup name="Person">
import {defineEmits, ref} from "vue";
let steps = ref(0);
const emit = defineEmits(['change']);
const onUpdate = () => {
steps.value += 10
emit('change', steps.value);
}
</script>
<template>
<button @click="onUpdate">行走10步</button>
</template>
<script setup>
import Person from "./components/Person.vue"
const onPersonChange = (steps) => {
console.log("步行了:", steps);
}
</script>
<template>
<Person @change="onPersonChange"></Person>
</template>
8.3 定义v-model
<script setup name="Person">
import {defineModel, watch} from "vue";
let steps = defineModel();
const onUpdate = () => {
steps.value += 10;
}
watch(steps, (newValue, oldValue) => {
console.log("Person中监听到steps:", newValue);
})
</script>
<template>
<button @click="onUpdate">行走10步</button>
</template>
<script setup>
import {ref, watch} from "vue";
import Person from "./components/Person.vue"
let steps = ref(0);
watch(steps, (newValue, oldValue) => {
console.log("App中监听到的:", newValue);
})
</script>
<template>
<Person v-model="steps"></Person>
</template>
9.插槽
<template>
<button type="submit">
<slot></slot>
</button>
</template>
<template>
<SubmitButton>登录</SubmitButton>
</template>
10.路由VueRouter
10.1 基本使用
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
})
export default router
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>
<template>
<header>
<div class="wrapper">
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
10.2 嵌套路由
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},{
path: '/news',
component: () => import('../views/news/NewsView.vue'),
name: 'news',
children: [{
path: 'detail',
name: 'news-detail',
component: import('../views/news/NewsDetailView.vue')
}]
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
})
<template>
<ul>
<li><RouterLink :to="{name: 'news-detail'}">新闻1</RouterLink></li>
<li>新闻2</li>
<li>新闻3</li>
</ul>
<RouterView></RouterView>
</template>
10.3 路由传参
10.3.1 路由指定参数
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
path: '/news',
component: () => import('../views/news/NewsView.vue'),
name: 'news',
children: [{
path: 'detail/:pk',
name: 'news-detail',
component: import('../views/news/NewsDetailView.vue')
}]
}
]
})
<RouterLink :to="{name: 'news-detail', params: {pk: 1}}">新闻1</RouterLink>
<script setup name="NewsDetailView">
import { onMounted } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
onMounted(() => {
const pk = route.params.pk;
console.log(pk);
})
</script>
10.3.2 查询字符串传参
<RouterLink :to="{name: 'news', query: {page: 1}}">News</RouterLink>
<script setup name="NewsView">
import { onMounted } from 'vue';
import {useRoute} from "vue-router";
const route = useRoute();
onMounted(() => {
console.log(route.query);
})
</script>
10.4 编程式导航
router.push跳转
<script setup>
import {useRouter} from "vue-router";
const router = useRouter();
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
</script>
const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user
router.replace替换
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
10.5 守卫
10.5.1 全局守卫
router.beforeEach((to, from) => {
if (
// 检查用户是否已登录
!isAuthenticated &&
// ❗️ 避免无限重定向
to.name !== 'Login'
) {
// 将用户重定向到登录页面
return { name: 'Login' }
}
})
router.afterEach(function(to,from){
console.log('to:',to);
console.log('from:',from);
})
10.5.2组件内导航守卫
beforeRouteLeave (to, from) {
const answer = window.confirm('本页面还未保存,您确定要离开吗?')
if (!answer) return false
}
10.5.3 路由导航守卫
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
11 Pinia
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
例:
import { defineStore } from 'pinia'
export const useTodos = defineStore('todos', {
state: () => ({
/** @type {{ text: string, id: number, isFinished: boolean }[]} */
todos: [],
/** @type {'all' | 'finished' | 'unfinished'} */
filter: 'all',
// 类型将自动推断为 number
nextId: 0,
}),
getters: {
finishedTodos(state) {
return state.todos.filter((todo) => todo.isFinished)
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
/**
* @returns {{ text: string, id: number, isFinished: boolean }[]}
*/
filteredTodos(state) {
if (this.filter === 'finished') {
// 调用其他带有自动补全的 getters ✨
return this.finishedTodos
} else if (this.filter === 'unfinished') {
return this.unfinishedTodos
}
return this.todos
},
},
actions: {
// 接受任何数量的参数,返回一个 Promise 或不返回
addTodo(text) {
// 你可以直接变更该状态
this.todos.push({ text, id: this.nextId++, isFinished: false })
},
},
})
- THE END -
最后修改:2025年12月15日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://blog.grover.top/2025/12/15/%e5%89%8d%e7%ab%afvue%e7%ac%94%e8%ae%b0%e4%b8%8a%e4%bc%a0/
共有 0 条评论