【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍

Vue3 的官方文档中大致介绍了这三种全局API。但是我看完过后,对于一些点还是处于一个比较懵逼的状态,于是自己去查阅了一些资料以及手写代码去尝试,才渐渐领悟了过来。
在看官方文档期间,我把自己在脑海中疑惑的问题整理了一下,然后写在了这篇文章中,并从网上和实践中去找寻到了一些答案。
咱们先来看文档中对于这三个全局API的介绍:

先来看下 defineComponent

【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第1张图片【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第2张图片
文档中第一句话很容易看懂,说白了就是从实现上看,用了或者跟不用这个api没多大区别~~但是呢,第二句话说的好像又有一些区别,不过我没太看懂。。。于是我自己的第一个问题就来了:

问题一:defineComponent 这个API用起来到底和不用有什么区别???

1. 显示 Vue Options 提示。

这个API一般是在ts或者tsx文件中使用的,所以,当我们创建了一个这种类型的文件后,它是不知道我们是要去写 vue 实例代码的,所以在这种情况下,我们需要defineComponent来帮我们做内部的一些options的提示,我们可以看一个使用了defineComponent和没有使用defineComponent的例子:
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第3张图片
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第4张图片

当然这背后的原理是利用 TypeScript 定义了defineComponent 参数类型实现的。

2. 给予正确的参数类型推断。

拿 setup 来说,defineComponent 可以为 setup 函数的 props 传参做出正确的类型推断,看下图:
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第5张图片

如果没有使用 defineComponent 的话,是没有办法推断出来的,需要自己显式地去定义类型。

3. 可返回一个合成类型的构造函数。

这也是官方文档中所说的,我在代码中尝试了一下,发现确实可以在其返回的构造函数中去定义一些钩子和属性等,如下图:
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第6张图片
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第7张图片

这就是目前我对这个API的一些理解吧~后续有新发现再补充

接下来来看下 defineAsyncComponent

这个API主要是用来定义异步组件的,先来看下官方文档的介绍:

【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第8张图片
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第9张图片
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第10张图片

其实关于这个API的话,我个人觉得官方文档中介绍得已经非常清楚了,就是用来定义一个异步组件,然后利用Promise将其在应该渲染的时候加载出来。
关于其高阶用法的相关配置,大家可以去文档中自寻查找。
https://v3.cn.vuejs.org/api/global-api.html#defineasynccomponent

最后来看下 defineCustomElement

最开始看到这个API的名字的时候,我有点懵,以为是和component api相同的功能:创建一个自定义组件就完事儿了。结果忽略了最后一个单词:Element。
这个API是用来创建原生的自定义元素的,由它创建出来的组件经过注册可以在其他框架(不只是Vue)甚至脱离框架去使用。我们先来看看它的官方介绍:

【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第11张图片
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第12张图片

官方文档中介绍得非常简单。在文档中引出了一个原生API的概念:customElements。关于这个 API ,主要作用就是它能够使开发者能够将HTML页面的功能封装为 custom elements(自定义标签)。大家有兴趣可以去MDN上看看:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_custom_elements
言归正传,在这里,我抛出了自己脑海里的第二个问题:

问题二:用 defineCustomElement 生成的标签可以实现和其他Vue组件一样的自身状态管理和事件处理吗?

先来看看是否有同样的状态管理能力:

import { createApp, h, defineCustomElement, ref } from 'vue'

const app = createApp(App)

const MyVueElement = defineCustomElement({
  setup () {
    const count = ref(0)
    return () => h('div', {
      onClick: () => count.value++
    }, `点击${count.value}`)
  }
})

customElements.define('my-vue-element', MyVueElement)

我按照文档中所说的方式创建并注册了一个原生自定义元素 my-vue-element,然后我现在打开浏览器,直接在浏览器控制台中输入这两行代码:
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第13张图片
然后我看到在页面中确实渲染出了我们刚刚注册的组件,并且点击元素,其设置的count会正常增加,说明它确实是实现了自身的状态管理功能的:
【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第14张图片
OK,自身状态管理没问题,那么再来看看是否支持事件。我们需要稍微修改一下代码,然后先测试一下,普通的Vue 组件的自定义事件是否能跑通,防止代码写错了:

import { createApp, h, defineComponent } from 'vue'

const app = createApp(App)

const MyVueElement = defineComponent({
  props: {
    title: {
      type: String,
      required: true
    }
  },
  emits: ['update:title'],
  setup (props, { emit }) {
    return () => h('div', [
      h('div', props.title),
      h('input', {
        value: props.title,
        onInput: (e: any) => emit('update:title', e.target.value)
      })
    ])
  }
})

app.component('MyVueElement1', MyVueElement)

【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第15张图片
在这里插入图片描述
OK,普通的Vue自定义组件没问题,接下来看看 defineCustomElement 创建的原生自定义元素是否支持:

import { createApp, h, defineCustomElement } from 'vue'

const app = createApp(App)

const MyVueElement = defineCustomElement({
  props: {
    title: {
      type: String,
      required: true
    }
  },
  emits: ['update:title'],
  setup (props, { emit }) {
    return () => h('div', [
      h('div', props.title),
      h('input', {
        value: props.title,
        onInput: (e: any) => emit('update:title', e.target.value)
      })
    ])
  }
})

customElements.define('my-vue-element', MyVueElement)

【Vue3】defineComponent、defineAsyncComponent、defineCustomElement 介绍_第16张图片
在这里插入图片描述
结果得出,由 defineCustomElement 创建出来的组件没办法支持事件处理的。

OK,三种API差不多介绍完啦~目前我只研究到了这个程度,如大佬们看到有问题的地方,请多多指教哈

你可能感兴趣的:(vue3,typescript,vue.js,前端)