一、setup中的computed(计算属性)

对于任何包含响应式数据的复杂逻辑,都应该使用计算属性。

在前面的Options API(选项式API)中,是使用computed选项来完成计算属性的定义。

Composition API(组合式API)中,我们可以在setup函数中使用computed方法来编写一个计算属性。

setup中如何使用computed:

1)基本写法

接收一个getter函数,并为getter函数返回的值,返回一个不变的ref对象。

<template>
  <h2>{{ fullname }}</h2>
  <button @click="setFullname">设置fullname</button>
</template>

<script>
// 1. 引入computed
import { reactive, computed, ref } from 'vue'

  export default {
    setup() {
      // 定义本地data数据
      const names = reactive({
        firstName: "jack",
        lastName: "bryant"
      })
		
      // 2. 使用基本写法定义计算属性
      const fullname = computed(() => {
         return names.firstName + " " + names.lastName
      })
      console.log(fullname)

      return {names, fullname, setFullname}
    }
  }
</script>

可以看到注意点有两个:

  • 一定要先引入computed才能够在setup函数中使用
  • 给计算属性赋值时需要使用.value属性,因为它是一个ref对象

2)监听多个计算属性

<template>
	<div class="main">
        <div class="bottom-info" :style="bottomInfo.style">
            {{ bottomInfo.content }}
        </div>
    </div>
</template>

<script setup>
  // 1.导入computed
  import { computed } from 'vue';


  // 2.定义计算属性
  // const titleText = computed(() => {
  //   return props.itemData.verify_info.messages.join(" ")
  // })
  // const titleColor = computed(() => {
  //   return props.itemData.verify_info.text_color
  // })

  // 挨个写多个计算属性会有些麻烦,有着组合关系的一些计算属性可以写到一起
  const bottomInfo = computed(() => {
    return {
      content: props.itemData.bottom_info.content,
      style: {
        color: props.itemData.bottom_info.content_color,
        fontSize: props.itemData.bottom_info.font_size + "px"
      }
    }
  })

</script>

3)完整语法

接收一个具有getset的对象,返回一个可变的(可读写)ref对象。

<template>
  <h2>{{ fullname }}</h2>
  <button @click="setFullname">设置fullname</button>
</template>

<script>
// 1. 引入computed
import { reactive, computed, ref } from 'vue'

  export default {
    setup() {
      // 定义本地data数据
      const names = reactive({
        firstName: "jack",
        lastName: "bryant"
      })
		
      // 2. 使用完整写法定义计算属性
      const fullname = computed({
        set: function(newValue) {
          const tempNames = newValue.split(" ")
          names.firstName = tempNames[0]
          names.lastName = tempNames[1]
        },
        get: function() {
          return names.firstName + " " + names.lastName
        }
      })
      console.log(fullname)
	
      // 3. 给计算属性赋值、此时会调用set方法去给names的firstName和lastName赋值
      function setFullname() {
        fullname.value = "lucy"
        console.log(names)
      }

      return {names, fullname, setFullname}
    }
  }
</script>

二、setup中如何使用监听器

在组合式API中,可以使用watchEffectwatch来完成响应式数据的侦听:

  • watchEffect:用于自动收集响应式数据的依赖

  • watch:需要手动指定侦听的数据源,数据变化时执行其回调函数

    默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调

2.1 watch

1) 监听一个数据源
const info = reactive({
    name: "why",
    age: 18,
    friend: {
        name: "kobe"
    }
})

watch(info, (newValue, oldValue) => {
    console.log(newValue, oldValue)
})
2)监听多个数据源
const info = reactive({
    name: "why",
    age: 18,
    friend: {
        name: "kobe"
    }
})
const message = ref("Hello watch");
watch([info, message], (newValue, oldValue) => {
    console.log(newValue, oldValue)
})
3) 关于深度监听和立即执行

默认情况下,组合式API中的watch函数就已经帮我们进行深度监听了。

