说明:Vue3.0中一个新的配置项,值为一个函数。
在Vue3中,我们使用setup函数来编写组件逻辑。setup函数是Vue3中的一个新概念,它是一个在组件实例创建之前被调用的函数。它接收两个参数:props和context。props是组件的属性,而context则是一些实用的工具和API。
<template>
<div>
<p>{{ message }}</p>
{{ say() }}
</div>
</template>
<script>
export default {
name: 'MyComponent',
setup(props) {
const message = 'Hello, Vue3!'
function say(){
console.log(`message ===== ${message}`)
}
return {
message,
say
}
}
}
</script>
在这个例子中,创建了message变量与一个say的方法。而后在setup函数中返回一个对象,该对象包含了我们希望在模板中使用的数据。在模板中,我们可以像使用普通的data属性一样使用setup返回的数据。使用setup函数的好处是它使得我们能够更好地组织和封装组件的逻辑。我们可以将组件的逻辑放在一个单独的函数中,使得代码更易于维护和测试。此外,setup函数还可以访问到一些实用的工具和API,例如:reactive、computed、watch等(这些将会在后面说到)。
ref 可以用来创建响应式的数据。它接收一个参数作为初始值,返回一个包含value属性的对象。这个value属性是一个响应式的数据,可以被用于模板中。
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment"> +1 </button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'MyComponent',
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
}
在这个例子中,我们使用ref函数创建了一个响应式的count变量,并将它作为组件的返回值。在模板中,我们可以像使用普通的变量一样使用count变量,它会自动更新视图。注意,在修改ref变量的值时,需要使用 value 属性。例如,在increment函数中,我们使用 count.value++ 来增加count变量的值。
ref函数还可以用于创建响应式的对象和数组。
const user = ref({
name: 'John',
age: 30
})
const list = ref([1, 2, 3])
总而言之,Vue3的ref函数是一个非常有用的API,它可以用于创建响应式的数据,使得组件的状态更易于管理和维护
Vue的ref是通过Proxy实现的响应式。在Vue3中,每个组件实例都有一个Proxy对象,用于拦截对组件数据的读取和修改。当我们使用ref创建一个响应式变量时,Vue会将这个变量包装成一个Proxy对象。这个Proxy对象会拦截对变量的读取和修改,以便在变量发生变化时,能够触发组件的重新渲染。具体来说,当我们读取ref变量的值时,Proxy会返回value属性的值。当我们修改ref变量的值时,Proxy会拦截这个修改,并将新的值赋给value属性。这个过程中,Vue会检测到ref变量的变化,并触发组件的重新渲染。而对于具体是如何实现响应式的可以参考该文:深入底层——浅谈Vue2和Vue3的响应式数据实现原理
reactive 可以用来创建响应式的数据对象。它接收一个普通的JavaScript对象作为参数,返回一个响应式的Proxy对象。这个Proxy对象会拦截对对象属性的读取和修改,以便在属性发生变化时,能够触发组件的重新渲染。
<template>
<div>
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<button @click="incrementAge">Age + 1</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'MyComponent',
setup() {
const user = reactive({
name: 'John',
age: 30
})
const incrementAge = () => {
user.age++
}
return {
user,
incrementAge
}
}
}
使用reactive函数创建了一个响应式的user对象,并将它作为组件的返回值。在模板中,我们可以像使用普通的对象一样使用user对象,它会自动更新视图。在修改reactive对象的属性时,我们不需要使用value属性。例如,在incrementAge函数中,我们使用user.age++来增加user对象的age属性的值。
ref和reactive都可以做到响应式的数据,那这两者之间有什么区别呢?
ref
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。reactive
Vue3 的计算属性与 Vue2 相似,但也有一些变化。在 Vue3 中,计算属性仍然是一个函数,但使用 computed 将其定义为计算属性时,需要使用 ref 函数包装计算属性的返回值。
import {computed} from 'vue'
// 单向响应式
person.fullName = computed(() => {
return person.start + ' ' + person.end
})
// 完整响应式
person.fullName = computed({
get() {
return person.start + ' ' + person.end
},
set(val) {
const arr = val.split(' ')
person.start = arr[0]
person.end = arr[1]
}
})
可以看到在Vue3当中的computed计算属性是加在setup当中,并且需要从vue当中解构引入这个计算属性
Vue3的watch和Vue2的watch相似,但是还是存在一点变化,需要从vue当中解构出watch,并且watch也是写在setup下的,直接看以下案例:
watch(sum, () => {
console.log('sum change')
}, {
immediate: true,
deep: true
})
watch([sum, msg], (newVal, oldVal) => {
console.log('change old =', oldVal," new =", newVal)
})
如果是reactive定义的数据,获取不到正确的oldVal
watch(person, (newVal, oldVal) => {
console.log('person change old =', oldVal, " new =", newVal)
}, {
// 强制开始了深度监视、配置deep无效
deep: false
})
watch(() => { return person.age }, (newVal, oldVal) => {
console.log('person age change old =', oldVal, " new =", newVal)
})
watch(() => { return [person.age, person.fullName] }, (newVal, oldVal) => {
console.log('person age change old =', oldVal, " new =", newVal)
})
watch(() => { return person.job }, (newVal, oldVal) => {
console.log('person job change old =', oldVal, " new =", newVal)
}, {
deep: true
})
需要从vue当中解构引入、watchEffect在回调当中使用了那些数据就对那些数据进行监听
watchEffect(() => {
console.log("watchEffect", sum.value)
})
Vue3中的自定义hook是通过使用函数式组件和Composition API实现的。自定义hook可以将重复的逻辑提取出来,使代码更加简洁和易于维护。要创建一个自定义hook,可以定义一个函数,该函数接受参数并返回一个对象或数组,该对象或数组可供组件使用。
filename usePoint.js
import { onBeforeUnmount, onMounted, reactive } from 'vue';
function useSavePoint() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(e) {
console.log(e)
point.x = e.clientX
point.y = e.clientY
}
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
export default useSavePoint
比如说,我们在一个系统当中需要实现一个功能,如上代码,即监听鼠标的单击事件,获取鼠标的当前位置,这个实现起来还是较为简单的,但是这个小功能其他的人也需要进行使用,我们就可以将这一段代码抽离出来作为一个hook,如上代码所示,使用一个usePoint.js进行保存,而如何使用这个之定义hook呢?如下:
<template>
<div>
x ==== >>>> {{ point.x }}
<br />
y ==== >>>> {{ point.y }}
</div>
</template>
<script>
import useSavePoint from './usePoint';
export default {
name: 'SelfHook',
setup() {
let point = useSavePoint()
return {
point
}
}
}
</script>
Vue3中的自定义hook可以让我们更好地重用逻辑和组合功能,使我们的代码更加简洁和可维护,就和vue2当中的minix混入比较像
创建一个 ref 对象,其value值指向另一个对象中的某个属性。
const name = toRef(person,'name')
在要将响应式对象中的某个属性单独提供给外部使用时可以使用toRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化事使用
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换时使用
当我们读取别的hook或者组件时不希望改变其内部值可以使用
reactive
生成的响应式对象转为普通对象。创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
<template>
<input v-model="keyword" />
<h3>{{ keyword }}</h3>
</template>
<script>
import { customRef } from 'vue';
export default {
name: 'selfRef',
setup() {
// let keyword = ref("1")
function myRef(value, delay) {
let timer
return customRef((track, trigger) => {
return {
get() {
console.log('自定义ref读取====>>>>>', value)
track()
return value
},
set(newValue) {
clearTimeout(timer)
timer = setTimeout(() => {
console.log('自定义ref修改====>>>>>', newValue)
value = newValue
trigger()
}, delay)
}
}
})
}
let keyword = myRef("1", 20)
return {
keyword
}
}
}
</script>
实现祖与后代组件间通信,父组件有一个 provide
选项来提供数据,后代组件有一个 inject
选项来开始使用这些数据,这个在vue2当中也有使用
祖先组件
provide('car', {
name: '奔驰'
})
孙(子代)组件
const car = inject('car')