学习vue3记录

1.toRef

toRef是用来给抽离响应式对象(被reactive包裹的对象)中的某一个属性的,并且把这个属性包裹成ref对象,使其和原对象产生链接。

或许有人就回有人有疑问了?这个toRef存在有什么意义呢?因为我们知道reactive包裹的东西其实已经有了响应式了,再用toRef什么意义呢?

<script setup lang="ts">
import { ref,reactive,toRef } from 'vue';

const userinfo = reactive({
  name:'张三',
  age:18
})
//定义一个age2赋值userinfo.age
const age2 = userinfo.age
// const state = toRef(userinfo,'age')
const toChange = () =>{
  userinfo.age++
  console.log("userinfo",userinfo);
  
}
</script>

<template>
  <div class="content">
    <button @click="toChange">按钮</button>{{ age2 }}
  </div>
</template>

<style scoped>
</style>
定义一个age2赋值userinfo.age,修改对象userinfo里的age值,数据改变DOM不改变

 

 

如图:我们如果要单独把对象中的某个值拿出来,如果直接赋值,会断掉响应式(age2)。就算后面puserinfo.age的名字改了,但是因为断掉了响应式,输出的还是原有的名字。
而使用toRef会保持对其源 property 的响应式连接。
并且响应是互相的,数据源数据也会被更改
数据会实时更新到HTML视图上
用toRef这样写
<script setup lang="ts">
import { ref,reactive,toRef } from 'vue';

const userinfo = reactive({
  name:'张三',
  age:18
})
const age2 = userinfo.age
//toRef
const state = toRef(userinfo,'age')
const toChange = () =>{
  //.value调用
  state.value++
  console.log("userinfo",userinfo);
  
}
</script>

<template>
  <div class="content">
    <button @click="toChange">按钮</button>{{ state }}
  </div>
</template>

<style scoped>
</style>

数据更新DOM也同步更新

 

 

 


有人又会问了,那如果说toRef只是把这个响应式对象的属性包裹成ref对象,那我直接用ref这个对象不就可以了吗?何必要用toRef呢。
答:ref是不会去更改原数据的,ref本质是拷贝原数据,而toRef会修改原数据!!
使用ref DOM更新,数据没更新
<script setup lang="ts">
import { ref,reactive,toRef } from 'vue';

const userinfo = reactive({
  name:'张三',
  age:18
})
const age2 = userinfo.age
//ref
const state = ref(userinfo.age)
const toChange = () =>{
  //.value调用
  state.value++
  console.log("userinfo",userinfo);
  
}
</script>

<template>
  <div class="content">
    <button @click="toChange">按钮</button>{{ state }}
  </div>
</template>

<style scoped>
</style>

 

2.toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref*。

这句官方定义我都读了很久没太懂啥玩意儿啊?别怕,我给你解释一下
批量版的toRef
toRef不是只能对象中的一个属性嘛,这个可以直接给你整个对象的属性都一起给整成ref。

<script setup lang="ts">
import { ref,reactive,toRef,toRefs } from 'vue';

const userinfo = reactive({
  name:'张三',
  age:18
})
const age2 = userinfo.age
//toRefs
const userinfo2 = toRefs(userinfo)
const toChange = () =>{
  //.value调用
  userinfo2.age.value++
  console.log("userinfo2",userinfo2);
  
}
</script>

<template>
  <div class="content">
    <button @click="toChange">按钮</button>{{ userinfo2.age.value }}
  </div>
</template>

<style scoped>
</style>

 

 和toRef的区别,在使用的时候注意要加上xxx.属性名.value

 总结:toRefs和toRef没啥区别,就是批量和单个的区别。也是相互响应

3.toRaw

将响应式对象修改为普通对象

4.computed

<script setup lang="ts">
import { computed } from '@vue/reactivity';
import { reactive } from 'vue';

const author = reactive({
  name:'张三',
  books:[
    'book1',
    'book2',
    'book3',
  ]
})
const com = computed(()=>{
  return author.books.length>0?"Yes":"No"
})
</script>

<template>
  <div class="content">
      <p>作者:{{ author.name }}</p>
      是否有作品:{{ com }}
  </div>
</template>

<style scoped>
</style>

 5.Watch

vue3 watch 的作用和 Vue2 中的 watch 作用是一样的,他们都是用来监听响应式状态发生变化的,当响应式状态发生变化时,就会触发一个回调函数。

watch(data,()=>{},{})
  • 参数一,监听的数据

  • 参数二,数据改变时触发的回调函数(newVal,oldVal)

  • 参数三,options配置项,为一个对象

  • 1、监听ref定义的一个响应式数据

