vue(十二) 组件二 动态组件(Component)和异步组件(defineAsyncComponent)


组件注册

一个 Vue 组件在使用前需要先被“注册”,这样 Vue 才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册。

1.全局注册

使用 Vue 应用实例的 .component() 方法,让组件在当前 Vue 应用中全局可用。

import { createApp } from 'vue'
const app = createApp({})
app.component(
  // 注册的名字
  'MyComponent',
  // 组件的实现
  {
    /* ... */
  }
)
  1. 单文件组件,注册被导入的 .vue 文件:
    import MyComponent from './App.vue'
    app.component('MyComponent', MyComponent)
    // 可以链式调用
    app
      .component('ComponentA', ComponentA)
      .component('ComponentB', ComponentB)
      .component('ComponentC', ComponentC)
    // 全局注册的组件可以在此应用的任意组件的模板中使用:
    // <!-- 这在当前应用的任意组件中都可用 -->
    <ComponentA/>
    <ComponentB/>
    <ComponentC/>
    

注:所有的子组件也可以使用全局注册的组件,这意味着这三个组件也都可以在彼此内部使用。

  1. 存在的问题
  • 全局注册,没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
  • 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性

2.局部注册

局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。

  1. 使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册:
    import ComponentA from './ComponentA.vue'
    </script>
    
    <template>
      <ComponentA />
    </template>在这里插入代码片
    
  2. <script setup>,则需要使用 components 选项来显式注册:
    import ComponentA from './ComponentA.js'
    export default {
      components: {
        ComponentA
      },
      setup() {
        // ...
      }
    }
    
  3. components 对象里的属性,它们的 key 名就是注册的组件名,而值就是相应组件的实现。上面的例子中使用的是 ES2015 的缩写语法,等价于:
    export default {
      components: {
        ComponentA: ComponentA
      }
      // ...
    }
    //ComponentA 注册后仅在当前组件可用,而在任何的子组件或更深层的子组件中都不可用。
    

注:局部注册的组件在后代组件中不可用

3.组件名格式

使用 ComponentA作为组件名的注册格式,这是因为:

  • ComponentA是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。
  • <ComponentA/> 在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来。

在单文件组件和内联字符串模板中,我们都推荐这样做。但是,ComponentA 的标签名在 DOM 内模板中是不可用的,详情参见 DOM 内模板解析注意事项。

Vue 支持将模板中使用 kebab-case 的标签解析为使用 ComponentA注册的组件。这意味着一个以 ComponentA为名注册的组件,在模板中可以通过 <component-a> <component-a> 引用。能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板。

动态组件

  1. 动态组件的概念

多个组件通过component标签挂载在同一个组件中,通过触发动作进行动态切换。常搭配<keep-alive></keep-alive>使用,多用于tab栏的切换等场景

  1. 动态组件的使用和实现
    通过设置<component>标签的is属性来指定要渲染的组件。
// Component.vue
<template>
  <div>
    <button
      v-for="(item, index) in tabs"
      :key="index"
      @click="currentTab = item.comName"
    >
      {{ item.name }}
    </button>
    <component :is="currentTab" class="tab"></component>
  </div>
</template>

<script lang="ts">
import { reactive, reactive, shallowRef } from 'vue'
import ComA from './components/ComA.vue'
import ComB from './components/ComB.vue'
import { reactive, shallowRef } from 'vue'
export default {
  setup () {
		  // reactive 会使数据变成响应式,此处为了节省性能开销,可以使用 shallowRef 或者 markRaw 跳过 proxy 代理
		const currentTab = shallowRef(ComA)
		const tabs = reactive([
		  {
		    name: 'ComA',
		    comName: shallowRef(ComA)
		  },
		  {
		    name: 'ComB',
		    comName: shallowRef(ComB)
		  },
		])
	  	return {
	  		currentTab,
	  		tabs,
	    }
  	}
  }
</script>
  1. vue 3 和vue2.0区别
  • <component :is=>标签可以动态渲染不同的组件,可以根据变量的值来动态切换不同的子组件。
  • vue 2 中is接收的是组件名称,
  • vue 3 中is接收的是组件实例。
  • 也可以使用 is attribute 来创建一般的 HTML 元素。
  • 为了节省性能开销,可以使用shallowRef或者markRaw跳过proxy代理。
  • 当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。可以通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态。

异步组件

1.基本使用

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

  1. 获取组件

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

    defineAsyncComponent 方法接收一个返回 Promise 的加载函数。这个 Promise 的 resolve 回调方法应该在从服务器获得组件定义时调用。你也可以调用 reject(reason) 表明加载失败。

  2. ES module动态导入

    ES 模块动态导入也会返回一个 Promise,所以多数情况下会将它和 defineAsyncComponent 搭配使用。类似 Vite 和 Webpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),因此可以用它来导入 Vue 单文件组件:

    <template>
      <div>
        异步组件
        <AsyncComponent />
      </div>
    </template>
    
    <script lang="ts">
    import { defineAsyncComponent, defineComponent } from 'vue'
    const AsyncComponent = defineAsyncComponent(() => import('./components/myAsyncCom.vue'))
    export default defineComponent({
      components: {
        AsyncComponent
      },
      setup () {
        return {
        }
      }
    })
    </script>
    <style scoped>
    </style>
    

    最后得到的AsyncComp是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的props和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。

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

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

    也可以直接在父组件中直接定义它们:

    <template>
      <div>
        异步组件
        <AsyncComponent />
      </div>
    </template>
    
    <script lang="ts">
    import { defineAsyncComponent, defineComponent } from 'vue'
    const AsyncComponent = defineAsyncComponent(() => import('./components/myAsyncCom.vue'))
    export default defineComponent({
      components: {
        AsyncComponent
      },
      setup () {
        return {
        }
      }
    })
    </script>
    <style scoped>
    </style>
    

2.加载与错误状态

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

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

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

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})
  • 如果提供了一个加载组件,它将在内部组件加载时先行显示。在加载组件显示之前有一个默认的 200ms 延迟——这是因为在网络状况较好时,加载完成得很快,加载组件和最终组件之间的替换太快可能产生闪烁,反而影响用户感受。
  • 如果提供了一个报错组件,则它会在加载器函数返回的 Promise 抛错时被渲染。可以指定一个超时时间,在请求耗时超过指定时间时也会渲染报错组件。

3.搭配Suspense 组件使用

异步组件可以搭配内置的 <Suspense> 组件一起使用,后面组件会对 <Suspense> 进行专门介绍。

  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值