开发一个封装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 相关文章推荐
如何正确解决VuePress本地访问出现资源报错404的问题
Dec 03 Vue.js
vue使用require.context实现动态注册路由
Dec 25 Vue.js
vue调用微信JSDK 扫一扫,相册等需要注意的事项
Jan 03 Vue.js
Vue实现图书管理案例
Jan 20 Vue.js
vue实现倒计时功能
Mar 24 Vue.js
vue实现简单数据双向绑定
Apr 28 Vue.js
Vue如何实现组件间通信
May 15 Vue.js
Vue3.0 手写放大镜效果
Jul 25 Vue.js
一定要知道的 25 个 Vue 技巧
Nov 02 Vue.js
如何用vue实现网页截图你知道吗
Nov 17 Vue.js
详解Vue的列表渲染
Nov 20 Vue.js
Vue ECharts实现机舱座位选择展示功能
May 15 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
spl_autoload_register与autoload的区别详解
2013/06/03 PHP
PHP5常用函数列表(分享)
2013/06/07 PHP
Yii实现单用户博客系统文章详情页插入评论表单的方法
2015/12/28 PHP
PHP中子类重载父类的方法【parent::方法名】
2016/05/06 PHP
如何用PHP做到页面注册审核
2017/03/02 PHP
Ubuntu上安装yaf扩展的方法
2018/01/29 PHP
PHP 多进程与信号中断实现多任务常驻内存管理实例方法
2019/10/04 PHP
根据分辨率不同,调用不同的css文件
2006/07/07 Javascript
Aptana调试javascript图解教程
2009/11/30 Javascript
JavaScript高级程序设计(第3版)学习笔记4 js运算符和操作符
2012/10/11 Javascript
Jquery实现页面加载时弹出对话框代码
2013/04/19 Javascript
jquery实现input输入框实时输入触发事件代码
2014/01/28 Javascript
jquery 实现返回顶部功能
2014/11/17 Javascript
Javascript 实现图片无缝滚动
2014/12/19 Javascript
使用AmplifyJS组件配合JavaScript进行编程的指南
2015/07/28 Javascript
浅谈JavaScript对象的创建方式
2016/06/13 Javascript
Vue.js组件使用开发实例教程
2016/11/01 Javascript
Angular.Js的自动化测试详解
2016/12/09 Javascript
详解Angular2响应式表单
2017/06/14 Javascript
vue-cli3单页构建大型项目方案
2020/04/07 Javascript
使用python解析xml成对应的html示例分享
2014/04/02 Python
Python使用pylab库实现画线功能的方法详解
2017/06/08 Python
使用Python监控文件内容变化代码实例
2018/06/04 Python
Django使用paginator插件实现翻页功能的实例
2018/10/24 Python
python计算无向图节点度的实例代码
2019/11/22 Python
使用python采集Excel表中某一格数据
2020/05/14 Python
python实现人性化显示金额数字实例详解
2020/09/25 Python
Python之多进程与多线程的使用
2021/02/23 Python
俄罗斯一家时尚女装商店:Charuel
2019/12/04 全球购物
Java 中访问数据库的步骤?Statement 和PreparedStatement 之间的区别?
2012/06/05 面试题
SQL Server提供的3种恢复模型都是什么? 有什么区别?
2012/05/13 面试题
倡议书格式范文
2014/04/14 职场文书
土地转让协议书
2014/04/15 职场文书
2014年采购工作总结
2014/11/20 职场文书
市场营销计划书
2019/04/24 职场文书
Python还能这么玩之用Python修改了班花的开机密码
2021/06/04 Python