Vue3组合式API体验
通过一个 Counter(自增)案例 体验Vue3新引入的组合式API
Vue2的代码:
<template>
<button @click="addCount">{{count}}</button>
</template>
<script>
export default {
data(){
return {
count: 0
}
},
methods: {
addCount(){
this.count++
}
}
}
</script>
Vue3组合式API实现的代码:
<template>
<button @click="addCount">{{count}}</button>
</template>
<script setup>
import {ref} from 'vue'
const count = ref(0)
const addCount = () => count.value++
</script>
Vue3更多的优势
- 更容易维护
- 组合式API
- 更好的TypeScript支持
- 更快的速度
- 重写diff算法
- 模板编译优化
- 更高效的组件初始化
- 更小的体积
- 良好的TreeShaking
- 按需引入
- 更优的数据响应式
- Proxy
认识create-vue
create-vue是Vue官方新的脚手架工具,底层切换到了vite(下一代前端工具链),为开发提供极速响应
- 前提环境条件
已安装16.0或更高版本的Node.js - 创建一个Vue应用
npm init vue@latest
这一指令将会安装并执行 create-vue
熟悉项目目录和关键文件
关键文件:
- vite.config.js - 项目的配置文件 基于vite的配置
- package.json - 项目包文件 核心依赖项变成了Vue3.x 和 vite
- main.js - 入口文件 createApp函数创建应用实例
- app.vue - 根组件 SFC单文件组件 script - template - style
变化一:脚本script和模板template顺序调整
变化二:模板template不再要求唯一根元素
变化三:脚本script添加setup标识支持组合式API - index.html - 单页入口 提供id为app的挂载点
组合式API - setup选项
setup选项的写法和执行
<script>
export default {
setup(){
},
beforeCreate(){
},
}
</script>
< script setup >语法糖
- 原始复杂写法
<script>
export default {
setup(){
//数据
const message = 'this is message'
//函数
const logMessage = () => {
console.log(message)
}
return {
message,
logMessage
}
}
}
</script>
- 语法糖写法
<script setup>
//数据
const message = 'this is message'
//函数
const logMessage = () => {
console.log(message)
}
</script>
总结:
- setup选项的执行机制?
beforeCreate钩子之前 自动执行 - setup写代码的特点是什么?
定义数据 + 函数 然后以对象方式return - < script setup >解决了什么问题?
经过语法糖的封装更简单的使用组合式API - setup中this还指向组件实例吗?
指向undefined
组合式API - reactive和ref函数
- reactive()
作用:接受对象类型数据的参数传入
并返回一个响应式对象
- 核心步骤:
<script setup>
//导入
import {reactive} from 'vue'
//执行函数 传入参数 变量接收
const state = reactive(对象类型数据)
</script>
- 从vue包中
导入reactive函数
- 在< script setup> 中
执行reactive 函数
并传入类型为对象
的初始值,并使用变量接收返回值
案例:
<script setup>
//1. 导入函数
import {reactive} from 'vue'
//2. 执行函数 传入一个对象类型的参数 变量接收
const state = reactive({
count: 0
})
const setCount = () => {
state.count++
}
</script>
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
</div>
</template>
- ref()
作用:接收简单类型或者对象类型的数据
传入并返回一个响应式的对象
核心步骤:
<script setup>
//导入
import {ref} from 'vue'
//执行函数 传入参数 变量接收
const count = ref(简单类型或者复杂类型数据)
</script>
- 从vue包中
导入ref函数
- 在< script setup >中
执行ref函数并传入
初始值,使用变量
接收ref函数的返回值
总结:
(1) reactive和ref函数的共同作用是什么?
用函数调用的方式生成响应式数据
(2)reactive vs ref?
- reactive不能处理简单类型的数据
- ref参数类型支持更好但是必须通过.value访问修改
- ref函数的内部实现依赖于reactive函数
(3)在实际工作中推荐使用哪个?
推荐使用ref函数,更加灵活
computed计算属性函数
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
核心步骤:
导入
computed函数执行函数
在回调参数中return基于响应式数据做计算的值
,用变量接收
计算属性小案例:
<script setup>
// 原始响应式数组
import {ref} from 'vue'
// 1.导入computed函数
import {computed} from 'vue'
const list = ref([1, 2, 3, 4, 5, 6, 7, 8])
// 2.执行函数 return 计算之后的值 变量接收
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
setTimeout(() => {
list.value.push(9, 10)
}, 3000)
</script>
<template>
<div>
原始响应数组 - {{ list }}
</div>
<div>
计算属性数组 - {{ computedList }}
</div>
</template>
- 计算属性中不应该有“副作用”(异步请求/修改dom)
- 避免直接修改计算属性的值(计算属性应该是只读的)
watch函数
作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
两个额外的参数:1. immediate(立即执行)2. deep(深度侦听)
- 基础使用 - 侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const setCount = ()=>{
count.value++
}
// ref对象不需要加.value
watch(count, (newVal, oldVal)=>{
})
</script>
<template>
<div>
<button @click="setCount">+{{ count }}</button>
</div>
</template>
- 基础使用 - 侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
<script setup>
// 侦听多个数据变化
import {ref, watch} from 'vue'
const count = ref(0)
const changeCount = ()=>{
count.value++
}
const name = ref('cp')
const changeName = ()=>{
name.value = 'pc'
}
watch([count, name], ([newCount, newName], [oldCount, oldName])=>{
console.log('count或者name变化了', [newCount, newName], [oldCount, oldName]);
})
</script>
<template>
<div>
<button @click="changeCount">修改count--{{ count }}</button>
</div>
<div>
<button @click="changeName">修改name--{{ name }}</button>
</div>
</template>
- immediate
说明:在侦听器创建时立即触发回调
,响应式数据变化之后继续执行回调
const count = ref(0)
watch(count, () => {
console.log('count发生了变化')},
{immediate: true})
- deep
默认机制:通过watch监听的ref对象默认是浅层侦听
的,直接修改嵌套的对象属性不会触发回调执行
,需要开启deep选项
<script setup>
import {ref, watch} from 'vue'
const state = ref({count: 0})
const changeStateByCount = () => {
//直接修改count
state.value.count++
}
//watch深度监听
watch(state, ()=>{
console.log('count变化了')
}, {
deep: true
})
</script>
<template>
<div>
{{state.count}}
<button @click="changeStateByCount">通过count修改</button>
</div>
</templete>
- 精确侦听对象的某个属性
需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
总结:
- 作为watch函数的第一个参数,ref对象需要添加.value吗?
不需要,watch会自动读取 - watch只能侦听单个数据吗?
单个或者多个 - 不开启deep,直接修改嵌套属性能触发回调吗?
不能,默认是浅层侦听 - 不开启deep,想在某个层次比较深的属性变化时执行回调怎么做?
可以把第一个参数写成函数的写法,返回要监听的具体属性
组合式API - 生命周期函数
Vue3的生命周期(选项式 VS 组合式)
选项式API | 组合式API |
---|---|
beforeCreate/created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
生命周期函数的基本使用
- 导入生命周期函数
- 执行生命周期函数 传入回调
执行多次
生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行
<script setup>
// 生命周期函数的使用
// 1.引入函数
import {onMounted} from 'vue'
// 2.执行函数 传入回调
onMounted(()=>{
console.log('组件挂载完毕mounted执行了1')
})
onMounted(()=>{
console.log('组件挂载完毕mounted执行了2')
})
</script>
<template>
<div>
</div>
</template>
- 组合式API中生命周期函数的格式是什么?
on+生命周期名字 - 组合式API中可以使用onCreated吗?
没有这个钩子函数,直接写到setup中 - 组合式API中组件卸载完毕时执行哪个函数?
onUnmounted
组合式API - 父子通信
组合式API下的父传子
基本思想
- 父组件中给子组件绑定属性
- 子组件内部通过props选项接收
父:
<script setup>
// setup语法糖下局部组件无需注册直接可以使用
import SonCom from './son-com.vue'
</script>
<template>
<div class="father">
<h2>父组件App</h2>
<!-- 1.绑定属性 -->
<SonCom message="father message"/>
</div>
</template>
<style scoped>
</style>
子:
<script setup>
// 2.defineProps接收数据
const props = defineProps({
message: String
})
console.log(props);
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<div>
父组件传入的数据 - {{ message }}
</div>
</div>
</template>
<style scoped>
</style>
组合式API下的子传父
基本思想
- 父组件中给
子组件标签通过 @ 绑定事件
- 子组件内部通过
$emit 方法触发事件
父:
<script setup>
// 引入子组件
import sonComVue from './son-com.vue'
const getMessage = (msg) => {
console.log(msg);
}
</script>
<template>
<sonComVue @get-message="getMessage"/>
</template>
子:
<script setup>
//2. 通过defineEmits编译器宏生成emit方法
const emit = defineEmits(['get-message'])
const sendMsg = () => {
//3. 触发自定义事件 并传递参数
emit('get-message', 'this is son msg')
}
</script>
<template>
<button @click="sendMsg">sendMsg</button>
</template>
父传子
- 子传父的过程中通过什么方式接收props?
defineProps({属性名:类型})
- setup语法糖中如何使用父组件传过来的数据?
const props = defineProps({属性名:类型})
子传父
- 子传父的过程中通过什么方式得到emit方法
defineEmits(['事件名称'])
组合式API - 模板引用
模板引用的概念:通过ref标识
获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理)
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
<script setup>
import {onMounted, ref} from 'vue'
import TestCom from './test-com.vue'
//1. 调用ref函数 -> ref对象
const h1Ref = ref(null)
const comRef = ref(null)
//组件挂载完毕之后才能获取
onMounted(() => {
console.log(h1Ref.value);
console.log(comRef.value);
})
</script>
<template>
<!-- //2. 通过ref标识绑定ref对象 -->
<h1 ref="h1Ref">我是dom标签h1</h1>
<TestCom ref="comRef"/>
</template>
defineExpose()
默认情况下在< script setup>语法糖下组件内部的属性和方法是不开放
给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问
<script setup>
import {ref} from 'vue'
const testMessage = ref('this is test msg')
</script>
<script setup>
import {ref} from 'vue'
const testMessage = ref('this is test msg')
defineExpose({
testMessage
})
</script>
- 获取模板引用的时机是什么?
组件挂载完毕 - defineExpose编译宏的作用是什么?
显示暴露组件内部的属性和方法