内容 | 链接 |
---|---|
从Vue2到Vue3【零】 | Vue3简介及创建 |
从Vue2到Vue3【一】 | Composition API(第一章) |
Vue3作为Vue.js框架的最新版本,引入了许多令人激动的新特性和改进。其中,组合式API是Vue3最引人注目的特性之一。在本文中,我们将深入探讨Vue3的组合式API,并探索其在开发过程中的优势和用法。无论是新手还是有经验的Vue开发者,通过学习如何使用组合式API,我们都能更高效地构建复杂的应用程序,提高代码的可维护性和重用性。
案例 鼠标移动显示鼠标坐标 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文件夹
这样做有什么好处呢?
基于逻辑功能组织代码,方便复用与维护
<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>
注意点:
<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
函数。
<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 实现,通过代理对象操作源对象内部数据
使用这两者函数之前,一定要先引入
import {reactive, ref} from 'vue'
reactive
转为代理对象Object.defineProperty()
的get
与set
来实现响应式(数据劫持).value
,读取数据时模板中直接读取不需要.value
.value
this.$attrs
this.$slots
this.$emit
父组件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>
从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>
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>
watch监视, 接收三个参数
//情况一:监视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})
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
总的来说,Vue3的组合式API为我们提供了一种更灵活、更强大的开发方式。通过充分理解和应用组合式API,我们可以开发出更高效、更精简的Vue应用程序,为用户提供更好的体验。不论是对于新手还是有经验的Vue开发者来说,掌握组合式API都是一个重要的技能,值得我们去学习和掌握。