开发一个封装iframe的vue组件


Posted in Vue.js onMarch 29, 2021

VUE的基本组成单元,我看应该是组件。用VUE开发前端项目,就是开发一个个组件,然后搭积木一样,将项目搭建出来。组件包含在页面,或者是更大的组件里面。在这里,组件与页面的界限,好像并不明显。事实上,对于单页应用,只有一个页面。

组件的好处,一是可以加强复用;二是能够将特定功能封装,利于调用;三是由于职责分明,组件高内聚,组件间低耦合,利于系统功能的优化、扩展和维护。好处多多。

开发组件,主要有2部分内容:
1、组件内部逻辑
2、外部接口
由于我这两天弄的组件,里面包含有一个<iframe>,那么还有一部分工作内容:
3、iframe接口

一、组件介绍

这是一个地图插件。功能是展示地图,以及接受外部命令,加载图层、绘制图形等相关操作。地图采用arcgis for js实现。由于我们过去开发的项目,地图操作有一些积累,不过并没有前后端分离,没有采用VUE或REACT,还是传统的WEB页面。因为时间紧,也想直接复用以前的成果,于是考虑用<iframe>承载地图页面,封装在VUE组件里,由组件对接外部命令并与iframe里的地图页面交互。

二、组件内部结构及逻辑

1、代码组织结构

开发一个封装iframe的vue组件

2、地图组件

Map.vue

<template>
 <div class="map-container">
    <!-- 承载地图页面 -->
  <iframe :src="src" ref="iframe" @load="iframeLoad"></iframe>
 </div>
</template>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped="scoped">
 .map-container iframe{
  width: 100%;
  height: 100%;
  border: none;  
 }
</style>
 
<script>
 import config from '../../vue.config'//里面有路径信息
  
 let iframeWin = null;//私有变量
  
 export default {
  props:['size'],//纯测试,没啥用,对应<Map id="map" ref="map" size="100"></Map>
  data() {
   return {
    src: '',//地图页面地址
    isLoaded: false,//地图页面是否加载完毕
    iMap: null,//地图页面暴露出来的,供外部访问的对象
    require: null//arcgis的require函数,用于引用自定义插件。我们过去写了不少自定义的地图插件
   }
  },
  created() {
   this.src = config.publicPath + 'map.html'
  },
  mounted() {
    //监听iframe的消息
   window.addEventListener('message', this.handleMessage)
   iframeWin = this.$refs.iframe.contentWindow
  },
  methods: {
   iframeLoad() {
    this.isLoaded = true;
    window.console.log("map is ready")
   },   
   async handleMessage() {//接收来自iframe的消息
    this.require = iframeWin.require;
    this.iMap = iframeWin.iMap;
   },
   loadLayer(nodes,servers){
    this.iMap.layerHandler.load(nodes,servers);
   },
   isReady(){
    return this.isLoaded;
   }
  }
 }
</script>

有关组件的结构,比如

export default {
 props:,//标记里的属性
 data() {//公共变量
 },
 created() {//加载时?
 },
 mounted() {//加载完毕时
 },
 methods: {//公共方法
 }
}

export代表了这是对外。所以里面的属性、变量、方法,都可以被外部访问。如果想私有,应该在export之外定义。如本例:

开发一个封装iframe的vue组件

像这类简单的介绍,在网上怎么也搜不到。vue的中文站点,陈旧,内容支离破碎,对初学者极不友好,加重了学习的成本。

三、iframe接口

组件Map.vue与里面的iframe是怎么通信的呢?
通过系统消息和直接访问iframe的对象。直接访问iframe里的对象有个前提,就是不能跨域。

iframe承载的地图页面map.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
        ...
 </head>
 <body>
  <div id="map"></div>
        ...
  </div>  
 
 </body>
