Vue3 学习笔记--深入组件

1、组件注册

1.1 全局注册

Vue.createApp({...}).component('my-component-name', {
  // ... 选项 ...
})

或者像下面这样写

const app = Vue.createApp({})

app.component('component-a', {
  /* ... */
})
app.component('component-b', {
  /* ... */
})

1.2 局部注册

通过一个普通的 JavaScript 对象来定义组件

const ComponentA = {
  /* ... */
}
const ComponentB = {
  /* ... */
}

然后在 components 选项中定义你想要使用的组件:

const app = Vue.createApp({
  components: {
    'component-a': ComponentA, // component-a 为对应的组件名
    'component-b': ComponentB
  }
})

局部注册的组件在其子组件中不可用

例如,上面的组件 ComponentAComponentB 都是在 app 中局部注册的

但在 ComponentB 中是无法使用 ComponentA 的

除非你这样写:

const ComponentA = {
  /* ... */
}

const ComponentB = {
  components: {
    'component-a': ComponentA
  }
  // ...
}

ComponentB 中注册 ComponentA 之后,才能使用 ComponentA


2、父子组件通信

父子组件通过两种不同的方式来相互通信

  • 父传子:props
  • 子传父:$emit

先来讲一下父传子,也就是 props

2.1 Props

Prop 是你可以在组件上注册的一些自定义 attribute

Props 有两种常见的用法:

  • 数组
  • 对象

直接用代码来解释用法

Vue.component('blog-post', {
  props: ['title'],
  template: '

{{ title }}

' })

在上述代码中,我们局部注册了一个组件 blog-post

并且在 props 中自定义了一个 attribute,并且在 template 中使用了 title



在父组件中使用 blog-post 组件,并且给 title 赋值

这样就实现了父组件给子组件传值

页面效果如下:

Vue3 学习笔记--深入组件_第1张图片

2.1.1 Props 的数组用法 

// 简单语法
app.component('props-demo-simple', {
  props: ['size', 'myMessage']
})

2.1.2 Props 的对象用法

Vue3 学习笔记--深入组件_第2张图片

type 的类型可以是下面这些:

Vue3 学习笔记--深入组件_第3张图片

补充:对象类型的其它写法

app.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default() {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator(value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 具有默认值的函数
    propG: {
      type: Function,
      // 与对象或数组默认值不同,这不是一个工厂函数 —— 这是一个用作默认值的函数
      default() {
        return 'Default function'
      }
    }
  }
})

注意:使用 DOM 中的模板时,驼峰命名的 prop 名需要使用其等价的短横线分隔符命名来替换

2.2 非 Prop 的 Attribute

一个非 propattribute 是指传向一个组件

但是该组件并没有相应 propsemits 定义的 attribute

2.2.1 Attribute 继承

Vue3 学习笔记--深入组件_第4张图片

2.2.2 禁用 Attribute 继承

如果我们不希望根元素继承 attribute

可以在组件的选项中设置

inheritAttrs: false

如下所示:

Vue3 学习笔记--深入组件_第5张图片

2.2.3 多个根节点上的 Attribute 继承

Vue3 学习笔记--深入组件_第6张图片

2.3 监听子组件事件

首先,我们需要 在子组件中定义好某些情况下触发的事件名称

其次,在父组件中以 v-on 的方式传入要监听的事件名称,并绑定对应的方法

最后,在子组件中发生某个事件的时候,根据事件名触发对应的事件

先看一个计数器的例子,如下图所示:

Vue3 学习笔记--深入组件_第7张图片

Vue3 学习笔记--深入组件_第8张图片

大致过程就是:

Vue3 学习笔记--深入组件_第9张图片

2.3.1 验证抛出的事件

const app = createApp({})

// 数组语法
app.component('todo-item', {
  emits: ['check'],
  created() {
    this.$emit('check')
  }
})

// 对象语法
app.component('reply-form', {
  emits: {
    // 没有验证函数
    click: null,

    // 带有验证函数
    submit: payload => {
      if (payload.email && payload.password) {
        return true
      } else {
        console.warn(`Invalid submit event payload!`)
        return false
      }
    }
  }
})

3、非父子组件通信

Vue3 学习笔记--深入组件_第10张图片

使用 provide 和 inject

无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者

这个特性有两个部分:

  1. 父组件有一个 provide 选项来提供数据
  2. 子组件有一个 inject 选项来开始使用这些数据

还是继续用前面那个例子进行讲解

Vue3 学习笔记--深入组件_第11张图片

provide 组件实例的 property

Vue3 学习笔记--深入组件_第12张图片

但如果 todos 中的内容发生了变化,也就是 todos.length 改变时,不会反映在 inject 中

3.1 处理响应性

Vue3 学习笔记--深入组件_第13张图片

全局事件总库 mitt 库 ?


4、插槽

4.1 基本使用

Vue3 学习笔记--深入组件_第14张图片

4.2 默认内容

使用插槽时,如果没有提供内容,则会显示插槽默认的内容

如果我们提供内容,则这个提供的内容将会被渲染从而取代默认内容

4.3 具名插槽

Vue3 学习笔记--深入组件_第15张图片

4.3.1 具名插槽的缩写

v-slot: 可以写成 #

因此上面的代码可以这样写:


  

  

  

4.4 动态插槽名


  

4.5 渲染作用域

规则只有一条:

父级模板里的所有内容都是在父级作用域中编译的

子模板里的所有内容都是在子作用域中编译的 

Vue3 学习笔记--深入组件_第16张图片

4.6 作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的

下面我们来看一个例子,组件 todo-list 中包含一个 items

我们将其动态的绑定到了 slot 上面

app.component('todo-list', {
  data() {
    return {
      items: ['Feed a cat', 'Buy milk']
    }
  },
  template: `
    
` })

我们在父组件中使用了 todo-list 组件,要想在父组件中使用 slot 中绑定的 attribute

我们需要先包裹一层 template,然后把之前绑定的 attribute 传过来

也就是给 v-slot 赋一个 slotProps (绑定在 slot 元素上的 attribute 被称为 插槽 prop

最后通过 slotProps 获取传过来的值


  

4.6.1 独占默认插槽的缩写语法

 在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用


  {{ slotProps.item }}

这种写法还可以更简单

当我们的插槽是默认插槽时,也就是说它没有名字,那就可以像这样写:


  {{ slotProps.item }}

默认插槽的缩写语法不能和具名插槽混用

只要出现多个插槽,请始终为所有的插槽使用完整的基于