自学内容网 自学内容网

vue3基础知识

书接上文,这篇继续来学习vue3的核心语法,可以先看上一篇再来看这篇效果更好。

1. computed

computed 用于创建 计算属性,即基于其他响应式数据的值动态计算并缓存的属性。它的主要作用是优化性能和提高代码的可维护性,避免不必要的重复计算。

a. 基本语法

computed 是通过 computed() 函数创建的。在 setup() 函数中,你可以使用 computed 来定义计算属性。

import { computed, ref } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const doubledCount = computed(() => count.value * 2);

    return { count, doubledCount };
  }
};

解释:

  • count 是一个响应式数据,初始值为 0
  • doubledCount 是一个计算属性,它依赖于 count,并返回 count 的两倍。
  • count 的值发生变化时,doubledCount 会自动重新计算。

b. 计算属性的缓存

computed 的一个重要特性是 缓存。只有当计算属性依赖的响应式数据发生变化时,computed 才会重新计算,否则会直接返回上一次计算的结果。

import { computed, ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    // 计算属性:返回 count 的平方
    const squaredCount = computed(() => {
      console.log('Computing squared count');
      return count.value * count.value;
    });

    return { count, squaredCount };
  }
};

在上面的例子中,如果你多次访问 squaredCount,你会看到 “Computing squared count” 只会在 count 改变时打印一次,而不会在每次访问时都打印,这就是 computed 的缓存机制。

c. 使用 computed 的场景

  • 计算派生状态:当你需要基于现有的响应式数据计算新的值时,computed 很有用。例如,计算一个总价、折扣后价格、过滤后的列表等。

    const price = ref(100);
    const discount = ref(0.2);
    
    const discountedPrice = computed(() => price.value * (1 - discount.value));
    
  • 优化性能:通过缓存计算结果,computed 可以避免重复计算,提升性能。

  • 条件渲染:你可以使用 computed 来做一些复杂的条件判断,而不需要在模板中使用大量的 v-if

watch 的对比

watch 用于观察响应式数据的变化,并执行副作用操作,如更新外部状态或执行异步操作。它适合处理需要副作用的场景,而 computed 更适合处理计算派生状态并返回值。

import { watch, ref } from 'vue';

const count = ref(0);

// 使用 watch
watch(count, (newValue) => {
  console.log('Count changed:', newValue);
});

// 使用 computed
const doubledCount = computed(() => count.value * 2);
  • watch 用于监听 count 的变化,并在变化时执行某个副作用。
  • computed 用于计算并返回派生值,在模板中或其他地方直接使用。

2. watch

在 Vue 3 中,watch 是用于 监听响应式数据的变化 并在数据变化时执行副作用操作的 API。它特别适用于处理那些需要在数据变化时执行的逻辑,例如异步请求、数据处理、或者与外部系统交互。

watch 的基本概念

  • 监听watch 允许你监控一个或多个响应式数据的变化。
  • 副作用:当被监听的数据发生变化时,watch 会执行一个回调函数,你可以在回调中执行任何副作用操作(例如,发起异步请求、更新外部状态等)。
  • 深度监听watch 也可以深度监听对象或数组的变化,适合用于嵌套对象或数组的更新。

a. 基本语法

watch 接受三个参数:

  • 第一个参数:要观察的响应式数据或计算属性。
  • 第二个参数:回调函数,监听到数据变化时会调用它。
  • 第三个参数(可选):配置对象,可以设置一些附加选项,如 immediatedeep

最简单的使用方式

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);

    // 监听 count 的变化
    watch(count, (newValue, oldValue) => {
      console.log(`count changed from ${oldValue} to ${newValue}`);
    });

    return { count };
  }
};

解释

  • watch 监听 count 的变化,每当 count 发生变化时,会调用回调函数。
  • 回调函数接收两个参数:newValue(新的值)和 oldValue(旧的值),可以在回调中执行任何副作用操作。

b. watch 的高级用法

监听多个响应式数据

你可以同时监听多个响应式数据,当它们中的任意一个变化时,回调都会被触发。

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const name = ref('Alice');

    // 同时监听 count 和 name 的变化
    watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
      console.log(`count changed from ${oldCount} to ${newCount}`);
      console.log(`name changed from ${oldName} to ${newName}`);
    });

    return { count, name };
  }
};

深度监听对象或数组

默认情况下,watch 只会监听对象或数组的 引用变化,而不会递归监听其内部属性或元素。要实现深度监听,可以使用 deep: true 配置选项。

import { ref, watch } from 'vue';

export default {
  setup() {
    const user = ref({
      name: 'Alice',
      age: 25
    });

    // 深度监听 user 对象的变化
    watch(user, (newValue, oldValue) => {
      console.log('User changed:', newValue);
    }, { deep: true });

    return { user };
  }
};

立即执行(immediate

watch 默认在被监听的数据发生变化时才会执行回调。如果你希望在监听开始时立即执行回调,可以使用 immediate: true 配置。

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);

    // 监听 count 的变化,并立即执行回调
    watch(count, (newValue, oldValue) => {
      console.log(`count changed from ${oldValue} to ${newValue}`);
    }, { immediate: true });

    return { count };
  }
};

执行异步操作

watch 是非常适合用于执行异步操作的。比如,当某个数据变化时,你可能需要去发起一个 API 请求。

