vue3的学习

110 阅读4分钟

组件传值 props

Props 是一种特别的 attributes,你可以在组件上声明注册。要传递给子组件一个属性,我们必须在组件的 props 列表上声明它。这里要用到 defineProps 宏,defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。声明的 props 会自动暴露给模板。

<!-- chlid.vue -->
<script setup>
defineProps(['title'])
</script>

<template>
  <h4>{{ title }}</h4>
</template>

子组件调用父组件方法 $emit

<!-- 父组件文件 -->

<BlogPost
  ...
  @enlarge-text="postFontSize += 0.1"   //监听
 />
<!-- 子组件文件 -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    //触发父组件中的 "enlarge-text" 事件
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

单向数据流

vue中的props是单向数据流原则,props 因父组件的更新而流传到子组件,但不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

但开发过程中有些场景需要修改父组件船体过来的props,比如

  1. prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
const props = defineProps(['initialCounter'])

// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
  1. 需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性
const props = defineProps(['size'])

// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())

v-modal

vue3中提供了一个宏 defineModel()

<!-- Child.vue -->
<script setup>
const model:msg = defineModel('msg')
const model:msg11 = defineModel('msg11')

</script>

<template>
  <input v-model="msg" />
</template>

父组件可以用 v-model 绑定一个值:

<!-- Parent.vue -->
<script setup>
const msg = ref("hello")
const msg11 = ref("bye")

</script>
<Child v-model:msg="msg" v-model:msg11="msg11 />

这样就可以达到子组件中的属性model发生变化,父组件中的属性msg会同步更新的效果

透传Attributes

透传Attributes这个名词,乍一听挺懵的,其实它就是在父组件中传入的属性,没有声明为prop,而自动透传到了子组件的根元素上,例如下面的class属性

<!-- <MyButton> 的模板 -->

<button>click me</button>
//parent.vue

<MyButton class="large" />

最后渲染出的 DOM 结果是:

<button class="large">click me</button>

一般能够透传的Attributes有:class,style,v-on监听器

同样的规则也适用于 v-on 事件监听器:
<MyButton @click="onClick" />

click 监听器会被添加到 <MyButton> 的根元素,即那个原生的 <button> 元素之上。当原生的 <button> 被点击,会触发父组件的 onClick 方法。同样的,如果原生 button 元素自身也通过 v-on 绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发。

深层组件继承

有些情况下一个组件会在根节点上渲染另一个组件。例如,我们重构一下 <MyButton>,让它在根节点上渲染 <BaseButton>

<!-- <MyButton/> 的模板,只是渲染另一个组件 -->
<BaseButton />
禁用 Attributes 继承

如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。 从 3.3 开始你也可以直接在 <script setup> 中使用 defineOptions

<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup 逻辑
</script>

这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。

<span>Fallthrough attribute: {{ $attrs }}</span>

有几点需要注意:

  • 和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样的一个 attribute 需要通过 $attrs['foo-bar'] 来访问。
  • 像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick

我们想要所有像 class 和 v-on 监听器这样的透传 attribute 都应用在内部的 <button> 上而不是外层的 <div> 上。我们可以通过设定 inheritAttrs: false 和使用 v-bind="$attrs" 来实现:

<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>

小提示:没有参数的 v-bind 会将一个对象的所有属性都作为 attribute 应用到目标元素上。

在 JavaScript 中访问透传 Attributes
<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

需要注意的是,虽然这里的 attrs 对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。如果你需要响应性,可以使用 prop。或者你也可以使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用。

provide reject

provide 和 inject 可以实现  一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

image.png

provide()为组件后代提供数据

//父组件
<script setup>
import { provide } from 'vue'

provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>

inject()注入上层组件提供的数据

//子组件
<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

有的时候,我们可能需要在注入方组件中更改数据。在这种情况下,我们推荐在供给方组件内声明并提供一个更改数据的方法函数:

<!-- 在供给方组件内 -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

最后,如果你想确保提供的数据不能被注入方的组件更改,你可以使用 readonly() 来包装提供的值。

<script setup>
import { ref, provide, readonly } from 'vue'

const count = ref(0)
provide('read-only-count', readonly(count))
</script>