在前面我们讲解过计算属性computed
:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理
在前面的Options API中,我们是使用computed选项来完成的;
在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
如何使用computed呢?
getter
函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;get 和 set
的对象,返回一个可变的(可读写)ref 对象;const fullName = computed(() => {
return firstName.value + " " + lastName.value
})
const fullName = computed({
get: () => {
return firstName.value + " " + lastName.value
},
set: newValue => {
const names = newValue.split(" ")
firstName.value = names[0]
lastName.value = names[1]
}
})
在setup中如何使用ref获取元素或者组件?
<template>
<h2 ref="titleRef">我是标题h2>
<button ref="btnRef">按钮button>
<show-info ref="showInfoRef">show-info>
<button @click="getElements">获取元素button>
template>
<script>
import { ref, onMounted } from 'vue'
import ShowInfo from './ShowInfo.vue'
export default {
components: {
ShowInfo
},
setup() {
const titleRef = ref()
const btnRef = ref()
const showInfoRef = ref()
// mounted的生命周期函数
onMounted(() => {
console.log(titleRef.value)
console.log(btnRef.value)
console.log(showInfoRef.value)
showInfoRef.value.showInfoFoo()
})
function getElements() {
console.log(titleRef.value)
}
return {
titleRef,
btnRef,
showInfoRef,
getElements
}
}
}
script>
<style scoped>
style>
我们前面说过 setup 可以用来替代 data 、 methods 、 computed 等等这些选项,也可以替代生命周期钩子。
那么setup中如何使用生命周期函数呢?
<template>
<div>AppContentdiv>
template>
<script>
import { onMounted, onUpdated, onUnmounted } from 'vue'
export default {
beforeCreate() {
},
// created() {
// },
// beforeMount() {
// },
// mounted() {
// },
// beforeUpdate() {
// },
// updated() {
// }
setup() {
// 在执行setup函数的过程中, 你需要注册别的生命周期函数
onMounted(() => {
console.log("onmounted")
})
}
}
script>
<style scoped>
style>
事实上我们之前还学习过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。
我们可以通过 provide来提供数据:
provide可以传入两个参数:
let counter = 100
let info = {
name: 'www',
age: 10
}
provide("counter", counter)
provide("info", info)
在后代组件中可以通过 inject 来注入需要的属性和对应的值:
inject
来注入需要的内容;inject可以传入两个参数:
const counter = inject("counter")
const info = inject("info")
为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 和 reactive。
let counter = ref(100)
let info = reactive({
name: 'www',
age: 10
})
provide("counter", counter)
provide("info", info)
在前面的Options API中,我们可以通过watch选项来侦听data
或者props
的数据变化,当数据变化时执行某一些操作。
在Composition API中,我们可以使用watchEffect
和watch
来完成响应式数据的侦听;
watch的API完全等同于组件watch选项的Property:
侦听特定的数据源
,并且执行其回调函数;const name = ref('kobe')
watch(name, (newValue, oldValue) +> {
console.log(newValue, oldValue)
})
const changeName = () => {
name.value = "james"
}
侦听器还可以使用数组同时侦听多个源:
const name = ref('kobe')
const age = ref(10)
watch([name,age], (newValue, oldValue) +> {
console.log(newValue, oldValue)
})
const changeName = () => {
name.value = "james"
}
如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:
// 1.定义数据
const message = ref("Hello World")
const info = reactive({
name: "why",
age: 18,
friend: {
name: "kobe"
}
})
// 2.侦听数据的变化
watch(message, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
watch(info, (newValue, oldValue) => {
console.log(newValue, oldValue)
console.log(newValue === oldValue)
}, {
immediate: true
})
// 3.监听reactive数据变化后, 获取普通对象
watch(() => ({ ...info }), (newValue, oldValue) => {
console.log(newValue, oldValue)
}, {
immediate: true,
deep: true
})
当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect。
我们来看一个案例:
const name = ref("abc")
const age = ref(18)
watchEffect(() => {
console.log("watchEffect执行~")
})
如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数
,调用该函数即可。
比如在上面的案例中,我们age达到20的时候就停止侦听:
<template>
<div>
<h2>当前计数: {{ counter }}h2>
<button @click="counter++">+1button>
<button @click="name = 'kobe'">修改namebutton>
div>
template>
<script>
import { watchEffect, watch, ref } from 'vue'
export default {
setup() {
const counter = ref(0)
const name = ref("why")
// watch(counter, (newValue, oldValue) => {})
// 1.watchEffect传入的函数默认会直接被执行
// 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
const stopWatch = watchEffect(() => {
console.log("-------", counter.value, name.value)
// 判断counter.value > 10
if (counter.value >= 10) {
stopWatch()
}
})
return {
counter,
name
}
}
}
script>
<style scoped>
style>
我们先来对之前的counter逻辑进行抽取:
import { ref, onMounted } from 'vue'
export default function useCounter() {
const counter = ref(0)
function increment() {
counter.value++
}
function decrement() {
counter.value--
}
onMounted(() => {
setTimeout(() => {
counter.value = 989
}, 1000);
})
return {
counter,
increment,
decrement
}
}
我们编写一个修改title的Hook:
import { ref, watch } from "vue";
export default function useTitle(titleValue) {
// document.title = title
// 定义ref的引入数据
const title = ref(titleValue)
// 监听title的改变
watch(title, (newValue) => {
document.title = newValue
}, {
immediate: true
})
// 返回ref值
return {
title
}
}
我们来完成一个监听界面滚动位置的Hook:
import { reactive } from 'vue'
export default function useScrollPosition() {
// 1.使用reative记录位置
const scrollPosition = reactive({
x: 0,
y: 0
})
// 2.监听滚动
document.addEventListener("scroll", () => {
scrollPosition.x = window.scrollX
scrollPosition.y = window.scrollY
})
return {
scrollPosition
}
}
App.vue
<template>
<div>AppContentdiv>
<button @click="changeTitle">修改titlebutton>
<button @click="currentPage = 'home'">homebutton>
<button @click="currentPage = 'about'">aboutbutton>
<component :is="currentPage">component>
<div class="content">div>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
template>
<script>
import { ref } from 'vue'
import Home from './views/Home.vue'
import About from './views/About.vue'
import useTitle from './hooks/useTitle'
export default {
components: {
Home,
About
},
setup() {
const currentPage = ref("home")
function changeTitle() {
useTitle("app title")
}
return {
changeTitle,
currentPage
}
}
}
script>
<style scoped>
.content {
width: 3000px;
height: 100px;
background-color: orange;
}
style>
views/About.vue
<template>
<h2>About计数: {{ counter }}h2>
<button @click="increment">+1button>
<button @clcik="decrement">-1button>
template>
<script>
import { onActivated } from 'vue'
import useCounter from '../hooks/useCounter'
import useTitle from '../hooks/useTitle'
export default {
setup() {
// 切换标题
useTitle("关于")
return {
...useCounter()
}
}
}
script>
<style scoped>
style>
views/Home.vue
<template>
<h2>Home计数: {{ counter }}h2>
<button @click="increment">+1button>
<button @click="decrement">-1button>
<button @click="popularClick">首页-流行button>
<button @click="hotClick">首页-热门button>
<button @click="songClick">首页-歌单button>
<div class="scroll">
<h2>x: {{ scrollPosition.x }}h2>
<h2>y: {{ scrollPosition.y }}h2>
div>
template>
<script>
import { onMounted, ref } from 'vue'
import useCounter from '../hooks/useCounter'
import useTitle from '../hooks/useTitle'
import useScrollPosition from '../hooks/useScrollPosition'
export default {
setup() {
// 1.counter逻辑
const { counter, increment, decrement } = useCounter()
// 2.修改标题
const { title } = useTitle("首页")
// 3.监听按钮的点击
function popularClick() {
title.value = "首页-流行"
}
function hotClick() {
title.value = "首页-热门"
}
function songClick() {
title.value = "首页-歌单"
}
// 4.获取滚动位置
const { scrollPosition } = useScrollPosition()
console.log(scrollPosition)
return {
counter,
increment,
decrement,
popularClick,
hotClick,
songClick,
scrollPosition
}
}
}
script>
<style scoped>
style>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,当同时使用 SFC 与组合式 API 时则
推荐该语法
。
使用这个语法,需要将 setup attribute 添加到 代码块上:
<script setup>
console.log(11)
script>
里面的代码会被编译成组件 setup() 函数的内容:
只在组件被首次引入的时候执行一次不同;
中的代码会在每次组件实例被创建的时候执行。当使用 的时候,任何在
声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用:
<script setup>
const message = ref(111)
script>
<template>
message: {{ message }}
template>
响应式数据需要通过ref、reactive来创建。
范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import ShowInfo from './ShowInfo.vue'
script>
<template>
<ShowInfo>ShowInfo>
template>
为了在声明 props 和 emits 选项时获得完整的类型推断支持,我们可以使用 defineProps
和 defineEmits
API,它们将自动地在 可用
<template>
<div>ShowInfo: {{ name }}-{{ age }}div>
<button @click="showInfoBtnClick">showInfoButtonbutton>
template>
<script setup>
// 定义props
const props = defineProps({
name: {
type: String,
default: "默认值"
},
age: {
type: Number,
default: 0
}
})
// 绑定函数, 并且发出事件
const emits = defineEmits(["infoBtnClick"])
function showInfoBtnClick() {
emits("infoBtnClick", "showInfo内部发生了点击")
}
// 定义foo的函数
function foo() {
console.log("foo function")
}
defineExpose({
foo
})
script>
<style scoped>
style>
使用 的组件是默认关闭的:
中声明的绑定;通过 defineExpose 编译器宏来显式指定在 组件中要暴露出去的 property:
ShowInfo.vue
function foo() {
log("foo function")
}
defineExpose({
foo
})
App.vue
const showInfoRef = ref(null)
function callShowInfoRef() {
showInfoRef.value.foo()
}