如何把vue组件在全局js调用的api中使用
前言背景
在项目开发,我们总是不可避免的能遇到需要通过 js 调用 api 在 html 中全局显示,以使灵活应付各种项目场景
例如:饿了么的 element-ui 的全局 api Message MessageBox Notification 等组件 或者是高德地图中的调用 自定义信息窗体 等api
但是通常我们会碰到,这些组件库提供的 api 并不能满足我们的个性化布局以及样式,无法做到还原设计稿或者是需求
如果使用html 字符串来传入 api 进行又使代码繁琐冗余,如果这些全局api里面的内容中,例如弹窗通知中里面还要用到别的组件该怎么办呢


思考办法
最好的体验应该是按照 vue组件 写法,去创建一个 vue 组件,来对内容进行个性化布局书写,那我们怎么把我们为创建的组件给它传入到这些 api 里面显示呢
我们的 dom 可以分为 真实DOM 以及 vue 的 VNode 虚拟 dom
在 vue 中,如果我们想要自定义这些全局 js 调用 api 里面的样式,肯定是使用 vue 的 VNode 虚拟 dom 更方便,也更自由灵活,方便书写我们的样式
那么我们如何来创建虚拟节点呢?
下面写一下 vue2 以及 vue3 的用法
vue2 写法
在 vue2 中我们可以使用 createElement 这个 api
或者使用 vue 的构造器extend 、 new 一个 Vue 来创建 Vue 实例,然后把创建后实例的$el 取出来放到需要使用的 api 上
具体怎么做看如下代码
首先创建一个自定义的 customComponent 组件,里面放我们想要显示的内容
<template>
<div class="customComponent">
<div>{{ name }}</div>
<div>
<i class="el-icon-platform-eleme" style="font-size: 22px"></i>
</div>
<el-button type="primary">按钮</el-button>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
default: "我是测试",
},
},
};
</script>
<style lang="less" scoped>
.customComponent{
display: flex;
align-items: center;
justify-content: space-between;
}
/deep/ .el-button{
font-size: 28px;
}
</style>
createElement
在 vue2 中使用 createElement 来创建 VNode 节点
一般情况下我们都是这样用的,传入普通的 html 元素
this.$createElement("div", {
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: "red",
fontSize: "14px",
},
// 普通的 HTML 特性
attrs: {
id: "foo",
},
});
如果这样书写样式以及灵活性就差很多了,也比较麻烦,而且还想要用到其他的组件改怎么做呢
但是其实这个api是可以把我们的组件传入的
并且可以传入相应的 props 参数
import customComponent from './components/customComponent/index.vue'
export default {
components: { customComponent }, // 注册
mounted() {
const element = this.$createElement('customComponent', { // 组件名称
props: {
name: '我是createElement创建的'
}
})
this.$notify({
dangerouslyUseHTMLString: true,
message: element,
});
}
}
效果如下,传入的参数、组件的样式、深度选择器 都生效了

extend 或者 new Vue 构造器
上面的方法虽然已经解决了我们的需求,但是这样的写法其实对于我们来说是比较繁琐的,代码可阅读性也差了点,那么能不能再我们的 .vue 组件里面去写好然后使用呢
那我们可以使用vue的 extend构造器 或者 Vue函数 来创建一个vue实例,然后通过获取实例的 $el 获取dom对象,这样更加灵活,并且涵盖场景多
首先可以在原先的组件文件夹下创建一个index.js文件
import Vue from 'vue'
import customComponent from './index.vue'
export const createComponent = (name = '') => {
const element = document.createElement('div') // 创建一个真实dom节点
// new Vue
const app = new Vue({
components: { customComponent },
template: `<customComponent :name="name"/>`, // 使用并传入props
data() {
return {
name,
};
},
});
// vue构造器extend
// const extendComponents = Vue.extend(customComponent)
// const app = new extendComponents({
// propsData: {
// name
// }
// })
app.$mount(element); // 挂载到真实节点
return app.$el
}
在饿了么组件中使用,注意不要使用innerHTML来转换字符串,它不会转换父节点
import { createComponent } from "./components/CustomComponents/index.js";
export default {
mounted() {
const element = createComponent('我是new Vue创建的')
console.log(element, 'element')
this.$notify({
message: element.outerHTML, // 把dom对象转为html字符串
dangerouslyUseHTMLString: true,
duration: 0
});
},
}

vue3 写法
同样先写一个组件
<template>
<div class="customComponent">
<div>{{ name }}</div>
<div>
<i class="el-icon-platform-eleme" style="font-size: 22px"></i>
</div>
<el-button type="primary">按钮</el-button>
</div>
</template>
<script setup lang="ts">
interface Props {
name: string;
}
const props = withDefaults(defineProps<Props>(), {
name: '我是测试',
});
</script>
<style lang="less" scoped>
.customComponent {
display: flex;
align-items: center;
justify-content: space-between;
}
:deep(.el-button) {
font-size: 28px;
}
</style>
h() 函数
这个vue3的api其实相当于vue2的createElement,只不过写法稍有区别
import { h } from "vue";
import { ElNotification } from "element-plus";
import CustomComponents from "./components/CustomComponents/index.vue";
onMounted(() => {
const element = h(CustomComponents, {
name: "我是createElement创建的",
});
console.log(element);
ElNotification({
message: element,
duration: 0,
});
});

createVNode() 函数
h() 函数和 createVNode() 函数都是创建 dom节点,作用是一样的,但是在 vue3 中createVNode() 函数的功能比 h() 函数要多且做了性能优化,渲染节点的速度也更快。
import { createVNode } from "vue";
onMounted(() => {
const element = createVNode(CustomComponents, {
name: '我是createVNode创建的'
})
console.log(element);
ElNotification({
message: element,
duration: 0,
});
});

在vue3 中 Vue.extend 构造器被移除了,所以我们也可以用 createApp 来创建一个vue应用实例,但是注意该实例与 项目中的 vue实例 没有任何关联
尾巴
这是本人理解的所有关于 把vue组件在全局js调用的api 如何使用的方法
可能细心的读者发现了,我展示的都是静态数据,这些方法并没有办法做成 实时数据更新
因为都是通过api调用显示的,如果想要实现数据的实时更新,我们需要手动封装一个自己专属的js调用组件,通过调用方法来传入数据实现更新
如果有这方面的需求可以看我的另一篇文章
转载自:https://juejin.cn/post/7243725204001947708