开发一个封装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 相关文章推荐
vue-drawer-layout实现手势滑出菜单栏
Nov 19 Vue.js
在Vue中使用mockjs代码实例
Nov 25 Vue.js
vue3.0实现插件封装
Dec 14 Vue.js
vue中如何添加百度统计代码
Dec 19 Vue.js
vue3使用vue-count-to组件的实现
Dec 25 Vue.js
基于Vue3.0开发轻量级手机端弹框组件V3Popup的场景分析
Dec 30 Vue.js
vue项目如何监听localStorage或sessionStorage的变化
Jan 04 Vue.js
vue二选一tab栏切换新做法实现
Jan 19 Vue.js
vue3.0 项目搭建和使用流程
Mar 04 Vue.js
SSM VUE Axios详解
Oct 05 Vue.js
vue cli4中mockjs在dev环境和build环境的配置详情
Apr 06 Vue.js
vue实现在data里引入相对路径
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
发布一个迷你php+AJAX聊天程序[聊天室]提供下载
2007/07/21 PHP
PHP 代码规范小结
2012/03/08 PHP
浅析关于PHP位运算的简单权限设计
2013/06/30 PHP
PHP生成条形码大揭秘
2015/09/24 PHP
PHP自定义函数判断是否为Get、Post及Ajax提交的方法
2017/07/27 PHP
PHP校验15位和18位身份证号的类封装
2018/11/07 PHP
Smarty模板变量与调节器实例详解
2019/07/20 PHP
JavaScript中计算网页中某个元素的位置
2015/06/10 Javascript
javascript获取系统当前时间的方法
2015/11/19 Javascript
使用Jasmine和Karma对AngularJS页面程序进行测试
2016/03/05 Javascript
AngularJs Managing Service Dependencies详解
2016/09/02 Javascript
JS正则替换掉小括号及内容的方法
2016/11/29 Javascript
vue中使用ueditor富文本编辑器
2018/02/08 Javascript
解决vue打包项目后刷新404的问题
2018/03/06 Javascript
微信小程序的部署方法步骤
2018/09/04 Javascript
使用ng-packagr打包Angular的方法示例
2018/09/21 Javascript
React实现全局组件的Toast轻提示效果
2018/09/21 Javascript
layui使用button按钮 点击出现弹层 弹层中加载表单的实例
2019/09/04 Javascript
基于Vue+Webpack拆分路由文件实现管理
2020/11/16 Javascript
[02:18]《我与DAC》之工作人员:为了热爱DOTA2的玩家们
2018/03/28 DOTA
Python中的对象,方法,类,实例,函数用法分析
2015/01/15 Python
使用graphics.py实现2048小游戏
2015/03/10 Python
浅析python3中的os.path.dirname(__file__)的使用
2018/08/30 Python
CSS3 实现的缩略图悬停效果
2020/12/09 HTML / CSS
html5的canvas实现3d雪花飘舞效果
2013/12/27 HTML / CSS
白兰氏健康Mall:BRAND’S
2017/11/13 全球购物
联想加拿大官方网站:Lenovo Canada
2018/04/05 全球购物
加拿大服装和鞋类零售商:Mark’s
2021/01/04 全球购物
PHP面试题集
2016/12/18 面试题
单位工作证明
2014/10/07 职场文书
自我工作评价范文
2015/03/06 职场文书
幼儿园六一儿童节主持词
2015/06/30 职场文书
遗嘱格式范本
2015/08/07 职场文书
生活小常识广播稿
2015/08/19 职场文书
2016党员学习作风建设心得体会
2016/01/21 职场文书
Java基于Dijkstra算法实现校园导游程序
2022/03/17 Java/Android