Vue.js 动态向页面中添加已有自定义组件

589 阅读1分钟

使用场景:

根据用户的输入信息,批量生成二维码图片并下载,下载完成后删除对应的DOM元素

使用插件 vue-qr、html2canvas

使用技术 Vue.extend


当然可以将组件提前写入html中,但是实际需求中,提前写入标签可能会影响页面性能,以下只是个人在开发使用的一种方法

动态添加组件的方法:

import QrCodeImage from '@/ik-components/QrCodeImage.vue' // 待添加的组件

const codeArr = ['12', '13', '14', '15'] // 用户输入的,待生成二维码的数据
let numIndex = 0 // 计数器,确保所有的二维码都正常保存下载使用

const DomConstructor = Vue.extend(QrCodeImage) // 创建组件的构造函数

codeArr.forEach((item, index) => {
    // 实例化组件
    const domObj = new DomConstructor({
      propsData: { // 组件内所需的参数
        codeInfo: item,
        uniqueId: index
      }
    })

    // 建立虚拟Dom
    domObj.$mount()
    // 将Dom挂在到页面上,html2canvas所需
    document.getElementById('app').appendChild(domObj.$el)
    domObj
      .toSave()
      .then(() => {
        domObj.$el.remove() // 保存下载成功后移除Dom元素
        numIndex++ // 下载完成后计数器加一
        if (numIndex === codeArr.length) console.log('全部成功')
      })
      .catch((err) => {
        console.error('失败:', err)
      })
})

生成二维码的组件:

待动态插入页面的组件

<template>
  <div :id="'qrCodeImage' + uniqueId" class="qr_code_content">
    <vue-qr :text="codeInfo" :size="240" />
    <div class="qrcode_info_content">
      {{ codeInfo }}
    </div>
  </div>
</template>

<script>
// https://www.npmjs.com/package/vue-qr
import VueQr from 'vue-qr'
import html2canvas from 'html2canvas'

export default {
  name: 'QrCodeImage',
  components: {
    VueQr
  },
  props: {
    // 待生成二维码的数据信息
    codeInfo: {
      type: String,
      default: ''
    },
    // DOM元素的唯一ID
    uniqueId: {
      type: Number,
      default: 0
    }
  },

  methods: {
    // 保存二维码
    toSave() {
      let resolveFunc, rejectFunc
      const saveSuccessPromise = new Promise((resolve, reject) => {
        resolveFunc = resolve
        rejectFunc = reject
      })

      setTimeout(() => { // 确保二维码图片生成后才可进行保存下载操作
        html2canvas(document.getElementById('qrCodeImage' + this.uniqueId))
          .then((canvas) => {
            const saveUrl = canvas.toDataURL('image/png')
            const aLink = document.createElement('a')
            const blob = this.base64ToBlob(saveUrl)
            const evt = document.createEvent('HTMLEvents')
            evt.initEvent('click', true, true)
            aLink.download = `批量生成二维码-${this.codeInfo}.jpg`
            aLink.href = URL.createObjectURL(blob)
            aLink.click()

            if (aLink.href) console.log(`二维码'${this.codeInfo}'保存成功`)
            resolveFunc()
          })
          .catch(rejectFunc)
      }, 0)

      // 返回一个Promise对象,确保二维码图片正常/异常下载都会被记录
      return saveSuccessPromise
    },

    // 图片转base64
    base64ToBlob(code) {
      const parts = code.split(';base64,')
      const contentType = parts[0].split(':')[1]
      const raw = window.atob(parts[1])
      const rawLength = raw.length
      const uInt8Array = new Uint8Array(rawLength)
      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i)
      }
      return new Blob([uInt8Array], { type: contentType })
    }
  }
}
</script>

<style lang="scss" scoped>
.qr_code_content {
  width: 250px;
  margin: 0 auto;
  padding: 10px;
  border-radius: 10px;
  background: #ffffff;
  border: #909399 1px solid;
  display: flex;
  align-items: center;
  flex-direction: column;
}

.qrcode_info_content {
  width: 100%;
  padding: 0 5px;
  color: #000000;

  font-size: 20px;
  font-weight: bold;
  line-height: 22px;
  text-align: center;
}
</style>