小言_互联网的博客

Vue3 – Composition API(二)

402人阅读  评论(0)

1、computed函数使用

1.1、computed

        在前面我们讲解过计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理

  • 在前面的Options API中,我们是使用computed选项来完成的;
  • 在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;

如何使用computed呢?

  • 方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;
  • 方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;

1.2、示例 

App.vue


  
  1. <template>
  2. <h2>{{ fullname }} </h2>
  3. <button @click="setFullname">设置fullname </button>
  4. <h2>{{ scoreLevel }} </h2>
  5. </template>
  6. <script>
  7. import {reactive, computed, ref} from 'vue'
  8. export default {
  9. setup( ) {
  10. // 1.定义数据
  11. const names = reactive({
  12. firstName: "kobe",
  13. lastName: "bryant"
  14. })
  15. // const fullname = computed(() => {
  16. // return names.firstName + " " + names.lastName
  17. // })
  18. const fullname = computed({
  19. set: function ( newValue) {
  20. const tempNames = newValue. split( " ")
  21. names. firstName = tempNames[ 0]
  22. names. lastName = tempNames[ 1]
  23. },
  24. get: function ( ) {
  25. return names. firstName + " " + names. lastName
  26. }
  27. })
  28. console. log(fullname) // 是一个ref对象
  29. function setFullname( ) {
  30. fullname. value = "coder why"
  31. console. log(names)
  32. }
  33. // 2.定义score
  34. const score = ref( 89)
  35. const scoreLevel = computed( () => {
  36. return score. value >= 60 ? "及格" : "不及格"
  37. })
  38. return {
  39. names,
  40. fullname,
  41. setFullname,
  42. scoreLevel
  43. }
  44. }
  45. }
  46. </script>
  47. <style scoped>
  48. </style>

2、setup中使用ref

在setup中如何使用ref获取元素或者组件?

  • 其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;

示例: 

ShowInfo.vue


  
  1. <template>
  2. <div>ShowInfo </div>
  3. </template>
  4. <script>
  5. export default {
  6. // methods: {
  7. // showInfoFoo() {
  8. // console.log("showInfo foo function")
  9. // }
  10. // }
  11. setup( ) {
  12. function showInfoFoo( ) {
  13. console. log( "showInfo foo function")
  14. }
  15. return {
  16. showInfoFoo
  17. }
  18. }
  19. }
  20. </script>
  21. <style scoped>
  22. </style>

App.vue


  
  1. <template>
  2. <!-- 1.获取元素 -->
  3. <h2 ref="titleRef">我是标题 </h2>
  4. <button ref="btnRef">按钮 </button>
  5. <!-- 2.获取组件实例 -->
  6. <show-info ref="showInfoRef"> </show-info>
  7. <button @click="getElements">获取元素 </button>
  8. </template>
  9. <script>
  10. import { ref, onMounted } from 'vue'
  11. import ShowInfo from './ShowInfo.vue'
  12. export default {
  13. components: {
  14. ShowInfo
  15. },
  16. setup( ) {
  17. const titleRef = ref()
  18. const btnRef = ref()
  19. const showInfoRef = ref()
  20. // mounted的生命周期函数
  21. onMounted( () => {
  22. console. log(titleRef. value)
  23. console. log(btnRef. value)
  24. console. log(showInfoRef. value)
  25. showInfoRef. value. showInfoFoo()
  26. })
  27. function getElements( ) {
  28. console. log(titleRef. value)
  29. }
  30. return {
  31. titleRef,
  32. btnRef,
  33. showInfoRef,
  34. getElements
  35. }
  36. }
  37. }
  38. </script>
  39. <style scoped>
  40. </style>

3、组件的生命周期函数

3.1、生命周期钩子

  • 我们前面说过 setup 可以用来替代 data 、 methods 、 computed  等等这些选项,也可以替代 生命周期钩子
  • 那么setup中如何使用生命周期函数呢?
    • 可以使用直接导入的 onX 函数注册生命周期钩子;

