vue3和vue2如何写一个用函数控制组件开关的组件

1,173 阅读3分钟

缘由

最近写了一个检测更新的打包发布机制。作用在于线上jekins打包的时候通过nodejs打包的时候自动修改版本号,然后前端检测的时候发现不一致那么就提示可能存在更新。

文章地址:webpack项目打包或git提交更新版本号,检测项目版本更新实现

其中写了一个函数弹窗,但是vue2还好,vue3啊,翻死了官网文档都找不到。现在大家百度,得到的很多都是通过createApp控制写的。但是都没有做一件事,那么就是如果保证单例模式。但是实际上,通过翻看element-plus的message部分源码还有ant-desgin部分源码得到其实我们不应该用createApp实现。有更好的方式(吐槽,感觉不如vue2来的简单)。

一、先实现弹窗样式代码

vue3实现部分和vue2有区别,但是实际上差异很小,等下会说注意点

<template>
  <div class="popup-version-tips" v-if="show">
    <div class="title">版本更新提示</div>
    <div class="tips">
      <span>您有新的版本更新,请尽快更新</span>
    </div>
    <div class="operation">
      <el-button size="small" @click="noUpdate">稍后手动刷新</el-button>
      <el-button size="small" type="primary" @click="toUpdate">点击立刻更新</el-button>
    </div>
  </div>
</template>

<script>
import { ElButton } from 'element-plus'
export default {
  components: { ElButton },
  name: 'PopupVersionTips',
  data() {
    return {
      show: true,
    }
  },
  methods: {
    noUpdate() {
      this.show = false
    },
    toUpdate() {
      this.show = false
      window.location.reload(true)
    },
  },
}
</script>

<style scoped lang="scss">
.popup-version-tips {
  position: fixed;
  z-index: 9999;
  right: 0;
  bottom: 0;
  background: var(--white);
  box-shadow: var(--shadow-balck);
  width: 300px;
  height: 200px;
  display: flex;
  flex-direction: column;
  .title {
    border-bottom: var(--border-color-1) 1px solid;
    font-size: var(--h4-16);
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .tips {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    word-break: break-all;
    padding: 20px;
    overflow: auto;
  }
  .operation {
    display: flex;
    justify-content: center;
    padding-bottom: 20px;
  }
}
</style>

1、vue2和vue3上述代码需要注意点

vue2部分是不需要import { ElButton } from 'element-plus'再声明一次组件的,vue3内部机制和jsx相似,所以需要独立声明,否则大家会看到关于组件部分代码是无法有正确的样式展示的。也可能是和vue3实现弹窗渲染的函数和vue2不一致导致的吧

其余部分完全可以直接抄到vue2下面。没有差异。

二、vue2的index.js部分代码(弹窗控制)

vue2主要通过Vue.extend(组件)来使得组件导出

代码部分

import Vue from 'vue'
import PopupVersionTips from './index.vue'

const PopupVersionTipsConstructor = Vue.extend(PopupVersionTips)
const instance = new PopupVersionTipsConstructor({})

const Version = () => {
  instance.show = true
  instance.$mount() // 卸载实例
  document.body.appendChild(instance.$el)
}

export default Version

1、创建实例

const PopupVersionTipsConstructor = Vue.extend(PopupVersionTips)
const instance = new PopupVersionTipsConstructor({})

2、核心在于$mount()

这个可以让组件重新渲染的时候在原来渲染的地方被清空

然后让document.body.appendChild(instance.$el)再加载进去就又展现了

三、vue3的index.js实现

vue3,差异很大,渲染方式也很大。偏偏是官方没有文档啊,还好有element-ui等开源的库。否则我们这种菜鸟真的无力吐槽。恨不得写全局组件了。

代码:

import { h, render } from 'vue'
import PopupVersionTips from './index.vue'

let container = document.createElement('div')
const Version = () => {
  const vm = h(PopupVersionTips, {})

  render(null, container)
  render(vm, container)

  document.body.appendChild(container.firstElementChild)
}

export default Version

1、h(),

其中h函数实际上是createVnode的二次封装。在element-ui的源码中使用的是createVnode,但是我看官方部分两者的功能应该是一致的。实现了一下,发现确实如此。这里的功能实际上和vue2的Vue.extend功能是一致的

2、render()函数

render函数的作用和之前的$mount功能非常相似。但是又不一样。主要是代码上看我们又创建了一个div容器。这个容器实际上不会被渲染到页面上,但是又非常重要。我没有仔细去看过官方对于extend的实现。

原本的extend部分能够自动追索原渲染地址,然后清空数据。但是这里创建div容器的作用就是定位和虚拟dom渲染。必须通过容器渲染组件才可以展现。然后组件需要清空,则需要原本容器的定位。

四、感谢观看。

这篇文章非常浅显。很多api的实际应用并没有讲解,大家有需要的部分请翻阅vue官方文档

vue2:cn.vuejs.org/v2/api/#Vue…

vue3:v3.cn.vuejs.org/guide/rende…