Vue文件转成Dom方案
问题
开发中我们经常会遇到引入一些成熟开发库的情况,如echarts、高德地图AMap、 leaflet等等,在一些事件响应的触发上,是这些库所提供的api进行的;以高德地图为例,列举了存在的问题:
- 在弹窗中,仅支持拼接字符串的方式;
- 当需要的在弹窗的dom上绑定事件的时,无法调取当前组件的事件;
- 字符串中充斥着大量内联样式,可扩展性、阅读性差;
- 对数据的操作不清晰;
如下代码
let content = `<div>
<div class='popup-content'>
<div class='content-padding'>北京市</div>
<div class='content-padding'>设备总数:0</div>
<div class='content-padding'>工厂总数:0</div>
</div>
<div class='info-sharp'></div>
<a class='adcombo-close' href="javascript: void(0)" @click='closeInfoWindow()'></a>
</div>`;
以上代码仅仅为一个简单的例子,如果页面弹窗结构以及内容复杂可想而知开发以及修改的困难成倍增长。
解决方法
- 使用Vue的extend()方法创建一个组件构造器
- 然后实例化创建的组件构造器
- 手动地挂载一个未挂载的实例
- 返回 Vue 实例使用的根 DOM 元素。
实例演示
- 新建一个通用方法,包含以上四步骤
// vueToDom.js
import Vue from 'vue'
export function CreateDom(obj){ // 创建一个构造函数
const vm_obj = obj; // 存储传入的参数对象
function _getDomItem(){ // 定义一个转换dom方法
const extendVM = Vue.extend(vm_obj); // 创建一个vue构造器
const vue_tmp = new extendVM(); // 实例化构造器
const dom = vue_tmp.$mount().$el; // 手动地挂载一个未挂载的实例,并获取跟元素
return dom;
}
return {
getDomItem: _getDomItem
}
}
- 根据需求,构造自己需要的vue文件
// deviceInfoWindow.vue
<template>
<div>
<div class="popup-content">
<div class="content-padding">北京市</div>
<div class="content-padding">设备总数:0</div>
<div class="content-padding">工厂总数:0</div>
</div>
<div class="info-sharp"></div>
<a
class="adcombo-close"
href="javascript: void(0)"
@click="closeInfoWindow()"
></a>
</div>
</template>
<script>
export default {
components: {},
props: {
infoWindow: {
type: Object,
default: () => {
return {};
}
},
},
data() {
return {};
},
created() {},
mounted() {},
methods: {
closeInfoWindow() {
this.infoWindow.close();
},
},
};
</script>
<style scoped lang="scss">
.popup-content {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
background: rgba(19, 18, 18, 0.7);
border-radius: 4px;
text-align: left;
border: 1px solid transparent;
// width: 100px;
height: 100px;
color: #fff;
}
.content-padding {
padding: 15px 40px 0px 15px;
}
.info-sharp {
// bottom: 0;
left: 50%;
margin-left: -8px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid rgba(19, 18, 18, 0.7);
position: absolute;
}
.info-sharp::after {
position: absolute;
content: "";
margin-left: -8px;
margin-top: -7px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid rgba(0, 0, 0, 0.3);
filter: blur(2px);
z-index: -1;
}
.adcombo-close {
position: absolute;
top: 4px;
right: 4px;
background: url(https://webapi.amap.com/images/close.png) center center
no-repeat;
width: 18px;
height: 18px;
text-indent: -9999em;
}
</style>
- 在需要使用以上组件的vue文件中引入
<template>
<div class="device-map">
<div class="device-content-total">
<div class="device-total">
<span class="content">{{ $t("deviceMap.deviceTotal") }}</span>
<span class="number">5</span>
</div>
<div class="factory-total">
<span class="content">{{ $t("deviceMap.factoryTotal") }}</span>
<span class="number">5</span>
</div>
</div>
<div class="map-content" :id="mapId"></div>
</div>
</template>
<script>
import { CreateDom } from "@/utils/vueToDom";
import DeviceInfoWindow from "./deviceInfoWindow";
export default {
name: "DeviceMap",
data() {
return {
map: null,
mapId: "deviceMapId",
mapPlugin: {},
provincePolygons: [],
infoWindow: null,
};
},
created() {
},
mounted() {
this.$nextTick(() => {
this.initAmapPlugin();
this.initAmap(this.mapId);
this.initMapEvent(this.map);
});
},
methods: {
// 初始化地图
initAmap(mapId) {
this.map = new AMap.Map(mapId, {
viewMode: "2D", // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D',
zoom: 4, //初始化地图层级
center: [116.397428, 39.90923], //初始化地图中心点
lang: this.$i18n.locale === "zh" ? "zh_cn" : "en",
});
},
// 初始化高德地图插件
initAmapPlugin() {
//异步同时加载多个插件
AMap.plugin(["AMap.Geocoder", "AMap.DistrictSearch"], () => {
this.mapPlugin["geocoder"] = new AMap.Geocoder({ extensions: "all" });
this.mapPlugin["districtSearch"] = new AMap.DistrictSearch({
subdistrict: 0, //获取边界不需要返回下级行政区
extensions: "all", //返回行政区边界坐标组等具体信息
level: "province", //查询行政级别为 市
});
});
},
// 初始化地图事件
initMapEvent(map) {
map.on("click", this.getDeviveAndFactoryInfo);
},
// 获取当前点击事件的区域内的设备与工厂总数
getDeviveAndFactoryInfo(e) {
// 获取点击事件的经纬度
let lnglat = [e.lnglat.getLng(), e.lnglat.getLat()];
// 根据经纬度查询当前点所在的省
this.mapPlugin.geocoder.getAddress(lnglat, (status, result) => {
if (status === "complete" && result.regeocode) {
let province = result.regeocode.addressComponent.province;
this.infoWindow = new AMap.InfoWindow({
isCustom: true,
anchor: "bottom-center",
closeWhenClickMap: true,
});
const that = this;
// 使用组件---------------------------------------------------------------------
const popupDom = new CreateDom({
template: "<DeviceInfoWindow :infoWindow='infoWindow'></DeviceInfoWindow>",
data() {
return { infoWindow: null };
},
created() {
// 获取当前页面的数据
this.infoWindow = that.infoWindow;
},
components: {
DeviceInfoWindow,
},
});
// 使用组件---------------------------------------------------------------------
this.infoWindow.setContent(popupDom.getDomItem());
this.infoWindow.open(this.map, lnglat);
// 根据当前省份获得当前省份的边界
this.mapPlugin.districtSearch.search(province, (status, result) => {
this.map.remove(this.provincePolygons); //清除上次结果
this.provincePolygons = [];
var bounds = result.districtList[0].boundaries;
console.log("边界------------", bounds);
if (bounds) {
for (var i = 0, l = bounds.length; i < l; i++) {
//生成行政区划polygon
var polygon = new AMap.Polygon({
strokeWeight: 1,
path: bounds[i],
fillOpacity: 0.4,
fillColor: "#80d8ff",
strokeColor: "#0091ea",
});
this.provincePolygons.push(polygon);
}
}
this.map.add(this.provincePolygons);
this.map.setFitView(this.provincePolygons); //视口自适应
});
} else {
this.$message.error("根据鼠标点查询省市失败");
}
});
}
},
};
</script>
- 效果图如下图
优缺点
优点
- 代码逻辑统一,可阅读性强。
- 可拓展性、可复用性强。
缺点
- 需要将项目的运行时构建更改为独立构建,性能消耗有所增加。
- 修改方案如下: (1)对于Vue cli3.0以上的版本 在vue.config.js文件中增加如下:
module.exports = {
runtimeCompiler: true,
}
(2)对于以下版本修改如下代码:
module.exports = {
// ...
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js', // runtime-only版本为'vue/dist/vue.runtime.esm.js'
'@': resolve('src')
}
},
总结
如果该篇文章对您有帮助,请帮忙点个赞,谢谢~~~~
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!