系列文章目录
内容 | 链接 |
---|---|
从Vue2到Vue3【零】 | Vue3简介 |
从Vue2到Vue3【一】 | Composition API(第一章) |
从Vue2到Vue3【二】 | Composition API(第二章) |
从Vue2到Vue3【三】 | Composition API(第三章) |
从Vue2到Vue3【四】 | Composition API(第四章) |
前言
Vue3作为Vue.js框架的最新版本,引入了许多令人激动的新特性和改进。其中,组合式API是Vue3最引人注目的特性之一。在本文中,我们将深入探讨Vue3的组合式API,并探索其在开发过程中的优势和用法。无论是新手还是有经验的Vue开发者,通过学习如何使用组合式API,我们都能更高效地构建复杂的应用程序,提高代码的可维护性和重用性。
一、 composition API 与 options API
- vue2 采用的是 options API
(1) 优点:易于学习和使用, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)
(2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显
(3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护 - vue3 新增的是 composition API
(1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能 api 相关放到一起
(2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api
(3) 大大的提升了 代码可读性 和 可维护性
案例 鼠标移动显示鼠标坐标 x, y
vue2 options API 传统做法
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
export default {
// vue2 中采用的是 options API
// 常见的配置项: data created methods watch computed components
data() {
return {
mouse: {
x: 0,
y: 0,
},
count: 0,
}
},
mounted() {
document.addEventListener('mousemove', this.move)
},
methods: {
move(e) {
this.mouse.x = e.pageX
this.mouse.y = e.pageY
},
add() {
this.count++
},
},
destroyed() {
document.removeEventListener('mousemove', this.move)
},
}
</script>
vue3 composition API 组合api做法
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
export default {
setup() {
const count = ref(0)
const add = () => {
count.value++
}
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return {
count,
add,
mouse,
}
},
}
</script>
可以继续抽离,把实现逻辑的具体代码抽离封装起来,方便复用和维护
function useMouse() {
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return mouse
}
function useCount() {
const count = ref(0)
const add = () => {
count.value++
}
return {
count,
add,
}
}
把抽离封装好的函数放到src目录下的hooks文件夹
这样做有什么好处呢?
基于逻辑功能组织代码,方便复用与维护
二、setup
- Vue3.0中一个新的配置项,值为一个函数,是composition API的起点(舞台)
- 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
- setup 中不能使用 this, this 指向 undefined
- 在模版中需要使用的数据和函数,需要在 setup 中返回
- 组件中所用到的:数据、方法等等,均要配置在setup中
<template>
<div class="container">
<h1 @click="say()">{{msg}}</h1>
</div>
</template>
<script>
export default {
setup () {
console.log('setup执行了') //setup先执行
console.log(this) // undefined
// 定义数据和函数
const msg = 'hi vue3'
const say = () => {
console.log(msg)
}
// 需要使用的一定要导出
return { msg , say}
},
beforeCreate() {
console.log('beforeCreate执行了')
console.log(this)
}
}
</script>
-
注意点:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法
- 但在setup中不能访问到Vue2.x配置(data、methos、computed…)
- 如果有重名, setup优先
- setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
- 尽量不要与Vue2.x配置混用
三、ref
- 定义一个响应式数据(可以是基本类型、也可以是对象类型)
<template>
<div>{{ money }}</div>
<button @click="money++">改值</button>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
setup() {
let money = ref(100)
money.value++
return {
money
}
}
}
</script>
-
使用
-
操作数据 (xxx.value)
-
使用数据 ( {{xxx}}} )
-
-
基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 -
对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
四、reactive
- 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<button @click="obj.name = 'ls'">改值</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
// 1. setup 需要返回值, 返回的值才能在模板中使用
// 2. 默认的普通的值不是响应式的, 需要用 reactive 函数
const obj = reactive({
name: 'zs',
age: 18
})
return {
obj
}
}
}
</script>
-
使用
-
操作数据 (xxx)
-
使用数据 ( {{xxx}}} )
-
-
reactive定义的响应式数据是“深层次的”
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据
五、reactive与ref对比
使用这两者函数之前,一定要先引入
import {reactive, ref} from 'vue'
- 从定义数据角度对比:
- ref用来定义:基本类型数据
- reactive用来定义:对象(或数组)类型数据
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象
- 从原理角度对比:
- ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持) - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据:均不需要
.value
- ref定义的数据:操作数据需要
六、setup注意事项
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
- slots: 收到的插槽内容, 相当于
this.$slots
- emit: 分发自定义事件的函数, 相当于
this.$emit
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
父组件App
<template>
<Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
// 具名插槽
<template v-slot:qwe>
<span>尚硅谷</span>
</template>
<template v-slot:asd>
<span>尚硅谷</span>
</template>
</Demo>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components:{Demo},
setup(){
function showHelloMsg(value){
alert(`你好啊,你触发了hello事件,我收到的参数是:${value}!`)
}
return {
showHelloMsg
}
}
}
</script>
子组件Demo
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="test">测试触发一下Demo组件的Hello事件</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'Demo',
props:['msg','school'],
emits:['hello'],
setup(props,context){
// console.log('---setup---',props)
// console.log('---setup---',context)
// console.log('---setup---',context.attrs) //相当与Vue2中的$attrs
// console.log('---setup---',context.emit) //触发自定义事件的。
console.log('---setup---',context.slots) //插槽
//数据
let person = reactive({
name:'张三',
age:18
})
//方法
function test(){
context.emit('hello',666)
}
//返回一个对象(常用)
return {
person,
test
}
}
}
</script>
七、script setup语法
从vue3.2开始,支持setup语法糖。
script setup是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁
顶层的绑定会自动暴露给模板,所以定义的变量,函数和import导入的内容(包括引入的子组件)都可以直接在模板中直接使用
<template>
<div>
<h3>根组件</h3>
<div>点击次数:{{ count }}</div>
<button @click="add">点击修改</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const add = () => {
count.value++
}
// 不再需要return {} 直接可以读取数据
</script>
使用script setup 语法 改写鼠标坐标案例
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script setup>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
const count = ref(0)
const add = () => {
count.value++
}
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
</script>
八、计算属性与监听
8.1 computed
computed函数调用时, 要接收一个处理函数, 处理函数中, 需要返回计算属性的值
<template>
<div>我今年的年纪 <input type="text" v-model="age" /></div>
<div>我明年的年龄 {{ nextAge }}</div>
<div>我后年的年龄 <input type="text" v-model="nextAge2" /></div>
</template>
<script setup>
import { computed, ref } from 'vue'
const age = ref(10)
// 不带set的计算属性 (简写)
const nextAge = computed(() => {
return +age.value + 1 // 变量前的加号是转为number类型
})
// 带set的计算属性 (完整写法)
const nextAge2 = computed({
get() {
return +age.value + 2
},
set(value) {
age.value = value - 2
},
})
</script>
8.2 watch
watch监视, 接收三个参数
- 参数1: 监视的数据源
- 参数2: 回调函数
- 参数3: 额外的配置 (如立即侦听、深度侦听)
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性 属性值是简单数据类型
watch(()=>person.age,(newValue,oldValue)=>{
console.log('person的age变化了',newValue,oldValue)
},{immediate:true})
//情况五:监视reactive定义的响应式数据中的某些属性 属性值是简单数据类型
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true})
//特殊情况 监视reactive定义的响应式数据中的某些属性 属性值是引用数据类型
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是job对象,所以deep配置有效
当ref的值是一个复杂数据类型,需要深度监听
let person = ref({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//由于监听的ref的值是一个对象 所以要开启深度侦听
watch(person,(newValue,oldValue)=>{
console.log('person的值变化了',newValue,oldValue)
},{deep:true})
8.3 watchEffect
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性文章来源:https://www.toymoban.com/news/detail-572481.html
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
总结
总的来说,Vue3的组合式API为我们提供了一种更灵活、更强大的开发方式。通过充分理解和应用组合式API,我们可以开发出更高效、更精简的Vue应用程序,为用户提供更好的体验。不论是对于新手还是有经验的Vue开发者来说,掌握组合式API都是一个重要的技能,值得我们去学习和掌握。文章来源地址https://www.toymoban.com/news/detail-572481.html
到了这里,关于从Vue2到Vue3【一】——Composition API(第一章)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!