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

前言背景

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

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

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

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

微信截图_20230509102610.png

微信截图_20230509102516.png

思考办法

最好的体验应该是按照 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,
    });
  }
}

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

image-20230515121131877.png

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

image-20230515121242942.png

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

image-20230515154046590.png

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

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

尾巴

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

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

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

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYcLZWjB' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片