<script setup lang="ts">
  import {ref, watch} from "vue";
  const str = ref('彼时彼刻')
  // 3s后改变str的值
  setTimeout(()=>{
    str.value = "恰如此时此刻"
  },3000)
  watch(str,(newVal,oldVal)=>{
    console.log(newVal,oldVal);
    
  })
</script>

<template>
  <div class="content">
    {{ str }}
  </div>
</template>

<style scoped>
</style>

  • 2、监听多个ref

这时候写法变为数组的形式

<script setup lang="ts">
  import {ref, watch} from "vue";
  const name = ref("张三")
  const age = ref(18)
  // 3s后改变str的值
  setTimeout(()=>{
    name.value = "李四"
    age.value = 19
  },3000)
  watch([name,age],(newVal,oldVal)=>{
    console.log(newVal,oldVal);
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

  • 3、监听Reactive定义的响应式对象

<script setup lang="ts">
  import {ref, watch,reactive} from "vue";
  const infoName = reactive({
    name:'张三',
    age:18
  })
  setTimeout(()=>{
    infoName.name = '李四',
    infoName.age = 19
  },3000)
  watch(infoName,(newVal,oldVal)=>{
    console.log(newVal,oldVal);
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

tips:

  • 监视reactive定义的响应式数据时,oldvalue无法正确获取,强制开启了深度监视(deep配置失败)
  • 监视reactive定义的响应式数据中某个属性时:deep配置有效
  • 4、监听reactive 定义响应式对象的单一属性

错误写法

<script setup lang="ts">
  import {ref, watch,reactive} from "vue";
  const infoName = reactive({
    name:'张三',
    age:18
  })
  setTimeout(()=>{
    infoName.name = '李四',
    infoName.age = 19
  },3000)
  watch(infoName.age,(newVal,oldVal)=>{
    console.log(newVal,oldVal);
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

控制台报错

如果我们非要监听响应式对象中的某个属性,我们可以使用 getter 函数的形式,即将watch第一个参数修改成一个回调函数的形式

正确写法:

<script setup lang="ts">
  import {ref, watch,reactive} from "vue";
  const infoName = reactive({
    name:'张三',
    age:18
  })
  setTimeout(()=>{
    infoName.name = '李四',
    infoName.age = 19
  },3000)
  //监听reactive 定义响应式对象的单一属性
  watch(()=>infoName.age,(newVal,oldVal)=>{
    console.log(newVal,oldVal);
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

控制台打印

  • 5、监听reactive定义的 引用数据

<script setup lang="ts">
  import {ref, watch,reactive} from "vue";
  const infoName = reactive({
    name:'张三',
    age:18,
    obj:{
      str:"hello Vue3!"
    }
  })
  setTimeout(()=>{
    infoName.obj.str="更改后的文字"
  },3000)
  // 需要自己开启 deep:true深度监听,不然不发触发 watch 的回调函数
  watch(()=>infoName.obj,(newVal,oldVal)=>{
    console.log(newVal,oldVal);  
  },{
    deep:true
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>
需要自己开启 deep:true深度监听,不然不发触发 watch 的回调函数

 6.WatchEffect

watchEffect函数的作用:

传入的一个函数,当依赖项变化的时候,重新执行改函数。

watchEffect函特性:

与watch相似都可以监听一个数据源。
但是watchEffect会在初始化的时候调用一次,与watch的immediate类似。

会立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。(有点像计算属性)

如果用到 a 就只会监听 a, 就是用到几个监听几个 而且是非惰性,会默认调用一次

<script setup lang="ts">
  import {ref, watchEffect} from "vue";
  const num = ref(0)
  setTimeout(()=>{
    num.value++
  },3000)
  watchEffect(()=>{
    console.log(num.value);
  })
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

可以在控制台上看到,第一次进入页面时,打印出num 值改变:0,三秒后,再次打印num 值改变:1

 

watchEffect 它与 watch 的区别主要有以下几点:

  1. 不需要手动传入依赖
  2. 每次初始化时会执行一次回调函数来自动获取依赖
  3. 无法获取到原值,只能得到变化后的值

 

<script setup lang="ts">
  import {reactive, ref, watchEffect} from "vue";
  const num = ref(0)
  const obj = reactive({
    name:'张三',
    age:18
  })
  watchEffect(()=>{
    console.log(obj.name);
    console.log(obj.age);
  })
  setTimeout(()=>{
    obj.name = '李四',
    obj.age = 19
  },3000)
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

  • 停止监听

当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

但是我们采用异步的方式创建了一个监听器,这个时候监听器没有与当前组件绑定,所以即使组件销毁了,监听器依然存在。

这个时候我们可以显式调用停止监听

<script setup lang="ts">
  import {reactive, ref, watchEffect} from "vue";
  const num = ref(0)
  const obj = reactive({
    name:'张三',
    age:18
  })
  const res = watchEffect(()=>{
    console.log(obj.name);
    console.log(obj.age);
  })
  
  setTimeout(()=>{
    obj.name = '李四',
    obj.age = 19
  },3000)
  res() //关闭监听
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

watch:显示指定依赖源,依赖源更新时执行回调函数。
watchEffect:自动收集依赖源,依赖源更新时候重新执行自身。

  • watchEffect 如果存在的话,组件初始化的时候就会自动自行一次,不需要像 watch 一样设置立即执行。
  • watch 每次回调之后是可以获取到最新值和上一次的老值,但是 watchEffect 是拿不到的。
  • watchEffect 不需要指定监听的属性,他会自动进行依赖收集,只要我们回调中使用了响应式的属性,那么这些属性在变更之后这个回调都会执行,不像 watch 只能监听指定的属性。
  • 使用的时候也是需要引入。
  • 记住一点哈, watch 可以代替 watchEffect,但是 watchEffect 不能替代 watch。

总结:能用 watch 就不要用 watchEffect。

  • 清除副作用(onInvalidate)

watchEffect 的第一个参数——effect函数——可以接收一个参数:叫onInvalidate,也是一个函数,用于清除 effect 产生的副作用

就是在触发监听之前会调用一个函数可以处理你的逻辑,例如防抖

<script setup lang="ts">
  import {reactive, ref, watchEffect} from "vue";
  const num = ref(0)
  watchEffect(onInvalidate =>{
    console.log(num.value);
    onInvalidate(()=>{
      console.log('执行');
    })
  })
  setTimeout(()=>{
    num.value++
  },3000)
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

 控制台依次输出:0 => 执行 => 1

onInvalidate清除副作用函数注意点

1.该函数总是在watchEffect执行的时候再次执行
2.当组件被销毁的时候该函数再次执行
3.该函数总是优先于watchEffect中的同步/异步代码执行
4.Promize函数的执行应该在该函数下面
  • 配置选项

watchEffect的第二个参数,用来定义副作用刷新时机,可以作为一个调试器来使用

flush (更新时机):

  • 1、pre:组件更新前执行

  • 2、sync:强制效果始终同步触发

  • 3、post:组件更新后执行


<script setup lang="ts">
  import {reactive, ref, watchEffect} from "vue";
  const num = ref(0)
  watchEffect(onInvalidate =>{
    console.log(num.value);
    onInvalidate(()=>{
      console.log('执行');
    });
  },{
    flush:"post", //此时这个函数会在组件更新之后去执行
    onTrigger(e){ //作为一个调试工具,可在开发中方便测试
      console.log('触发',e);
      
    }
  })
  setTimeout(()=>{
    num.value++
  },3000)
</script>

<template>
  <div class="content">
  </div>
</template>

<style scoped>
</style>

7.生命周期

和 vue2 相比的话,基本上就是将 Vue2 中的beforeDestroy名称变更成beforeUnmount; destroyed 表更为 unmounted;然后用setup代替了两个钩子函数 beforeCreate 和 created;新增了两个开发环境用于调试的钩子

 8.父子组件传参

defineProps

父组件传参

 父组件:
<script setup lang="ts">
import TestChildren from './components/testChildren.vue';
  const msg = "张三"
  const list = [1,2,3]
</script>

<template>
  <div class="content">
    <TestChildren :msg="msg" :list="list"></TestChildren>
  </div>
</template>
<style scoped>
</style>

t setup 中,引入的组件会自动注册,所以可以直接使用,无需再通过components进行注册

子组件接受值

defineProps 来接收父组件传递的值, defineProps是无须引入的直接使用即可

<template>
    <div>
        <p>{{ msg }}</p>
        <p>{{ list }}</p>
        <!-- <button @change="handleClick"></button> -->
    </div>
</template>
<script setup lang="ts">
    // defineProps ts写法
    // defineProps<{
    //     msg:string,
    //     list:number[],
    // }>()
    // 两种方法均可
    const props = defineProps({
        msg:{
            type:String
        },
        list:{
            type:Array
        }
    })
</script>
<style>
</style>

子组件向父组件抛出事件

defineEmits

子组件派发事件

 

 

 
posted @ 2023-03-02 10:53  seven&night  阅读(45)  评论(0编辑  收藏  举报