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操作。建议仅在确实需要动态调用的场景使用,常规需求优先使用标准组件化方案。

相关推荐
palpitation9735 分钟前
在Flutter中使用Builder的正确方式:一场context的教育
前端
Eliauk__37 分钟前
深入剖析 Vue 双向数据绑定机制 —— 从响应式原理到 v-model 实现全解析
前端·javascript·面试
代码小学僧37 分钟前
Cursor 的系统级提示词被大佬逆向出来了!一起来看看优秀 prompt是怎么写的
前端·ai编程·cursor
MrsBaek41 分钟前
前端笔记-Axios
前端·笔记
洋流44 分钟前
什么?还没弄懂关键字this?一篇文章带你速通
前端·javascript
晴殇i1 小时前
for...in 循环的坑,别再用它遍历 JavaScript 数组了!
前端·javascript
littleplayer1 小时前
iOS 单元测试详细讲解-DeepSeek
前端
littleplayer1 小时前
iOS 单元测试与 UI 测试详解-DeepSeek
前端·单元测试·测试
夜熵1 小时前
Vue中nextTick()用法
前端·面试
小桥风满袖1 小时前
Three.js-硬要自学系列15 (圆弧顶点、几何体方法、曲线简介、圆、椭圆、样条曲线、贝塞尔曲线)
前端·css·three.js