Vue 3 入门指南:从基础到实战
文章目录
模板语法:构建动态页面
Vue 3 使用简单直观的模板语法,让你轻松构建动态 HTML 页面。
插值表达式
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const title = ref('欢迎来到 Vue 3 世界')
const message = ref('开始你的前端之旅吧!')
</script>
指令:v-bind 和 v-on
<template>
<button v-bind:class="{ active: isActive }" v-on:click="handleClick">
点击我
</button>
</template>
<script setup>
import { ref } from 'vue'
const isActive = ref(false)
const handleClick = () => {
isActive.value = !isActive.value
}
</script>
条件渲染:v-if 和 v-else
<template>
<div v-if="showGreeting">
<h1>欢迎回来!</h1>
</div>
<div v-else>
<h1>请登录</h1>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showGreeting = ref(true)
</script>
列表渲染:v-for
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const items = ref(['苹果', '香蕉', '橘子'])
</script>
侦听器:监听数据变化
<template>
<button @click="increment">计数:{{ count }}</button>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
watch(count, (newCount, oldCount) => {
console.log(`计数器从 ${oldCount} 变为 ${newCount}`)
})
</script>
计算属性:简化复杂逻辑
<template>
<p>商品总价:{{ totalPrice }}</p>
</template>
<script setup>
import { ref, computed } from 'vue'
const price = ref(100)
const quantity = ref(2)
const totalPrice = computed(() => {
return price.value * quantity.value
})
</script>
组件:构建可复用模块
注册组件
<template>
<MyComponent message="欢迎使用组件!" />
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
父组件向子组件传值
<!-- MyComponent.vue -->
<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps(['message'])
</script>
子组件向父组件传值
<!-- Child.vue -->
<template>
<button @click="emitClick">点击我</button>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits()
const emitClick = () => {
emit('childClick')
}
</script>
<!-- Parent.vue -->
<template>
<Child @childClick="handleClick" />
</template>
<script setup>
const handleClick = () => {
console.log('子组件被点击了!')
}
</script>
透传属性和事件
<!-- Child.vue -->
<template>
<div v-bind="$attrs" @click="$emit('click', $event)">
{{ message }}
</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps(['message'])
</script>
<!-- Parent.vue -->
<template>
<Child :message="greeting" class="my-class" @click="handleClick" />
</template>
<script setup>
import { ref } from 'vue'
const greeting = ref('你好!')
const handleClick = (event) => {
console.log('Child 组件被点击了!', event)
}
</script>
插槽:定制组件内容
<!-- Child.vue -->
<template>
<div>
<slot />
</div>
</template>
<!-- Parent.vue -->
<template>
<Child>
<p>这是自定义内容,插入到插槽中</p>
</Child>
</template>
依赖注入:共享数据和方法
<!-- main.js -->
import { createApp } from 'vue'
import App from './App.vue'
import MyService from './MyService.js'
const app = createApp(App)
app.provide('myService', new MyService())
app.mount('#app')
<!-- MyService.js -->
export default class MyService {
getMessage() {
return '来自依赖注入服务的欢迎信息!'
}
}
<!-- App.vue -->
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { inject } from 'vue'
const myService = inject('myService')
const message = myService.getMessage()
</script>
模板引用:访问DOM元素
v-ref
<template>
<input type="text" v-model="name" ref="inputRef" />
<button @click="focusInput">聚焦输入框</button>
</template>
<script setup>
import { ref } from 'vue'
const name = ref('')
const inputRef = ref(null)
const focusInput = () => {
inputRef.value.focus()
}
</script>
v-for 中的模板引用
<template>
<ul>
<li v-for="(item, index) in items" :key="index" ref="listItemRefs">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const items = ref(['苹果', '香蕉', '橘子'])
const listItemRefs = ref([])
onMounted(() => {
console.log(listItemRefs.value[0]) // 访问第一个列表项
})
</script>
组件上的 ref
<!-- Child.vue -->
<template>
<div ref="childRef">{{ message }}</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps(['message'])
</script>
<!-- Parent.vue -->
<template>
<Child ref="childComponent" message="来自父组件的信息" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const childComponent = ref(null)
onMounted(() => {
console.log(childComponent.value.$refs.childRef)
})
</script>
路由:构建单页面应用
快速使用
<!-- main.js -->
import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import App from './App.vue'
import Home from './components/Home.vue'
import About from './components/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
const app = createApp(App)
app.use(router)
app.mount('#app')
<!-- App.vue -->
<template>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view />
</div>
</template>
重定向路由
const routes = [
{ path: '/', redirect: '/home' }, // 重定向到 '/home'
]
嵌套路由
const routes = [
{
path: '/products',
component: Products,
children: [
{ path: ':id', component: ProductDetails } // 商品详情
]
}
]
路径参数
<!-- ProductDetails.vue -->
<template>
<div>
<h1>商品详情:{{ $route.params.id }}</h1>
</div>
</template>
声明式与编程式导航
<!-- 使用 router-link 进行声明式导航 -->
<template>
<router-link to="/about">关于</router-link>
</template>
<!-- 编程式导航 -->
<template>
<button @click="goHome">返回首页</button>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goHome = () => {
router.push('/')
}
</script>
导航守卫
const router = createRouter({
routes,
beforeEach: (to, from) => {
if (to.path === '/admin' && !isLoggedIn) {
return '/login' // 重定向到登录页面
}
return true // 允许导航
}
})
状态管理库 Pinia:集中管理状态
创建 Store
// store.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
使用 Store
<template>
<div>
<p>计数:{{ counter.count }}</p>
<p>双倍计数:{{ counter.doubleCount }}</p>
<button @click="counter.increment">增加</button>
</div>
</template>
<script setup>
import { useCounterStore } from './store'
const counter = useCounterStore()
</script>
网络请求:使用 Axios
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const posts = ref([])
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
posts.value = response.data
})
.catch(error => {
console.error('请求失败:', error)
})
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>