vue3-基础知识(4)- 组件

组件

通用型组件就是各大组件库的组件风格,包括按钮、表单、弹窗等通用功能。
业务型组件包含业务的交互逻辑,包括购物车、登录注册等,会和我们不同的业务强绑定。

特点

  1. 可通过具名导出在一个文件中导出多个组件
  2. 在 DOM 模板中,我们必须显式地写出关闭标签
  3. 限制
  • 某些 HTML 元素对于放在其中的元素类型有限制,例如 <ul>,<ol>,<table> 和 <select>
  • 相应的,某些元素仅在放置于特定元素中时才会显示,例如<li>,<tr> 和 <option>
  • 解决:使用is
    <table>
      <tr is="vue:blog-post-row"></tr>
    </table>
    
  1. 一个可以通过其“name”选项递归渲染自己的组件,(如果使用单文件组件,则从文件名推断)
  2. 全局注册组件问题
  • 需要注册
  • 全局注册,但并没有被使用的组件无法在生产打包时被自动移除
  • 全局注册在大型项目中使项目的依赖关系变得不那么明确,定位不易,不好维护。
  1. 局部组件(推荐)
  • 无需注册,引入直接使用
  • 局部注册的组件需要在使用它的父组件中显式导入
  • 只能在该父组件中使用。
  • 使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好
  1. 格式-双驼峰(推荐)
  2. props
  • 为了和 HTML attribute 对齐
    • 一般将props传入写为kebab-case形式
    • 仅写上 prop 但不传值,会隐式转换为 true
  • 组件名-双驼峰(推荐)
  • 单向绑定、单向数据流
  • prop只读,组件内不可更新
  • prop更改场景
    • prop为初始值,后续作为组件内部变量使用。可使用ref(props.a)
    • 转换prop值。使用computed
  • 更改对象 / 数组类型的 props,可被更改但不推荐。推荐使用抛出事件来处理。
  • defineProps() 宏中的参数不可以访问<script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
  • Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
  • Boolean 类型的未传递 prop 将被转换为 false
  • 如果声明了 default 值,外部设置值为undefine无效
  • Boolean的使用默认值规则。一个prop设置多种包含了Boolean类型的类型,该规则都会被使用。
      <!-- 等同于传入 :disabled="true" -->
      <MyComponent disabled />
      <!-- 等同于传入 :disabled="false" -->
      <MyComponent />
    
  1. 事件
  • 和原生 DOM 事件不一样,组件触发的事件没有冒泡机制。你只能监听直接子组件触发的事件。平级组件或是跨越多层嵌套的组件间通信,应使用一个外部的事件总线,或是使用一个全局状态管理方案。
  • emit 可以安全地被解构
  • emits 选项还支持对象语法,允许我们对触发事件的参数进行验证。
      const emit = defineEmits({
        submit(payload) {
          // 通过返回值为 `true` 还是为 `false` 来判断
          // 验证是否通过,提交data给父组件
        }
      })
    
  • 组件事件会覆盖原生事件名
  • v-model的使用
    • API用法3
    • 默认值modelValue,配合update:modelValue
    • 通过给 v-model 指定一个参数来更改
        <MyComponent v-model:title="bookTitle" />
      
    • 自定义的修饰符,设置modelModifiers
        <script setup>
          const props = defineProps({
            modelValue: String,
            modelModifiers: { default: () => ({}) }
          })
      
          defineEmits(['update:modelValue'])
      
          console.log(props.modelModifiers) // { capitalize: true }
          // 可对该值进行处理
        </script>
      
        <template>
          <input
            type="text"
            :value="modelValue"
            @input="$emit('update:modelValue', $event.target.value)"
          />
        </template>
        <MyComponent v-model.capitalize="myText" />
      
  • 另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件
      <!-- CustomInput.vue -->
      <script setup>
        import { computed } from 'vue'
    
        const props = defineProps(['modelValue'])
        const emit = defineEmits(['update:modelValue'])
    
        const value = computed({
          get() {
            return props.modelValue
          },
          set(value) {
            emit('update:modelValue', value)
          }
        })
      </script>
    
      <template>
        <input v-model="value" />
      </template>
    
  1. 透传
  • 当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上
  • 如果一个子组件的根元素已经有了 class 或 style attribute,它会和从父组件上继承的值合并
  • 监听器同样式效果
  • 下一个组件会在根节点上渲染另一个组件,会透传。
  • 如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置inheritAttrs: false
  • 禁用透传的组件中
    • 直接用 $attrs 访问。
    • 使用v-bind="$attrs"将传递的数据进一步传给其他指定组件。
    • 访问:const attrs = useAttrs()
    • attrs不是响应式
    • 需要使用响应式attrs
      • 使用 prop
      • 使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用
  • 有着多个根节点的组件没有自动 attribute 透传行为,如果 $attrs 没有被显式绑定,将会抛出一个运行时警告。
  1. 插槽
  • 具名作用域插槽
    分两个点,名字和插槽数据传递
    子组件内插槽:
      <!-- <MyComponent> 的模板 -->
      <div>
        <slot name="test" :text="greetingMessage" :count="1"></slot>
      </div>
    
    父组件使用:
      <MyComponent #test="slotProps">
        {{ slotProps.text }} {{ slotProps.count }}
      </MyComponent>
    
  • 高级列表组件示例
      <FancyList :api-url="url" :per-page="10">
        <template #item="{ body, username, likes }">
          <div class="item">
            <p>{{ body }}</p>
            <p>by {{ username }} | {{ likes }} likes</p>
          </div>
        </template>
      </FancyList>
    
    使用:
      <ul>
        <li v-for="item in items">
          <slot name="item" v-bind="item"></slot>
        </li>
      </ul>
    
  • 无渲染组件
    • 只包括了逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。我们将这种类型的组件称为无渲染组件。
    • 适用:作用域插槽在需要同时封装逻辑、组合视图界面时
  1. 依赖注入
  • 一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
  • 用法:
    注入:
      <script setup>
        import { ref, provide } from 'vue'
        // 一般使用
        provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
    
        // 响应式
        // 提供的响应式状态使后代组件可以由此和提供者建立响应式的联系
        const count = ref(0)
        provide('key', count)
      </script>
    
    调用:
      <script setup>
        import { inject } from 'vue'
        // 如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值。
        // 这使得注入方组件能够通过 ref 对象保持了和供给方的响应性链接。
        const message = inject('message')
        // 注入默认值
        const value = inject('message', '这是默认值')
        // 调用函数或者初始化类
        const value = inject('key', () => new ExpensiveClass())
      </script>
    
  • 当提供 / 注入响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。
  • 在注入方组件中更改数据,提供refAsetRefA两个函数。
  • 确保数据被注入方的组件更改:readonly()
  • 推荐在一个单独的文件中导出这些注入名 Symbol
  1. 异步组件
  • 用法:
  const AsyncComp = defineAsyncComponent(() =>
    import('./components/MyComponent.vue')
  )
  ...
  const AsyncComp = defineAsyncComponent({
    // 加载函数
    loader: () => import('./Foo.vue'),

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

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

API用法

  1. defineProps,编译宏命令,不需要显示引入,可直接使用。
  2. defineEmits
    设置:
  definedEmits([
    'sendData'
  ])

使用:

  emit('sendData', data)
  1. v-model
    设置:
  definedProps({
    modelValue: 'x'
  })
  const emit = definedEmits([
    'update:modelValue'
  ])

使用:

  emit('update:modelValue')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值