vue3查漏补缺

一、watch和watchEffect

watch需要明确你想要监听某一个响应式的值

const x = ref(0)
const y = ref(0)

// 单个 ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})

// getter 函数
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

有时候可能只需要监听某个值发生一次改变就行了

watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  },
  { once: true }
)

watchEffect不需要明确监听的值,只要是回调函数里面的某个响应式值状态发生了改变,都会重新执行这个回调函数,并且它一开始就会立即执行一次

//只有num1发生改变时才会执行回调函数
let num1 = ref(0);
let num2 = ref(0);
watchEffect(() => {
  console.log(num1.value);
  console.log(123)
});
// num1和num2其中一个变化都会重新执行回调函数
let num1 = ref(0);
let num2 = ref(0);
watchEffect(() => {
  console.log(num1.value);
  console.log(num2.value);
  console.log(123)
});

回调的触发时机:
1.默认情况下,侦听器回调会在父组件更新 (如有) 之后、所属组件的 DOM 更新之前被调用。这意味着如果你尝试在侦听器回调中访问所属组件的 DOM,那么 DOM 将处于更新前的状态。
2.如果想在侦听器回调中能访问被 Vue 更新之后的所属组件的 DOM,需要指明 flush: ‘post’ 选项

watch(source, callback, {
  flush: 'post'
})

watchEffect(callback, {
  flush: 'post'
})

//后置刷新的 watchEffect() 有个更方便的别名 watchPostEffect()
import { watchPostEffect } from 'vue'  
watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
})

3.还可以创建一个同步触发的侦听器,它会在 Vue 进行任何更新之前触发,(不建议使用同步)

watch(source, callback, {
  flush: 'sync'
})

watchEffect(callback, {
  flush: 'sync'
})

//同步触发的 watchEffect() 有个更方便的别名 watchSyncEffect(),不推荐使用,不灵活,万一要改成异步,还得重新去引入
import { watchSyncEffect } from 'vue'
watchSyncEffect(() => {
  /* 在响应式数据变化时同步执行 */
})

4.停止监听器
正常的监听器无需手动停止,它是绑定在组件上的,组件销毁就自动停止了,而侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)

//手动停止监听器
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()
</script>

二、透传 Attributes

透传attribute指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

<!-- <MyButton> 的模板 -->
<button>Click Me</button>
//父页面
<MyButton class="large" />
// 渲染结果
<button class="large">Click Me</button>

以上这种方式就叫做透传attribute,使用前提是子组件为单节点,这样才知道绑定到哪里,如果是多节点,就需要指定绑定的节点了

//父页面
<CustomLayout id="custom-layout" @click="changeValue" />
//子页面,这样会抛出警告
<header>...</header>
<main>...</main>
<footer>...</footer>
//需手动绑定
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

如果不想自动透传,可以在子组件中禁用

//前提是没有手动指定,比如上面的手动绑定,就算设置了禁用也会生效
<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup 逻辑
</script>

v-bind

如果 attribute 的名称与绑定的 JavaScript 值的名称相同,那么可以进一步简化语法,省略 attribute 值,注意得3.4以上版本

<!-- 与 :id="id" 相同 -->
<div :id></div>

<!-- 这也同样有效 -->
<div v-bind:id></div>

props细节

如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name

const post = {
  id: 1,
  title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />

//等价于
<BlogPost :id="post.id" :title="post.title" />

为了更贴近原生 boolean attributes 的行为,声明为 Boolean 类型的 props 有特别的类型转换规则

// 子组件
defineProps({
  disabled: Boolean
})
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />

<!-- 等同于传入 :disabled="false" -->
<MyComponent />

emit

要为事件添加校验,那么事件可以被赋值为一个函数,接受的参数就是抛出事件时传入 emit 的内容,返回一个布尔值来表明事件是否合法

<script setup>
const emit = defineEmits({
  // 没有校验
  click: null,

  // 校验 submit 事件
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>

slot

默认插槽给父组件传参

<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>
<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

具名插槽给父组件传参

<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>
</MyComponent>
<slot name="header" message="hello"></slot>

自定义指令

<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。在上面的例子中,vFocus 即可以在模板中以 v-focus 的形式使用

<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>

在没有使用 <script setup> 的情况下,自定义指令需要通过directives 选项注册:

export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}

Transition内置组件

<template>
  <div class="container">
    <button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">hello</p>
</Transition>
</div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
let show = ref(true);
</script>

<style lang="scss" scoped>
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
  transform: translateX(90px);
}
</style>

Teleport

它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去,也就是说,可以在任何层次的组件中,将某部分dom插入你想要的地方,比如body,这个用来写弹出,模态框特别有用

<button @click="open = true">Open Modal</button>
<!-- to表示插到哪里 -->
<Teleport to="body"> 
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值