选项式 API

Hook inside setup

beforecreate

created

beforeMount

onBeforeMount

mounted

onMounted

beforeupdate

onBeforeupdate

updated

onUpdated

beforeUnmount

onBeforeUnmount

unmounted

onUnmounted

activated

onActivated

deactivated

onDeactivated

Tip:因为 setup 是围绕 beforeCreate created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中缩写的任何代码都应该直接在 setup 函数中编写。

3.2、示例 

App.vue


  
  1. <template>
  2. <div>AppContent </div>
  3. </template>
  4. <script>
  5. import {onMounted, onUpdated, onUnmounted} from 'vue'
  6. export default {
  7. beforeCreate( ) {
  8. },
  9. // created() {
  10. // },
  11. // beforeMount() {
  12. // },
  13. // mounted() {
  14. // },
  15. // beforeUpdate() {
  16. // },
  17. // updated() {
  18. // }
  19. setup( ) {
  20. // 在执行setup函数的过程中, 你需要注册别的生命周期函数
  21. onMounted( () => {
  22. console. log( "onmounted")
  23. })
  24. }
  25. }
  26. </script>
  27. <style scoped>
  28. </style>

4、Provide/Inject使用

4.1、Provide函数

  • 事实上我们之前还学习过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。
  • 我们可以通过 provide来提供数据:
    • 可以通过 provide 方法来定义每个 Property;
  • provide可以传入两个参数:
    • name提供的属性名称
    • value提供的属性值

4.2、Inject函数 

  • 后代组件 中可以通过 inject 来注入需要的属性和对应的值:
  • 可以通过 inject 来注入需要的内容;
  • inject可以传入两个参数
    • 要 inject 的 property 的 name;
    • 默认值;

4.3、数据的响应式 

为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 和 reactive。

4.4、示例 

ShowInfo.vue


  
  1. <template>
  2. <div>ShowInfo: {{ name }}-{{ age }}-{{ height }} </div>
  3. </template>
  4. <script>
  5. import {inject} from 'vue'
  6. export default {
  7. // inject的options api注入, 那么依然需要手动来解包
  8. // inject: ["name", "age"],
  9. setup( ) {
  10. const name = inject( "name")
  11. const age = inject( "age")
  12. const height = inject( "height", 1.88)
  13. return {
  14. name,
  15. age,
  16. height
  17. }
  18. }
  19. }
  20. </script>
  21. <style scoped>
  22. </style>

App.vue


  
  1. <template>
  2. <div>AppContent: {{ name }} </div>
  3. <button @click="name = 'kobe'">app btn </button>
  4. <show-info> </show-info>
  5. </template>
  6. <script>
  7. import { provide, ref } from 'vue'
  8. import ShowInfo from './ShowInfo.vue'
  9. export default {
  10. components: {
  11. ShowInfo
  12. },
  13. setup( ) {
  14. const name = ref( "why")
  15. provide( "name", name)
  16. provide( "age", 18)
  17. return {
  18. name
  19. }
  20. }
  21. }
  22. </script>
  23. <style scoped>
  24. </style>

5、watch/watchEffect

5.1、侦听数据的变化

  • 在前面的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行某一些操作。
  • 在Composition API中,我们可以使用watchEffect和watch来完成响应式数据的侦听;
    • watchEffect:用于自动收集响应式数据的依赖;
    • watch:需要手动指定侦听的数据源;

5.2、Watch的使用

watch的API完全等同于组件watch选项的Property:

  • watch需要侦听特定的数据源,并且执行其回调函数;
  • 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;

5.3、侦听多个数据源 

侦听器还可以使用数组同时侦听多个源

5.4、watch的选项 

        如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:也可以传入 immediate 立即执行;

5.5、watchEffect 

  • 当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect
  • 我们来看一个案例:
    • 首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
    • 其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;

