VUE3 学习日记(四)

一、插槽 Slots

1.1 用法

在某些场景下  我们可能想要为子组件传递一些模板片段 让子组件在他们的组件中渲染这些内容

比如:

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>

//最后渲染成
<button class="fancy-btn">Click me!</button>

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

1.2 渲染作用域

插槽可以访问到父组件的数据作用域 想干  插槽内容无法访问子组件的数据 vue模板中的表达式只能访问其定义时所处的作用域,这和JavaScript的词法作用域规则是一致的。

1.3 默认插槽 和 具名插槽

对于某些场景,需要多个插槽出口      <slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容。带name的插槽被称为具名插槽  没有name的会隐式地命名为“default”

要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令: v-slot 有对应的简写 #  <template v-slot:header> 可以简写为 <template #header>

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

1.4 动态插槽名

动态指令参数在v-slot 上也是有效的 如:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

1.5 作用域插槽

我们知道  插槽其实是无法访问到子组件里面数据以及状态的 但是有些场景下就是需要呢 下面有一个方法  可以像组件传递props那样,向一个插槽的出口上传递attributes :

<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。下面我们将先展示默认插槽如何接受 props,通过子组件标签上的 v-slot 指令,直接接收到了一个插槽 props 对象:

<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
//也可以解构
<MyComponent v-slot="{ text, count }">
  {{ text }} {{ count }}
</MyComponent>

1.6 具名作用域插槽

插槽 props 可以作为 v-slot 指令的值被访问到:v-slot:name="slotProps"

插槽上的 name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }

<!-- 该模板无法编译 -->
<template>
  <MyComponent v-slot="{ message }">
    <p>{{ message }}</p>
    <template #footer>
      <!-- message 属于默认插槽,此处不可用 -->
      <p>{{ message }}</p>
    </template>
  </MyComponent>
</template>

为默认插槽使用显式的 <template> 标签有助于更清晰地指出 message 属性在其他插槽中不可用:

<template>
  <MyComponent>
    <!-- 使用显式的默认插槽 -->
    <template #default="{ message }">
      <p>{{ message }}</p>
    </template>

    <template #footer>
      <p>Here's some contact info</p>
    </template>
  </MyComponent>
</template>

二、依赖注入

2.1 了解

正常情况下  当我们需要父组件想子组件传递数据时  会用到props  但是当我们需要多层级嵌套的组件  那么就会很麻烦,有子孙组件出来的话  props就会一层一层的往下传递  这个问题我们称之为“prop逐级透传”。

provide 和 inject 可以帮助我们解决这个问题  一个父组件相对于其所有的后代组件   会作为依赖提供者  

2.2 provide(提供)

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

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

除了在一个组件中提供依赖 我们还可以在整个应用层提供依赖:

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

在应用级别提供的数据在该应用内的所有组件中都可以注入

2.3 inject

要注入上层组件提供的数据 需要inject()函数:

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

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

如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值。

3.3和响应式数据配合使用

当提供 / 注入响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。

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

<!-- 在供给方组件内 -->
<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>

三、异步组件

3.1基本用法

在大型项目中 我们可能需要拆分应用为更小的块  并仅在需要时再从服务器加载相关组件  vue提供了defineAsyncComponent 方法来实现此功能:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

ES 模块动态导入也会返回一个 Promise,所以多数情况下我们会将它和 defineAsyncComponent 搭配使用。

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

const AdminPage = defineAsyncComponent(() =>
  import('./components/AdminPageComponent.vue')
)
</script>

<template>
  <AdminPage />
</template>

与普通组件一样,异步组件可以使用 app.component() 全局注册

app.component('MyComponent', defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
))

3.2加载与错误状态

异步操作不可避免地会涉及到加载和错误状态,因此 defineAsyncComponent() 也支持在高级选项中处理这些状态:

const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

 四、透传  Attributes

4.1了解

透传其实就是传递给一个组件,却没有被组件声明props或者emits的attribute或者v-on事件监听器。比如class style id 等

注意:透传的 attribute 不会包含 <MyButton> 上声明过的 props 或是针对 emits 声明事件的 v-on 侦听函数,换句话说,声明过的 props 和侦听函数被 <MyButton>“消费”了

如果透传符合声明  也可以作为props传入组件

4.2 禁用attributes 继承

如果你不想再组件自动继承 attribute 你可以在组件选项中设置 inheritAttrs: false

如果你使用了 <script setup>,你需要一个额外的 <script> 块来书写这个选项声明

<script>
// 使用普通的 <script> 来声明选项
export default {
  inheritAttrs: false
}
</script>

<script setup>
// ...setup 部分逻辑
</script>

注意:

和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样的一个 attribute 需要通过 $attrs['foo-bar'] 来访问

像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值