赞
踩
在前面我们讲解过计算属性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> <!-- 1.获取元素 --> <h2 ref="titleRef">我是标题</h2> <button ref="btnRef">按钮</button> <!-- 2.获取组件实例 --> <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>AppContent</div> </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++">+1</button> <button @click="name = 'kobe'">修改name</button> </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>AppContent</div> <button @click="changeTitle">修改title</button> <!-- 1.计数器 --> <!-- <hr> <home></home> <hr> <about></about> --> <!-- 2.home和about页面的切换 --> <button @click="currentPage = 'home'">home</button> <button @click="currentPage = 'about'">about</button> <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">+1</button> <button @clcik="decrement">-1</button> </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">+1</button> <button @click="decrement">-1</button> <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>
<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,当同时使用 SFC 与组合式 API 时则推荐该语法。
使用这个语法,需要将 setup attribute 添加到 <script> 代码块上:
<script setup>
console.log(11)
</script>
里面的代码会被编译成组件 setup() 函数的内容:
<script> 只在组件被首次引入的时候执行一次不同;<script setup> 中的代码会在每次组件实例被创建的时候执行。当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用:
<script setup>
const message = ref(111)
</script>
<template>
message: {{ message }}
</template>
响应式数据需要通过ref、reactive来创建。
<script setup>范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import ShowInfo from './ShowInfo.vue'
</script>
<template>
<ShowInfo></ShowInfo>
</template>
为了在声明 props 和 emits 选项时获得完整的类型推断支持,我们可以使用 defineProps 和 defineEmits API,它们将自动地在 <script setup>可用
<template> <div>ShowInfo: {{ name }}-{{ age }}</div> <button @click="showInfoBtnClick">showInfoButton</button> </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>
使用 <script setup> 的组件是默认关闭的:
<script setup> 中声明的绑定;通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的 property:
ShowInfo.vue
function foo() {
log("foo function")
}
defineExpose({
foo
})
App.vue
const showInfoRef = ref(null)
function callShowInfoRef() {
showInfoRef.value.foo()
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。