5.6、watchEffect的停止侦听 

  • 如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。
  • 比如在上面的案例中,我们age达到20的时候就停止侦听:

5.7、示例 

App-watch.vue


  
  1. <template>
  2. <div>AppContent </div>
  3. <button @click="message = '你好啊,李银河!'">修改message </button>
  4. <button @click="info.friend.name = 'james'">修改info </button>
  5. </template>
  6. <script>
  7. import {reactive, ref, watch} from 'vue'
  8. export default {
  9. setup( ) {
  10. // 1.定义数据
  11. const message = ref( "Hello World")
  12. const info = reactive({
  13. name: "why",
  14. age: 18,
  15. friend: {
  16. name: "kobe"
  17. }
  18. })
  19. // 2.侦听数据的变化
  20. watch(message, (newValue, oldValue) => {
  21. console. log(newValue, oldValue)
  22. })
  23. watch(info, (newValue, oldValue) => {
  24. console. log(newValue, oldValue)
  25. console. log(newValue === oldValue) // true,两者为同一个对象(浅拷贝)
  26. }, {
  27. // 这个属性作用就是,加载后默认就会执行一次这个console.log回调方法
  28. immediate: true
  29. })
  30. // 3.监听reactive数据变化后, 获取普通对象
  31. watch( () => ({...info}), (newValue, oldValue) => {
  32. console. log(newValue, oldValue)
  33. }, {
  34. immediate: true,
  35. deep: true
  36. })
  37. return {
  38. message,
  39. info
  40. }
  41. }
  42. }
  43. </script>
  44. <style scoped>
  45. </style>

App.vue


  
  1. <template>
  2. <div>
  3. <h2>当前计数: {{ counter }} </h2>
  4. <button @click="counter++">+1 </button>
  5. <button @click="name = 'kobe'">修改name </button>
  6. </div>
  7. </template>
  8. <script>
  9. import { watchEffect, watch, ref } from 'vue'
  10. export default {
  11. setup( ) {
  12. const counter = ref( 0)
  13. const name = ref( "why")
  14. // watch(counter, (newValue, oldValue) => {})
  15. // 1.watchEffect传入的函数默认会直接被执行
  16. // 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
  17. const stopWatch = watchEffect( () => {
  18. console. log( "-------", counter. value, name. value)
  19. // 判断counter.value > 10
  20. if (counter. value >= 10) {
  21. // 停止监听
  22. stopWatch()
  23. }
  24. })
  25. return {
  26. counter,
  27. name
  28. }
  29. }
  30. }
  31. </script>
  32. <style scoped>
  33. </style>

6、script setup语法糖

6.1、script setup语法

  • <script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,当同时使用 SFC 与组合式 API 时则推荐该语法。
    • 更少的样板内容,更简洁的代码;
    • 能够使用纯 Typescript 声明 prop 和抛出事件;
    • 更好的运行时性能 ;
    • 更好的 IDE 类型推断性能 ;
  • 使用这个语法,需要将 setup attribute 添加到 <script> 代码块上

  • 里面的代码会被编译成组件 setup() 函数的内容:
    • 这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同;
    • <script setup> 中的代码会在每次组件实例被创建的时候执行。 

6.2、顶层的绑定会被暴露给模板

        当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容)都能在模板中直接使用:

响应式数据需要通过ref、reactive来创建。 

6.3、导入的组件直接使用

<script setup> 范围里的值也能被直接作为自定义组件的标签名使用:

6.4、defineProps() 和 defineEmits() 

        为了在声明 props 和 emits 选项时获得完整的类型推断支持,我们可以使用 defineProps 和 defineEmits API,它们将自动地在 <script setup> 中可用:

6.5、defineExpose() 

  • 使用 <script setup> 的组件是默认关闭的
    • 通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定;
  • 通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的 property: 

6.6、示例 

