开发一个封装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+iview实现分页及查询功能
Nov 17 Vue.js
Vue+penlayers实现多边形绘制及展示
Dec 24 Vue.js
Vue.extend 登录注册模态框的实现
Dec 29 Vue.js
详解Vue的七种传值方式
Feb 08 Vue.js
Vue实现todo应用的示例
Feb 20 Vue.js
详解vue3中组件的非兼容变更
Mar 03 Vue.js
使用Vue.js和MJML创建响应式电子邮件
Mar 23 Vue.js
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
Apr 17 Vue.js
Vue过滤器(filter)实现及应用场景详解
Jun 15 Vue.js
Vue ECharts实现机舱座位选择展示功能
May 15 Vue.js
VUE递归树形实现多级列表
Jul 15 Vue.js
el-table-column 内容不自动换行的解决方法
Aug 14 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
Apache, PHP在Windows 9x/NT下的安装与配置 (一)
2006/10/09 PHP
php实现上传图片保存到数据库的方法
2015/02/11 PHP
PHP完全二叉树定义与实现方法示例
2017/10/09 PHP
PHP自定义递归函数实现数组转JSON功能【支持GBK编码】
2018/07/17 PHP
php文件操作之文件写入字符串、数组的方法分析
2019/04/15 PHP
JavaScript高级程序设计 读书笔记之九 本地对象Array
2012/02/27 Javascript
jQuery(js)获取文字宽度(显示长度)示例代码
2013/12/31 Javascript
Ajax请求在数据量大的时候出现超时的解决方法
2014/02/27 Javascript
浅析JS中document对象的一些重要属性
2014/03/06 Javascript
微信分享的标题、缩略图、连接及描述设置方法
2014/10/14 Javascript
JavaScript常用字符串与数组扩展函数小结
2016/04/24 Javascript
jQuery插件FusionCharts实现的MSBar2D图效果示例【附demo源码】
2017/03/24 jQuery
vue.js计算属性computed用法实例分析
2018/07/06 Javascript
Js中使用正则表达式验证输入是否有特殊字符
2018/09/07 Javascript
JavaScript实现单英文金山打字通
2020/07/24 Javascript
Vue.js计算机属性computed和methods方法详解
2019/10/12 Javascript
ES6的异步操作之promise用法和async函数的具体使用
2019/12/06 Javascript
Python单元测试框架unittest简明使用实例
2015/04/13 Python
python实现梯度下降算法
2020/03/24 Python
python redis 删除key脚本的实例
2019/02/19 Python
Python基于opencv调用摄像头获取个人图片的实现方法
2019/02/21 Python
django的model操作汇整详解
2019/07/26 Python
python retrying模块的使用方法详解
2019/09/25 Python
利用pandas将非数值数据转换成数值的方式
2019/12/18 Python
Keras设定GPU使用内存大小方式(Tensorflow backend)
2020/05/22 Python
python 发送邮件的示例代码(Python2/3都可以直接使用)
2020/12/03 Python
CSS3实现全景图特效示例代码
2018/03/26 HTML / CSS
应届生幼儿园求职信
2013/11/12 职场文书
关于赌博的检讨书
2014/01/24 职场文书
语文教学感言
2014/02/06 职场文书
大家访活动实施方案
2014/03/10 职场文书
乡村卫生服务一体化管理实施方案
2014/03/30 职场文书
环保建议书600字
2014/05/14 职场文书
新闻学专业职业生涯规划范文:我的人生我做主
2014/09/12 职场文书
2015年元旦促销方案书
2014/12/09 职场文书
Python实现滑雪小游戏
2021/09/25 Python