当然也可以选择传入deep显式指定。

而立即执行则需要传入immediate显式指定。

const info = reactive({
    name: "why",
    age: 18,
    friend: {
        name: "kobe"
    }
})

watch(info, (newValue, oldValue) => {
    console.log(newValue, oldValue)
},{
    immediate: true,
    deep: true
})

2.2 watchEffect

1)基本使用

当监听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用watchEffect

<script>
  import { watchEffect, watch, ref } from 'vue'

  export default {
    setup() {
      const counter = ref(0)
      const name = ref("张三")

      // 1.watchEffect传入的函数默认会直接被执行
      // 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
      // 比如箭头函数中用到的counter和name,一旦发生变化,回调函数就会直接执行
      const stopWatch = watchEffect(() => {
        console.log("watchEffect", counter.value, name.value)
      })

      return {counter, name}
    }
  }
</script>

首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;  其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行

2)停止监听

有时希望可以让watchEffect停止侦听。此时可以获取watchEffect的返回值函数,调用该函数即可。

<script>
  import { watchEffect, watch, ref } from 'vue'

  export default {
    setup() {
      const counter = ref(0)
      const name = ref("张三")

	  // 只要调用watchEffect的返回值,就可以停止监听
      const stopWatch = watchEffect(() => {
        console.log("watchEffect", counter.value, name.value)

        // counter.value > 10 时停止继续监听
        if (counter.value >= 10) {
          stopWatch()
        }
      })

      return {counter, name}
    }
  }
</script>

2.3 watch/watchEffect区别

  • watch必须制定数据源, watchEffect自动收集依赖
  • watch监听到改变, 可以拿到改变前后value,watchEffect只能拿到变化后的
  • watchEffect默认直接执行一次,watch在不设置immediate第一次是不执行

三、setup中如何使用ref获取元素或者组件

上一篇已经说过了如何使用ref函数去定义响应式数据。

Vue2.x中的有一个通过ref属性去获取DOM元素或者组件。这在Vue3.xsetup函数中又是怎么做到的呢?

setup函数中的方式是定义一个ref对象,绑定到元素或者组件的ref属性上即可。

<template>
  <!-- 1.获取DOM元素 -->
  <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() {
      // 定义一个ref对象
      const btnRef = ref()
      const showInfoRef = ref()

      // mounted的生命周期函数中就可以拿到相应的ref数据了
      onMounted(() => {
        // 完成之后btnRef就相当于之前的this.$refs.btnRef
        console.log(btnRef.value)
        console.log(showInfoRef.value)
      })

      function getElements() {
        console.log(titleRef.value)
      }
      return {btnRef, showInfoRef, getElements}
    }
  }
</script>

四、setup中如何使用生命周期钩子

4.1 使用示例

<template>
  <div></div>
</template>

<script>
  // 1.引入函数onMounted
  import { onMounted } from 'vue'

  export default {
    setup() {
      // 2.注册生命周期函数
      onMounted(() => {
        console.log("onmounted")
      })
    }
  }
</script>

使用起来就是先引入,然后在setup函数中进行使用,需要接收一个函数对象,一般就要箭头函数。

4.2 选项式API和组合式API的对应关系

选项式APIsetup中对应的组合式API
beforecreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeupdateonBeforeupdate
updatedonupdated
beforeunmountonBeforeunmount
unmountedonUnmounted
activatedonActivated
deactivatedonDeactivated

4.3 为什么没有beforecreate和created对应的组合式API

因为setup是围绕beforecreatecreated生命周期钩子运行的,所以不需要显式地定义它们。

因此在这些钩子中编写的任何代码都可以直接在setup函数中编写。

五、setup中如何使用依赖注入

5.1 Provide函数

provide函数可以传入两个参数:

  • name:提供的属性名称;

  • value:提供的属性值

const name = ref("张三")
provide("name", name)	
provide("age", 18)	

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

