Vue3 是 Vue 的新一代版本,核心更新是组合式 API,它让代码组织更灵活、逻辑复用更高效。
一、为什么要学 Vue3?
相比 Vue2,Vue3 的核心优势:
- 性能更强:打包体积更小、运行速度更快;
- 组合式 API:替代 Vue2 的选项式 API,更灵活地组织代码逻辑;
- 更好的 TypeScript 支持:原生兼容 TS,类型提示更友好;
- 新特性:如Teleport、Suspense等,提升开发效率。
二、Vue3 项目创建:使用 create-vue
Vue3 推荐用官方脚手架create-vue创建项目(替代 Vue2 的 Vue CLI)。
步骤 1:创建项目
打开终端执行:
bash
npm create vue@latest
根据提示选择配置(新手推荐:TypeScript选No,其余默认),完成后进入项目目录并启动:
bash
cd 项目名
npm install
npm run dev
步骤 2:项目目录和关键文件
Vue3 项目核心目录结构:
plaintext
项目名/
├── public/ # 静态资源(如favicon)
├── src/
│ ├── assets/ # 静态资源(图片、样式)
│ ├── components/ # 公共组件
│ ├── App.vue # 根组件(组合式API写法)
│ └── main.js # 入口文件(创建Vue实例)
└── package.json # 项目配置
关键文件:main.js(Vue3 创建实例的方式):
javascript
import { createApp } from 'vue'
import App from './App.vue'
// Vue3通过createApp创建实例
createApp(App).mount('#app')
三、组合式 API 核心用法
组合式 API 的入口是setup,它是 Vue3 组件的逻辑核心,所有组合式 API 都在setup中使用。
1. setup 选项:组合式 API 的入口
setup是组件的 "逻辑区域",在组件创建前执行,返回的对象会暴露给模板使用。
示例:
vue
<!-- <script>
// setup
// 1. 执行时机,比beforeCreate还要早
// 2. setup函数中,获取不到this (this是undefined)
// 3. 数据 和 函数,需要在 setup 最后 return,才能在模板中使用
// 问题:每次都要return很麻烦
// 4. 通过 setup 语法糖简化代码
export default{
setup () {
console.log('setup函数')
const message = '你好'
return {message}
},
beforeCreate () {
console.log('beforeCreate函数')
}
}
</script> -->
<script setup>
const message = '我是语法糖'
</script>
<template>
<div>vue3学习</div>
<div>{{message}}</div>
</template>
2. reactive 和 ref:响应式数据定义
Vue3 通过reactive和ref定义响应式数据(数据变化时自动更新视图)。
(1)reactive:定义对象 / 数组类型的响应式数据
vue
<script>
import { reactive } from 'vue' // 引入reactive
export default {
setup() {
// 用reactive包裹对象,使其变为响应式
const user = reactive({
name: '张三',
age: 20
})
// 修改响应式数据
const updateUser = () => {
user.age = 21 // 自动触发视图更新
}
return { user, updateUser }
}
}
</script>
<template>
<div>姓名:{{ user.name }}</div>
<div>年龄:{{ user.age }}</div>
<button @click="updateUser">增加年龄</button>
</template>
(2)ref:定义基本类型(或单值)的响应式数据
vue
<script>
import { ref } from 'vue' // 引入ref
export default {
setup() {
// 用ref包裹基本类型,返回的是响应式对象
const count = ref(0)
// 修改ref数据时,需通过.value访问
const increment = () => {
count.value++
}
return { count, increment }
}
}
</script>
<template>
<!-- 模板中使用ref数据,无需.value -->
<div>计数:{{ count }}</div>
<button @click="increment">+1</button>
</template>
3. computed:计算属性
Vue3 的computed是一个函数,用于定义基于响应式数据的计算属性。
示例
vue
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const price = ref(10)
const count = ref(2)
// 计算总价(响应式)
const total = computed(() => {
return price.value * count.value
})
return { price, count, total }
}
}
</script>
<template>
<div>单价:{{ price }}</div>
<div>数量:{{ count }}</div>
<div>总价:{{ total }}</div>
</template>
4. watch:监听数据变化
Vue3 的watch用于监听响应式数据的变化,执行自定义逻辑。
示例
vue
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')
const addCount = () => {
count.value++
}
const changeName = () => {
nickname.value = '李四'
}
// 1. 监视单个数据的变化
// watch(ref对象,(newValue, oldValue) => {...})
// watch(count,(newValue,oldValue) => {
// console.log(newValue,oldValue)
// })
// 2. 监听多个数据的变化
// watch([ref对象1,ref对象2....],(newArr,oldArr) => {...})
// watch([count, nickname], (newArr, oldArr) => {
// console.log(newArr, oldArr)
// })
// 3. immediate 立刻执行
watch(count,(newValue,oldValue) => {
console.log(newValue,oldValue)
}, {
immediate: true
})
// 4. deep 深度监视,默认 watch 进行的是 浅层监视
// const ref1 = ref(简单类型) 可以直接监视
// const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
const userInfo = ref({
name: 'zs',
age: 18
})
const changeUserInfo = () => {
// 修改了 userInfo.value 修改了对象的地址,才能监听到
// userInfo.value = { name: 'ls', age: 20 }
userInfo.value.age++
}
// 4. deep 深度监视,默认 watch 进行的是 浅层监视
// watch(userInfo,(newValue,oldValue) => {
// console.log(newValue,oldValue)
// },{
// deep: true
// })
// 5. 对于对象中的属性,进行监听
watch(() => userInfo.value.age, (newValue, oldValue) => {
console.log(newValue,oldValue)
})
</script>
<template>
<div>{{ count }}</div>
<button @click="addCount">+1</button>
<div>{{ nickname }}</div>
<button @click="changeName">改名字</button>
<div>{{ userInfo }}</div>
<button @click="changeUserInfo">修改</button>
</template>
5. 父子组件通信
(1)父传子:props
父组件通过属性传递数据,子组件通过defineProps接收(Vue3 的宏)。
父组件:
vue
<template>
<Child :msg="parentMsg" />
</template>
<script>
import Child from './Child.vue'
import { ref } from 'vue'
export default {
components: { Child },
setup() {
const parentMsg = ref('来自父组件的消息')
return { parentMsg }
}
}
</script>
子组件
vue
<template>
<div>{{ msg }}</div>
</template>
<script>
// 用defineProps接收父组件传递的props
const props = defineProps({
msg: {
type: String,
required: true
}
})
export default {
setup() {
// 可以在setup中通过props访问
console.log(props.msg)
}
}
</script>
(2)子传父:emit
子组件通过defineEmits定义事件,父组件监听事件接收数据。
子组件:
vue
<template>
<button @click="sendData">向父组件传值</button>
</template>
<script>
// 用defineEmits定义可触发的事件
const emit = defineEmits(['child-event'])
export default {
setup() {
const sendData = () => {
// 触发事件,传递数据
emit('child-event', '子组件的数据')
}
return { sendData }
}
}
</script>
父组件:
vue
<template>
<Child @child-event="handleChildEvent" />
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
setup() {
const handleChildEvent = (data) => {
console.log('子组件传递的数据:', data)
}
return { handleChildEvent }
}
}
</script>
6. 模板引用:defineExpose
Vue3 中,子组件的属性 / 方法默认不会暴露给父组件,需用defineExpose主动暴露。
子组件:
vue
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
// 暴露count和increment给父组件
defineExpose({ count, increment })
return { count, increment }
}
}
</script>
父组件:
vue
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script>
import Child from './Child.vue'
import { ref } from 'vue'
export default {
components: { Child },
setup() {
const childRef = ref(null)
const callChildMethod = () => {
// 访问子组件暴露的属性和方法
childRef.value.increment()
console.log(childRef.value.count)
}
return { childRef, callChildMethod }
}
}
</script>
7.provide 和 inject:跨组件通信
provide和inject用于跨层级组件通信(父组件提供数据,子孙组件注入数据)。
顶层组件:
vue
<script setup>
import CenterCom from './components/center-com.vue';
import {provide ,ref} from 'vue'
// 1. 跨层传递普通数据
provide('theme-color', 'pink')
// 2. 跨层传递响应式数据
const count = ref(100)
provide('count', count)
setTimeout(() => {
count.value = 500
}, 5000)
// 3. 跨层传递函数 => 给子孙后代传递可以修改数据的方法
provide('changeCount', (newCount) => {
count.value = newCount
})
</script>
<template>
<div><h1>我是顶层组件</h1>
<CenterCom></CenterCom>
</div>
</template>
中层组件
vue
<script setup>
import BottonCom from './botton-com.vue';
</script>
<template>
<div><h2>我是中层组件</h2>
<BottonCom></BottonCom>
</div>
</template>
底部组件
vue
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const clickFn = () => {
changeCount(1500)
}
</script>
<template>
<div><h3>我是底部组件---{{ themeColor }}------{{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>
注意:主要看顶层组件和底部组件,中层组件只是用来过渡的