ShowInfo.vue


  
  1. <template>
  2. <div>ShowInfo: {{ name }}-{{ age }} </div>
  3. <button @click="showInfoBtnClick">showInfoButton </button>
  4. </template>
  5. <script setup>
  6. // 定义props
  7. const props = defineProps({
  8. name: {
  9. type: String,
  10. default: "默认值"
  11. },
  12. age: {
  13. type: Number,
  14. default: 0
  15. }
  16. })
  17. // 绑定函数, 并且发出事件
  18. const emits = defineEmits([ "infoBtnClick"])
  19. function showInfoBtnClick( ) {
  20. emits( "infoBtnClick", "showInfo内部发生了点击")
  21. }
  22. // 定义foo的函数
  23. function foo( ) {
  24. console. log( "foo function")
  25. }
  26. // 暴露实例
  27. defineExpose({
  28. foo
  29. })
  30. </script>
  31. <style scoped>
  32. </style>

App.vue


  
  1. <template>
  2. <div>AppContent: {{ message }} </div>
  3. <button @click="changeMessage">修改message </button>
  4. <show-info name="why"
  5. :age= "18"
  6. @ info-btn-click= "infoBtnClick"
  7. ref= "showInfoRef">
  8. </show-info>
  9. <show-info> </show-info>
  10. <show-info> </show-info>
  11. </template>
  12. <script setup>
  13. // 1.所有编写在顶层中的代码, 都是默认暴露给template可以使用
  14. import {ref, onMounted} from 'vue'
  15. // 组件不在需要注册,直接导入使用即可
  16. import ShowInfo from './ShowInfo.vue'
  17. // 2.定义响应式数据
  18. const message = ref( "Hello World")
  19. console. log(message. value)
  20. // 3.定义绑定的函数
  21. function changeMessage( ) {
  22. message. value = "你好啊, 李银河!"
  23. }
  24. function infoBtnClick( payload) {
  25. console. log( "监听到showInfo内部的点击:", payload)
  26. }
  27. // 4.获取组件实例
  28. const showInfoRef = ref()
  29. onMounted( () => {
  30. showInfoRef. value. foo()
  31. })
  32. </script>
  33. <style scoped>
  34. </style>

7、自定义Hook练习

useCounter.js


  
  1. import { ref, onMounted } from 'vue'
  2. export default function useCounter( ) {
  3. const counter = ref( 0)
  4. function increment( ) {
  5. counter. value++
  6. }
  7. function decrement( ) {
  8. counter. value--
  9. }
  10. onMounted( () => {
  11. setTimeout( () => {
  12. counter. value = 989
  13. }, 1000);
  14. })
  15. return {
  16. counter,
  17. increment,
  18. decrement
  19. }
  20. }

useScrollPosition.js


  
  1. import { reactive } from 'vue'
  2. export default function useScrollPosition( ) {
  3. // 1.使用reative记录位置
  4. const scrollPosition = reactive({
  5. x: 0,
  6. y: 0
  7. })
  8. // 2.监听滚动
  9. document. addEventListener( "scroll", () => {
  10. scrollPosition. x = window. scrollX
  11. scrollPosition. y = window. scrollY
  12. })
  13. return {
  14. scrollPosition
  15. }
  16. }

useTitle.js


  
  1. import { ref, watch } from "vue";
  2. export default function useTitle( titleValue) {
  3. // document.title = title
  4. // 定义ref的引入数据
  5. const title = ref(titleValue)
  6. // 监听title的改变
  7. watch(title, (newValue) => {
  8. document. title = newValue
  9. }, {
  10. immediate: true
  11. })
  12. // 返回ref值
  13. return {
  14. title
  15. }
  16. }

About.vue


  
  1. <template>
  2. <h2>About计数: {{ counter }} </h2>
  3. <button @click="increment">+1 </button>
  4. <button @clcik="decrement">-1 </button>
  5. </template>
  6. <script>
  7. import { onActivated } from 'vue'
  8. import useCounter from '../hooks/useCounter'
  9. import useTitle from '../hooks/useTitle'
  10. export default {
  11. setup( ) {
  12. // 切换标题
  13. useTitle( "关于")
  14. return {
  15. ... useCounter()
  16. }
  17. }
  18. }
  19. </script>
  20. <style scoped>
  21. </style>