</html>
<script src="http://192.168.0.200/pubzy211/arcgis_js_api/3.19/init.js"></script>
<script type="text/javascript">
 var iMap = {}; //外部引用接口
 
 require([
  "esri/config",
  "esri/map",
  "esri/geometry/Extent",
  "esri/SpatialReference",
 
  "layerlib/LtLayer",
 
  "dojo/dom",
  "dojo/_base/array",
  "dojo/parser",
  "dojo/domReady!"
 ], function(
  esriConfig,
  Map,
  Extent,
  SpatialReference,
 
  LtLayer,
  dom,
  arrayUtils,
  parser
 ) {
  //map
  var map = ...
 
  /* 外部接口 */
  iMap = {
   map: map,
   legend: legend,
   home: home,
   tipDialog: tipDialog,
   toggle: toggle,
   overviewMap: overviewMap
  };
  iMap.drawHandler = ...
  iMap.layerHandler = ...;
  iMap.centerAt = ...;
  iMap.clear = ...;
  iMap.restoreView = ...;
 
  // 向父vue页面发送加载完毕信号
  window.parent.postMessage({
   cmd: 'mapIsReady',
   params: {
    success: true,
    data: true
   }
  }, '*');
  /* end of 外部接口 */
 
 });
</script>

地图组件Map.vue对应iframe部分,详见一.2中的代码

export default {
        。。。
  mounted() {
      //监听iframe的消息
   window.addEventListener('message', this.handleMessage)
   //获得iframe的window对象
   iframeWin = this.$refs.iframe.contentWindow
  },
  methods: {
   iframeLoad() {
    this.isLoaded = true;
    window.console.log("map is ready")
   },   
   async handleMessage() {//接收来自iframe的消息
    this.require = iframeWin.require;
    this.iMap = iframeWin.iMap;
   },
   loadLayer(nodes,servers){
    //加载图层
    this.iMap.layerHandler.load(nodes,servers);
   }
  }
 }

四、外部接口

Map.vue是一个组件,它要跟它所在的组件或页面进行通信。

现在,Map.vue放在了一个容器页面Home.vue(即测试页面)里,里面还有一个命令组件Layer.vue。点击命令组件里的按钮,地图要做出相应的响应。其中的原理如下:

命令组件的按钮点击后,发射信息到容器页面,然后容器页面调用地图组件的方法。

测试页面Home.vue

<template>
 <div id="app1">
  <div id="map-container">
   <div>地图组件</div>
   <Map id="map" ref="map" size="100"></Map>
  </div>
  <div id="layer-container">
   <div>其他组件</div>
   <Layer @loadLayer="loadLayer" @loadCloud="loadCloud" @clear="clearMap"></Layer>
  </div>
 </div>
</template>
 
<script>
 import Map from '../components/Map.vue'
 import Layer from '../components/Layer.vue'
 
 export default {
  name: 'App',
  components: {
   Map,
   Layer
  },
  methods:{
   loadLayer(nodes,servers){//加载图层
    let map = this.$refs.map;
    map.loadLayer(nodes,servers);
   },
   loadCloud(data){//加载卫星云图
    let map = this.$refs.map;
    map.require(["drawlib/Cloud"], function (Cloud) {
     let iMap = map.iMap;
     let cloudId = 'cloud';
     let cloud = new Cloud(iMap.map);
     iMap.drawHandler.push(cloudId, cloud);
     cloud.draw(data,cloudId);
    });
   },
   clearMap(){//清除
    let map = this.$refs.map;
    map.iMap.clear();
   }
  }
 }
</script>
 
<style>
。。。
</style>

命令组件Layer.vue

<template>
 <div class="layer-container">
  <button @click="loadLayer">加载图层</button>
  <button @click="loadCloud">卫星云图</button>
  <button @click="clear">清除</button>
 </div>
</template>
 
<script>
 export default {
  methods: {
   loadLayer() {
    let nodes = ...
    let servers = ...
    this.$emit("loadLayer", nodes,servers)
   },
   loadCloud(){
    let data = ...;
    this.$emit("loadCloud", data);
   },
   clear(){
    this.$emit("clear");
   }
  },
 }
</script>
 
<style scoped="scoped">
。。。
</style>

注意命令组件发射消息中指定的方法,在容器页面中都有相关的属性与之对应:

//命令组件
loadCloud(){
 let data = ...;
 this.$emit("loadCloud", data);
},
 
//容器页面
<Layer @loadLayer="loadLayer" @loadCloud="loadCloud" @clear="clearMap"></Layer>

五、运行结果

开发一个封装iframe的vue组件

六、总结