就想这里的age就不具备响应式。

5.2 Inject函数

后代组件中可以通过inject函数来注入需要的属性和对应的值.

Inject函数可以传入两个参数(可以只传其一):

  • 要注入的属性的名称;

  • 注入属性不存在时的默认值;

const name = inject("name")
const age = inject("age", 0)

对于复杂数据的传共享,都会使用Vuex或者pinia去共享数据。大部分情况不会去使用依赖注入的方式

六、setup语法糖

6.1 <script setup>语法的优势

当打算把所有的内容都写在setup函数中时,可以使用<script setup>语法。

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。

当同时使用SFC与组合式API时则推荐该语法,他有如下特点:

  • 更少的样板内容,更简洁的代码

  • 能够使用纯 Typescript 声明 prop和抛出事件

  • 更好的运行时性能

  • 更好的IDE类型推断性能

6.2 基础语法示例

<template>
  <div>AppContent: {{ message }}</div>
  <button @click="changeMessage">修改message</button>
  <show-info name="why" 
             :age="18"
             @info-btn-click="infoBtnClick"
             ref="showInfoRef">
  </show-info>
  <show-info></show-info>
  <show-info></show-info>
</template>

<script setup>
  // 1.所有编写在顶层中的代码, 都是默认暴露给template可以使用
  import { ref, onMounted } from 'vue'
  import ShowInfo from './ShowInfo.vue'

  // 2.定义响应式数据
  const message = ref("Hello World")
  console.log(message.value)

  // 3.定义绑定的函数
  function changeMessage() {
    message.value = "你好啊, 李银河!"
  }

  function infoBtnClick(payload) {
    console.log("监听到showInfo内部的点击:", payload)
  }

  // 4.获取组件实例
  const showInfoRef = ref()
  onMounted(() => {
    showInfoRef.value.foo()
  })

</script>

注意点:

  • 如何使用?

    setup属性 添加到 <script> 标签上。里面的代码会被编译成组件 setup() 函数的内容。

    这意味着与普通的

6.3 如何定义props的值

使用setup语法糖时,提供了defineProps()函数去定义props

<template>
  <div>ShowInfo: {{ name }}-{{ age }}</div>
</template>

<script setup>

// 定义props
const props = defineProps({
  name: {
    type: String,
    default: "默认值"
  },
  age: {
    type: Number,
    default: 0
  }
})

</script>

defineProps()函数的返回值是一个只读属性,只能使用,不能修改。

6.4 如何发出事件

使用setup语法糖时,提供了defineEmits()函数去发出事件,从而给父元素传递一些信息。

<template>
  <button @click="showInfoBtnClick">showInfoButton</button>
</template>

<script setup>

// 绑定函数, 并且发出事件
const emits = defineEmits(["infoBtnClick"])
function showInfoBtnClick() {
  emits("infoBtnClick", "showInfo内部发生了点击")
}

</script>

6.4 如何暴露方法或者属性

使用<script setup>的组件内容是不公开的。

意思就是在父组件中的模板ref或者$parent链获取到的组件的公开实例。

不会暴露任何在<script setup>中声明的属性或者方法。

通过defineExpose编译器宏来显式指定在<script setup>组件中要暴露出去的属性或者方法。

子组件:

<template>
  <div></div>
</template>

<script setup>


// 定义foo的函数
function foo() {
  console.log("foo function")
}

// 把foo函数暴露出去
defineExpose({
  foo
})

</script>

父组件:

<template>
  <show-info ref="showInfoRef"></show-info>
</template>

<script setup>
  // 导入内置函数和ShowInfo组件
  import { ref, onMounted } from 'vue'
  import ShowInfo from './ShowInfo.vue'
    
  // 获取组件实例(只能使用组件中暴露出来的属性和方法)
  const showInfoRef = ref()
  onMounted(() => {
    showInfoRef.value.foo()
  })

</script>
Logo

前往低代码交流专区

更多推荐