import { ref, watch } from 'vue';

export default {
  setup() {
    const searchQuery = ref('');

    // 监听 searchQuery 的变化,发起一个异步操作
    watch(searchQuery, async (newQuery) => {
      if (newQuery) {
        const results = await fetch(`https://api.example.com/search?q=${newQuery}`);
        console.log(await results.json());
      }
    });

    return { searchQuery };
  }
};

2.1 watchEffect 的使用

watchEffectwatch 的一个变体,它会在组件挂载时立即执行,并且会自动追踪作用域内所有的响应式依赖。与 watch 主要是用于监听特定数据的变化不同,watchEffect 会自动检测你使用的所有响应式数据。

import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const count = ref(0);

    // watchEffect 会自动监听 count 的变化
    watchEffect(() => {
      console.log(`Count has changed: ${count.value}`);
    });

    return { count };
  }
};

watchEffectwatch 的区别:

  • watch:用于精确监听一个或多个特定的数据源,并可以对变化做出响应。
  • watchEffect:自动追踪你在其作用域内使用的所有响应式数据,并在数据变化时重新执行回调。

c. watch 的使用场景

  • 监听数据变化并执行副作用:例如,发起 API 请求、更新外部状态等。
  • 异步操作:比如在数据变化时发起一个网络请求或进行复杂的数据处理。
  • 表单验证:在表单字段变化时执行验证逻辑。
  • 数据持久化:监听数据变化并将数据持久化到本地存储或服务器。

3. props

props 是父组件向子组件传递数据的一种方式。通过 props,父组件可以将数据或参数传递给子组件,从而实现组件之间的通信。

a. 基本概念

  • 父组件 -> 子组件props 是单向数据流(单向绑定)的机制,数据只能从父组件传递到子组件,子组件不能直接修改从父组件接收的 props
  • 类型验证:Vue 允许对传递的 props 进行类型验证和默认值设置,从而确保数据的正确性。

b. 基本使用

父组件

<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import {reactive} from 'vue'
  import {type Persons} from './types'
  
   let persons = reactive<Persons>([
      {id:'e98219e12', name:'张三', age:18},
      {id:'e98219e13', name:'李四', age:19},
      {id:'e98219e14', name:'王五', age:20}
   ])
</script>

子组件

<script lang="ts" setup name="Person">
   import {defineProps} from 'vue'
   // types中包含了Persons类型
   import {type Persons} from '@/types'
  
   // 第一种写法:仅接收
   // const props = defineProps(['list'])
  
   // 第二种写法:接收 + 限制类型
   // defineProps<{list:Persons}>()
  
   // 第三种写法:接收 + 限制类型 + 指定默认值 + 限制必要性
   // list后如果加?表示可传可不传,不加则必须传
   let props = withDefaults(defineProps<{list?:Persons}>(),{
      list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]
   })
   console.log(props)
</script>

通过 props,Vue 提供了一种简单而高效的组件通信方式,非常适合用于父子组件之间的数据传递和状态共享。


4. Hooks

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin。
  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

示例代码:
useSum.ts

import {ref,onMounted} from 'vue'

export default function(){
  let sum = ref(0)

  const increment = ()=>{
    sum.value += 1
  }
  const decrement = ()=>{
    sum.value -= 1
  }
  onMounted(()=>{
    increment()
  })

  //向外部暴露数据
  return {sum,increment,decrement}
}

useDog.ts

import {reactive,onMounted} from 'vue'
import axios,{AxiosError} from 'axios'

export default function(){
  let dogList = reactive<string[]>([])

  // 方法
  async function getDog(){
    try {
      // 发请求
      let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
      // 维护数据
      dogList.push(data.message)
    } catch (error) {
      // 处理错误
      const err = <AxiosError>error
      console.log(err.message)
    }
  }

  // 挂载钩子
  onMounted(()=>{
    getDog()
  })

  //向外部暴露数据
  return {dogList,getDog}
}

App.vue

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="increment">点我+1</button>
  <button @click="decrement">点我-1</button>
  <hr>
  <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)"> 
  <span v-show="dogList.isLoading">加载中......</span><br>
  <button @click="getDog">再来一只狗</button>
</template>

<script lang="ts">
  import {defineComponent} from 'vue'

  export default defineComponent({
    name:'App',
  })
</script>

<script setup lang="ts">
  import useSum from './hooks/useSum'
  import useDog from './hooks/useDog'

  let {sum,increment,decrement} = useSum()
  let {dogList,getDog} = useDog()
</script>

是的,hook 可以帮助实现模块化开发,尤其在 Vue 3 中,结合 组合式 API (Composition API) 使用时,它极大地提高了代码的可复用性和模块化程度。

分析:

  • 模块化:通过将数据获取和表单处理的逻辑分别提取到 useSumuseDog hook 中,逻辑更加清晰和模块化。
  • 复用性:这两个 hook 可以在不同的组件中复用,而无需重复编写相同的代码。
  • 解耦:不同的功能模块(如数据请求、表单验证等)被清晰地分离开来,组件只负责调用这些 hook,减少了组件内部的复杂度。

如果你能看到这里给你点个赞,如果对你有帮助的话不妨点赞支持一下~
参考:张天禹老师b站课程


原文地址:https://blog.csdn.net/m0_73969414/article/details/144182090

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!