Home.vue


  
  1. <template>
  2. <h2>Home计数: {{ counter }} </h2>
  3. <button @click="increment">+1 </button>
  4. <button @click="decrement">-1 </button>
  5. <button @click="popularClick">首页-流行 </button>
  6. <button @click="hotClick">首页-热门 </button>
  7. <button @click="songClick">首页-歌单 </button>
  8. <div class="scroll">
  9. <h2>x: {{ scrollPosition.x }} </h2>
  10. <h2>y: {{ scrollPosition.y }} </h2>
  11. </div>
  12. </template>
  13. <script>
  14. import { onMounted, ref } from 'vue'
  15. import useCounter from '../hooks/useCounter'
  16. import useTitle from '../hooks/useTitle'
  17. import useScrollPosition from '../hooks/useScrollPosition'
  18. export default {
  19. setup( ) {
  20. // 1.counter逻辑
  21. const { counter, increment, decrement } = useCounter()
  22. // 2.修改标题
  23. const { title } = useTitle( "首页")
  24. // 3.监听按钮的点击
  25. function popularClick( ) {
  26. title. value = "首页-流行"
  27. }
  28. function hotClick( ) {
  29. title. value = "首页-热门"
  30. }
  31. function songClick( ) {
  32. title. value = "首页-歌单"
  33. }
  34. // 4.获取滚动位置
  35. const { scrollPosition } = useScrollPosition()
  36. console. log(scrollPosition)
  37. return {
  38. counter,
  39. increment,
  40. decrement,
  41. popularClick,
  42. hotClick,
  43. songClick,
  44. scrollPosition
  45. }
  46. }
  47. }
  48. </script>
  49. <style scoped>
  50. </style>

App.vue


  
  1. <template>
  2. <div>AppContent </div>
  3. <button @click="changeTitle">修改title </button>
  4. <!-- 1.计数器 -->
  5. <!-- <hr>
  6. <home></home>
  7. <hr>
  8. <about></about> -->
  9. <!-- 2.home和about页面的切换 -->
  10. <button @click="currentPage = 'home'">home </button>
  11. <button @click="currentPage = 'about'">about </button>
  12. <component :is="currentPage"> </component>
  13. <div class="content"> </div>
  14. <br> <br> <br> <br> <br> <br>
  15. <br> <br> <br> <br> <br> <br>
  16. <br> <br> <br> <br> <br> <br>
  17. <br> <br> <br> <br> <br> <br>
  18. <br> <br> <br> <br> <br> <br>
  19. <br> <br> <br> <br> <br> <br>
  20. <br> <br> <br> <br> <br> <br>
  21. <br> <br> <br> <br> <br> <br>
  22. <br> <br> <br> <br> <br> <br>
  23. <br> <br> <br> <br> <br> <br>
  24. <br> <br> <br> <br> <br> <br>
  25. </template>
  26. <script>
  27. import { ref } from 'vue'
  28. import Home from './views/Home.vue'
  29. import About from './views/About.vue'
  30. import useTitle from './hooks/useTitle'
  31. export default {
  32. components: {
  33. Home,
  34. About
  35. },
  36. setup( ) {
  37. const currentPage = ref( "home")
  38. function changeTitle( ) {
  39. useTitle( "app title")
  40. }
  41. return {
  42. changeTitle,
  43. currentPage
  44. }
  45. }
  46. }
  47. </script>
  48. <style scoped>
  49. .content {
  50. width: 3000px;
  51. height: 100px;
  52. background-color: orange;
  53. }
  54. </style>

注意:这个案例只是展示了setup中其他函数的搭配使用方式。 


转载:https://blog.csdn.net/weixin_52851967/article/details/128749380
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场