Skip to content

二.数据输入(动态)

前言

动态组件,即组件可以作为一个变量传入到某个组件中根据一些条件,动态地切换某个组件,或动态地选择渲染某个组件

  • 下拉菜单组件应该由两部分组成:
    • 选中项的文本
    • 待选菜单(默认隐藏)
  • 它的主要功能包括:
    • 鼠标经过下拉菜单组件,显示待选菜单
    • 鼠标滑出下拉菜单组件,隐藏待选菜单
    • 鼠标点击待选菜单中的条目,选中项文本更新,组件派发 change 事件

1. 动态组件

使用 Render 或 Function Render 可以解决动态切换组件的需求,不过那是基于一个 JS 对象(Render 函数),而 Vue.js 提供了另外一个内置组件<component>is特性,可以更好地实现动态组件。

父组件中一个<component>is的基本用法示例:

vue
<template>
  <div>
    <button @click="handleChange("A")">显示 a 组件</button>
    <button @click="handleChange("B")">显示 b 组件</button>
    <button @click="handleChange("C")">显示 c 组件</button>
    <component :is="component"></component>   <!-- 这里的is动态绑定的是一个组件对象(Object),它直接指向 a/b/c 三个组件中的一个 -->
  </div>
</template>
<script>
import componentA from './components/a.vue'
import componentB from './components/b.vue'
import componentC from './components/c.vue'
const com = {A:componentA,B:componentB,C:componentC}
export default {
  data (){
    return {
      component: com['A']
    }
  },
  methods: {
    handleChange(component){
        this.component = com[component]
    }
  }
}
</script>
  • 子组件:a.vue
vue
<template>
  <div>
    子组件:a.vue
  </div>
</template>
  • 子组件:b.vue
vue
<template>
  <div>
    子组件:b.vue
  </div>
</template>
  • 子组件:c.vue
vue
<template>
  <div>
    子组件:c.vue
  </div>
</template>

除了直接绑定一个 Object,还可以是一个 String,比如标签名,组件名。

2. 实现一个动态组件

下面的这个组件,将原生按钮 button 进行了封装,如果传入了prop:to,它会渲染为一个<a>标签,用于打开这个链接地址,如果没有传入to,就当做普通的 button 使用。

  • 封装组件:button.vue
vue
<template>
  <component :is="tagName" v-bind="tagProps">
    <slot></slot>
  </component>
</template>
<script>
export default{
 props:{
   to:{
     type:String,
     default:'
   },
   target:{
     type:String,
     default:"_self"
   }
 },
 computed:{
   tagName(){
     return this.to === ''?'button':'a' // 动态渲染不同的标签
   },
   tagProps(){ // 如果是链接,把这些属性绑定到component上
     let props = {}
     if(this.to){
       props = {
         target:this.target,
         href:this.to
       }
     }
     return props
   }
 }
}
</script>

使用组件:

vue
<template>
  <div>
    <my-button>普通组件</my-button>
    <my-button to="https://123.com">链接按钮</my-button>
    <my-button to="https://123.com" target="_blank">
      新窗口打开链接按钮
    </my-button>
  </div>
</template>
<script>
import myButton from "../component/button.vue";
export default {
  components: { myButton },
};
</script>

最终会渲染出一个原生button按钮和两个原生的链接a,且第二个点击会在新窗口中打开链接。

my-button 组件中component is绑定的就是一个标签名称 button/a,并且通过v-bind将一些额外的属性全部绑定到component上,

再回到第一个 a/b/c 组件的切换狩猎,如果这类组件,频繁切换,实际上组件是会重新渲染的,比如我们在组件 A 里加两个生命周期。

  • a.vue
vue
<template>
  <div>组件A</div>
</template>
<script>
export default {
  mounted() {
    console.log("组件创建了"); // 只要切换到 A 组件,`mounted`就会触发一次,
  },
  beforeDestory() {
    console.log("组件销毁了"); // 切换到其他组件,`beforeDesotory`也会触发一次,说明组件在重新渲染,这样有可能导致性能问题。
  },
};
</script>

使用keep-alive只有mounted触发了,如果不离开当前页面,切换其他组件beforeDestory不会触发

vue
<template>
  <keep-alive>
    <component :is="component"></component>
    <!-- 为了避免组件的重复渲染,使用`<keep-alive>`,这样组件就会被缓存起来 -->
  </keep-alive>
</template>

keep-alive 还有一些额外的 props 可以配置

  • include:字符串或正则表达式,只有名称匹配的组件会被缓存
  • exclude:字符串或正则表达式,任何名称匹配的组件都不会被缓存
  • max:数字,最多可以缓存多少组件实例

总结

合理使用动态组件可以让我们写出的代码更加简洁,减少冗余代码