其他组件要与地图组件交互,中间要通过容器页面,其他组件与地图组件并没有直接交互。这其实是一种命令模式。好处是其他组件和地图组件解耦,没有耦合在一起,意味着互不影响。这有利于地图组件本身的扩展和优化。缺点的话,每个东东都要通过容器页面转发,容器页面代码可能会有冗余,有些方法根本就是个传声筒,给人的感觉是重重复复的写,意义不太大。

Vue.js 相关文章推荐
vue3.0实现点击切换验证码(组件)及校验
Nov 18 Vue.js
vue-calendar-component 封装多日期选择组件的实例代码
Dec 04 Vue.js
vue监听键盘事件的相关总结
Jan 29 Vue.js
关于Vue Router的10条高级技巧总结
May 06 Vue.js
如何使用vue3打造一个物料库
May 08 Vue.js
Vue全家桶入门基础教程
May 14 Vue.js
vue实现锚点定位功能
Jun 29 Vue.js
Vue的生命周期一起来看看
Feb 24 Vue.js
Vue全局事件总线你了解吗
Feb 24 Vue.js
VUE使用draggable实现组件拖拽
Apr 06 Vue.js
Vue.Draggable实现交换位置
Apr 07 Vue.js
Vue 打包后相对路径的引用问题
Jun 05 Vue.js
如何让vue长列表快速加载
Vue3 Composition API的使用简介
vue+django实现下载文件的示例
vue路由实现登录拦截
vue项目实现分页效果
Mar 24 #Vue.js
vue实现倒计时功能
Mar 24 #Vue.js
vue 中 get / delete 传递数组参数方法
Mar 23 #Vue.js
You might like
Protoss建筑一览
2020/03/14 星际争霸
PHP 内存缓存加速功能memcached安装与用法
2009/09/03 PHP
PHP反射机制用法实例
2014/08/28 PHP
PHP仿微信多图片预览上传实例代码
2016/09/13 PHP
你不知道的文件上传漏洞php代码分析
2016/09/29 PHP
PHP 中常量的知识整理
2017/04/14 PHP
javascript innerHTML、outerHTML、innerText、outerText的区别
2008/11/24 Javascript
extjs fckeditor集成代码
2009/05/10 Javascript
原生javascript实现获取指定元素下所有后代元素的方法
2014/10/28 Javascript
AngularJS基础教程之简单介绍
2015/09/27 Javascript
全面解析Bootstrap中nav、collapse的使用方法
2016/05/22 Javascript
教你如何在Node.js中使用jQuery
2016/08/28 Javascript
关于JS中二维数组的声明方法
2016/09/24 Javascript
js基于FileSaver.js 浏览器导出Excel文件的示例
2017/08/15 Javascript
JavaScript定义及输出螺旋矩阵的方法详解
2017/12/01 Javascript
解决JSON.stringify()自动将中文转译成unicode的问题
2018/01/05 Javascript
详解vue-cli项目中的proxyTable跨域问题小结
2018/02/09 Javascript
使用puppeteer爬取网站并抓出404无效链接
2018/12/20 Javascript
vue cli 3.x 项目部署到 github pages的方法
2019/04/17 Javascript
[06:13]DOTA2进化论(修改版)
2013/10/08 DOTA
在Python中使用HTML模版的教程
2015/04/29 Python
Python简单实现TCP包发送十六进制数据的方法
2016/04/16 Python
在Python中通过threading模块定义和调用线程的方法
2016/07/12 Python
python实现自动化上线脚本的示例
2019/07/01 Python
python匿名函数用法实例分析
2019/08/03 Python
Python数据可视化:饼状图的实例讲解
2019/12/07 Python
AUC计算方法与Python实现代码
2020/02/28 Python
html5 实现客户端验证上传文件的大小(简单实例)
2016/05/15 HTML / CSS
HTML5如何使用SVG的方法示例
2019/01/11 HTML / CSS
美国演唱会订票网站:Ticketmaster美国
2017/10/05 全球购物
杠杆的科学教学反思
2014/01/10 职场文书
动员大会主持词
2014/03/20 职场文书
写给父母的感谢信
2015/01/22 职场文书
2015年卫生院健康教育工作总结
2015/07/24 职场文书
Nginx开启Brotli压缩算法实现过程详解
2021/03/31 Servers
html css3不拉伸图片显示效果
2021/06/07 HTML / CSS