Vue2函数式组件实战:手写可调用的动态组件,适用于toast轻提示、tip提示、dialog弹窗等

Vue2函数式组件实战:手写可调用的动态组件

一、需求场景分析

在开发中常遇到需要动态调用的组件场景:

  • 全局弹窗提示
  • 即时消息通知
  • 动态表单验证
  • 需要脱离当前DOM树的悬浮组件

传统组件调用方式的痛点:必须预先写入模板,可能还要用ref绑定组件

html 复制代码
<template>
  <!-- 必须预先写入模板 -->
  <MyComponent v-if="show" :content="text"/>
</template>

二、函数式组件实现方案

js 复制代码
//myComponent.js
import MyComponent from './MyComponent.vue';
import Vue from 'vue';

let instance;

//实例初始化
function initInstance(){
    //先销毁已有实例,单例模式
    if(instance){
        instance.$destroy();
        // 显式移除DOM
        instance.$el.parentNode.removeChild(instance.$el) 
    }
    instance = new (Vue.extend(MyComponent))({
        el: document.createElement('div')
    });

    //自定义事件,控制组件显隐
    instance.$on('show',value=>{
        instance.value = value
    })
}

//函数式组件调用
function myComponent(options){
    if(!instance){
        initInstance();
    }
    //复制传参并控制显示组件
    Vue.nextTick(() => {
        Object.keys(options).forEach(key => {
            instance[key] = options[key]
        })
        instance.value = true
    })
}

myComponent.Component = MyComponent;
export default myComponent;
html 复制代码
<template>
  <transition name="fade">
    <div v-show="value" class="component-wrapper">
      <div class="header">
        <slot name="header">{{ title }}</slot>
      </div>
      <div class="content">
        {{ content }}
      </div>
      <div class="footer">
        <button @click="handleClose">关闭</button>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  props: {
    content: String,
    title: {
      type: String,
      default: '提示'
    }
  },
  mounted() {
    // 智能挂载到最近的dialog-container
    const container = document.querySelector('.dialog-container') || document.body
    container.appendChild(this.$el)
  },
  methods: {
    handleClose() {
            this.$emit('show',false);
    }
  },
  beforeDestroy() {
    // 安全移除DOM
    if (this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
  }
}
</script>

<style scoped>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

三、综合分析

使用方式对比

传统组件调用

html 复制代码
<template>
  <button @click="show = true">打开</button>
  <MyComponent v-if="show" @close="show = false"/>
</template>

<script>
export default {
  data: () => ({
    show: false
  })
}
</script>

函数式组件调用

js 复制代码
import { myComponent} from '@/utils/myComponent'

// 任意位置调用
showComponent({
  content: '操作成功',
  title: '系统提示',
})

弹窗需要考虑的问题

  1. 当页面滚动时,弹窗是否需要滚动?是否会被其他元素遮挡
  2. 移动端如何避免点击穿透问题
  3. 当频繁触发弹窗时,如何确保弹窗的内容/显隐时间正常

进阶优化建议

  1. 多实例支持
js 复制代码
function createInstance() {
  return new (Vue.extend(MyComponent))({
    el: document.createElement('div')
  })
}

export function showComponent(options) {
  const instance = createInstance()
  // 单独管理实例
}
  1. 如果是vue3定制函数式组件,可以使用teleport将组件传送至最外层

函数式组件是Vue2中实现命令式调用的利器,但需要特别注意实例管理和DOM操作。建议仅在确实需要动态调用的场景使用,常规需求优先使用标准组件化方案。

相关推荐
仟濹35 分钟前
【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】
大数据·前端·爬虫·数据挖掘·数据分析·html
小小小小宇2 小时前
前端WebWorker笔记总结
前端
小小小小宇2 小时前
前端监控用户停留时长
前端
小小小小宇2 小时前
前端性能监控笔记
前端
烛阴3 小时前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小53 小时前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步
穗余3 小时前
NodeJS全栈开发面试题讲解——P6安全与鉴权
前端·sql·xss
穗余4 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js
航Hang*4 小时前
WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
前端·笔记·edge·less·css3·html5·webstorm
江城开朗的豌豆4 小时前
JavaScript篇:a==0 && a==1 居然能成立?揭秘JS中的"魔法"比较
前端·javascript·面试