如何优雅在vue3中编写命令式Confirm组件

2,055 阅读2分钟

在编写7享项目的时候,我发现一个编写问题,就是我的自定义弹窗组件会在项目中会多次重复调用,并且使用起来不方便。比如我使用组件的地方要先import组件进来,组件注册,再然后给组件加ref引用,最后调用组件的方法来控制状态。那有什么办法可以通过简单方式来调用自己的组件了呢? 答案是有的,我们直接通过命令式来调用组件,而无需别的操作。也就是这样调用:

this.$success()

没有import,没有ref,没有组件注册,这样就能调用自己的组件,是不是更方便了。

编写组件

写出组件(完整代码放在我的github仓,这里以success组件为例)

跟往常编写其他的组件一样编写组件,这里只放出主要的 js 部分

const Success = {
  name: 'success',
  inject: ['text']
}
export default Success

编写根节点

用于将组件挂载在根节点上,而这里没有挂到本来项目的根节点上的原因是避免受到原来项目的样式影响

let root = document.createElement('div')
root.setAttribute('id', 'my')

挂载组件

const DEFAULT = '成功'
import {createApp} from 'vue'

let instanceCache;
function mountComponent(arg) {
  if (!instanceCache) {
    instanceCache = createApp(Success) // 构建应用上下文的应用实例
    let text = arg[0]?arg[0]:DEFAULT
    instanceCache.provide('text', text) // 外部注入到组件的值
    document.body.appendChild(root); // 本根节点添加到body
    instanceCache.mount('#my') // 挂载组件
  }
}

编写调用函数

每次组件不使用时,需从根目录上卸载组件实例,最后将根节点从文档中移除,同时将临时的实例缓存也清空,防止内存泄露

const INTERVAL = 3500
export const success = function () {
  let arg = arguments
  mountComponent(arg)
  setTimeout(() => {
    instanceCache.unmount(root);
    document.body.removeChild(root);
    instanceCache = ''
  }, INTERVAL)
}

编写调用命令

想要通过一个函数来调用组件,我这里通过官方提供的 app.config.globalProperties 来暴露我这个方法,从而实现命令式调用我的组件

import {createApp} from "vue";
import {success} from './plugin/success'
import App from "./App";
const app = createApp(App)
app.config.globalProperties.$success = success

这写法看上去没啥问题,但这方法每次调用自己的组件,都要这样 import一次自己的组件,这显然不是我想要的。那有什么办法可以自动帮我导入组件,并且实现组件的命令式的自定义呢?

关键一步就是通过 webpack 提供的属性 require.context 来创建自己的上下文,它允许您传递要搜索的目录,指示是否也应搜索子目录的标志以及用于匹配文件的正则表达式

简单来说,可以通过该属性可以获取到放在指定范围内满足条件的文件。

我利用该属性可以获取到指定文件夹内的自定义好的插件信息,通过collectPlugins.keys()获取到的文件数组来遍历实现文件的命令式api注册.

// 收集在文档中所有插件
const collectPlugins = require.context('./plugin', true, /.vue$/)

export const createAPI = (app) => {
    collectPlugins.keys().forEach(file_name => {
        // 拿到每个组件的name值,这要求每个组件都要设置name
        let fileName = collectPlugins(file_name).default.name
        // 检测组件内是否有命名name
        if (fileName===undefined ||fileName === '' ){
            throw '请设置组件的名字'
        }
        // 检测组件内的名字是否与组件的命名是否一致
        if (file_name.match(/([A-Za-z]+)/g)[0]!= fileName) {
            throw fileName + '组件里面的name与组件命名不一致'
        }
        // 注册
        let fn = collectPlugins(file_name)[fileName]
        let api = '$' + fileName
        app.config.globalProperties[api] = fn
    })
}

运行

将函数 createAPI() 导入至vue入口文件 main.js

import { createApp } from 'vue'
import {createAPI} from "./plugin";
import App from './App.vue'
const app = createApp(App)
createAPI(app)

最后

演示网址:www.ifenghua.top/mobile ,该项目基于vue3建立,大伙可自行注册账号和密码后方可使用。

github:github.com/dengguochao…

最后,大伙别忘给个star呗,您的star是我创造最大的动力。