欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue2.x将vue文件编写的组件使用API方式动态创建(vue.extend)

程序员文章站 2022-03-11 21:40:31
...

文章参考

  1. Vue.extend 看完这篇,你就学废了
  2. vue2.x挂载$mount、Vue.extend()函数的作用——由elementUI message方法联想
  3. Vue2.x 如何将vue文件编写的组件变为注册到全局组件?Vue.extend()

问题描述

  1. 在工作中使用的是Vue 脚手架搭建的,现在需要在地图上弹出一个pop 弹窗,问题是这个弹窗里面的内容全部要自己通过html写出来,因此官方给出的代码是自己写一个html字符串
let divStr = "<br/><div οnclick="myfunc()">我是弹窗显示的内容</div>",
let ele = document.createElement("div");
ele.innerHTML = divStr;
  1. 如果按照上面的写法,就需要 定义全局方法,污染全局,代码可读性降低不利于维护
  2. CSS 也需要定义全局的,不符合vue 将方法和css 放到一个组件的设计原则

解决办法

解决思路

  1. 能否用vue文件编写好组件代码,js逻辑和css全部写到vue文件中,然后创建vue组件的实例,将vue.$el 赋值给创建的div中?
  2. 如果能以这样的方式实现,就可以让js的方法作用范围在组件内部,在组件定义的css也只作用于组件,不影响全局

解决方式 Vue.extend() API

Vue.extend返回的是一个扩展实例构造器,也就是预设了部分选项的Vue实例构造器,但未曾实例化,可以理解为创建一个子类,然后让它继承Vue身上的一些功能

1. 定义好面板vue组件,将css 、js 、html 集成到一个文件中

<template>
  <div class="MapRoadPanel">
    <div class="MapRoadPanel--title">
      <span>{{markerObj.roadName}}</span>
      <span class="close h-icon-close" @click="closePanelAction"></span>
    </div>
    <div class="MapRoadPanel--content">
      <div class="MapRoadPanel--content__item">
        <div class="label">道路代码</div>
        <div class="value">{{markerObj.roadCode}} | {{markerObj.roadName}}</div>
      </div>
      <div class="MapRoadPanel--content__item">
        <div class="label">路段代码</div>
        <div class="value">{{markerObj.roadSectionCode}} | {{markerObj.roadSectionName}}</div>
      </div>
      <div class="MapRoadPanel--content__item">
        <div class="label">违禁图片</div>
        <div class="value">
          <img :src="markerObj.logoImageUrl">
        </div>
      </div>
    </div>
    <div class="MapRoadPanel--footer">
      <el-button type="primary" class="btn" @click="closePanelAction">确定</el-button>
    </div>
  </div>

</template>

<script>
export default {
  data () {
    return {
      markerObj: {
        // roadName: '长沙市南二环',
        // roadCode: '51000',
        // logoImageUrl: 'https://baidu.com/dB9I=',
        // createTime: '2021-01-29T17:11:31.849',
        // roadSectionName: '南天门',
        // roadSectionCode: '2102',
        // gpsLocation: '112.945129,28.161757',
        // dateTime: '2021-01-29 17:11:31',
        // id: '202101291711310003'
      }
    }
  },
  mounted () {},
  methods: {
    closePanelAction () {
      alert('closePanelAction')
    }
  }
}
</script>

<style lang="scss" scoped>
.MapRoadPanel {
  width: 336px;
  height: 422px;
  background: #fff;
  padding: 0 12px;
  position: relative;
  &--title {
    line-height: 40px;
    font-size: 12px;
    color: #4d4d4d;
    letter-spacing: 0;
    border-bottom: 1px solid #efefef;
    .close {
      font-size: 24px;
      position: relative;
      top: 8px;
      float: right;
      cursor: pointer;
    }
  }
  &--content {
    &__item {
      padding-top: 12px;
      .label {
        font-size: 12px;
        color: rgba(0, 0, 0, 0.4);
        letter-spacing: 0;
        line-height: 20px;
      }
      .value {
        font-size: 14px;
        color: rgba(0, 0, 0, 0.7);
        letter-spacing: 0;
        line-height: 26px;
        img {
          width: 100%;
          height: auto;
          max-height: 160px;
        }
      }
    }
  }
  &--footer {
    height: 59px;
    background-color: rgba(0, 0, 0, 0.04);
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    .btn {
      float: right;
      position: relative;
      top: 12px;
      right: 12px;
      background: #2080f7;
      width: 96px;
      height: 32px;
      color: #fff;
      line-height: 32px;
      text-align: center;
      cursor: pointer;
    }
  }
}
</style>

2. 目标组件,引用子组件,覆盖默认组件的data 和 方法

<script>
import Vue from 'vue'
import MapRoadPanel from '@/components/illegalMapConfig/MapRoadPanel'
import locationImg from '@/assets/images/icons/location.png'
import bigLocationImg from '@/assets/images/icons/bigLocation.png'
import { ajaxCtx } from '@/api/config.js'

export default {
  provide: function () {
    return {
      mapContext: this // 把当前Vue对象提供给所有子组件可以访问
    }
  },
  data () {
    // 根据定义好的视图
    const MyMapRoadPanel = Vue.extend(MapRoadPanel)
    return {
      MyMapRoadPanel, // 自定义弹窗层的组件
      locationImg,
      bigLocationImg,
      selectRoadObj: null, // 用户选中的road对象
    }
  },
  components: {
    selectConfigPanel,
    addConfigPanel
  },
  watch: {
    // 监听用户是否有选择道路
    selectRoadObj (newObj) {
      // 重新渲染图标
      this.addMarker()
      // 如果有值,则弹出详情,没有则隐藏
      if (newObj) {
        this.showSimplePopup()
      }
    }
  },
  mounted () {
    this.initMap()
  },
  methods: {
    // 返回组件的HTML
    getPanelCompHtml (markerObj, removePop) {
      const that = this
      const onePanel = new this.MyMapRoadPanel({
        data: {
          markerObj
        },
        methods: {
          closePanelAction: function () {
            that.selectRoadObj = null
          }
        }
      })
      // $mound() 方法执行之后,才会生成 $el,即创建dom 节点,视图和方法关联
      onePanel.$mount()
      return onePanel.$el
    },
    // 显示弹出层
    showSimplePopup (pointArr) {
      const ele = document.createElement('div')
      const panelDom = this.getPanelCompHtml(this.selectRoadObj, removePop)
      ele.appendChild(panelDom)
  }
}
</script>

注意:

  • 自定义组件没有注册到当前组件中,因此 自定义的组件 this ,代表的是自定义组件,而不是业务逻辑中的组件
  • 自定义组件没有注册到当前组件中,因此 this.$parent 为 undefined