likes
comments
collection
share

如何把vue组件在全局js调用的api中使用

作者站长头像
站长
· 阅读数 17

前言背景

在项目开发,我们总是不可避免的能遇到需要通过 js 调用 api 在 html 中全局显示,以使灵活应付各种项目场景

例如:饿了么的 element-ui 的全局 api Message MessageBox Notification 等组件 或者是高德地图中的调用 自定义信息窗体 等api

但是通常我们会碰到,这些组件库提供的 api 并不能满足我们的个性化布局以及样式,无法做到还原设计稿或者是需求

如果使用html 字符串来传入 api 进行又使代码繁琐冗余,如果这些全局api里面的内容中,例如弹窗通知中里面还要用到别的组件该怎么办呢

如何把vue组件在全局js调用的api中使用

如何把vue组件在全局js调用的api中使用

思考办法

最好的体验应该是按照 vue组件 写法,去创建一个 vue 组件,来对内容进行个性化布局书写,那我们怎么把我们为创建的组件给它传入到这些 api 里面显示呢

我们的 dom 可以分为 真实DOM 以及 vue 的 VNode 虚拟 dom

在 vue 中,如果我们想要自定义这些全局 js 调用 api 里面的样式,肯定是使用 vue 的 VNode 虚拟 dom 更方便,也更自由灵活,方便书写我们的样式

那么我们如何来创建虚拟节点呢?

下面写一下 vue2 以及 vue3 的用法

vue2 写法

在 vue2 中我们可以使用 createElement 这个 api

或者使用 vue 的构造器extendnew 一个 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,
    });
  }
}

效果如下,传入的参数、组件的样式、深度选择器 都生效了

如何把vue组件在全局js调用的api中使用

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
    });
  },
}

如何把vue组件在全局js调用的api中使用

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,
  });
});

如何把vue组件在全局js调用的api中使用

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,
  });
});

如何把vue组件在全局js调用的api中使用

在vue3 中 Vue.extend 构造器被移除了,所以我们也可以用 createApp 来创建一个vue应用实例,但是注意该实例与 项目中的 vue实例 没有任何关联

尾巴

这是本人理解的所有关于 把vue组件在全局js调用的api 如何使用的方法

可能细心的读者发现了,我展示的都是静态数据,这些方法并没有办法做成 实时数据更新

因为都是通过api调用显示的,如果想要实现数据的实时更新,我们需要手动封装一个自己专属的js调用组件,通过调用方法来传入数据实现更新

如果有这方面的需求可以看我的另一篇文章