关键词搜索

源码搜索 ×
×

开发一个封装了iframe的VUE组件

发布2021-03-25浏览1129次

详情内容

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

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

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

一、组件介绍

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

二、组件内部结构及逻辑

1、代码组织结构
在这里插入图片描述
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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

有关组件的结构,比如

export default {
    props:,//标记里的属性
    data() {//公共变量
    },
    created() {//加载时?
    },
    mounted() {//加载完毕时
    },
    methods: {//公共方法
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

export代表了这是对外。所以里面的属性、变量、方法,都可以被外部访问。如果想私有,应该在export之外定义。如本例:
在这里插入图片描述
像这类简单的介绍,在网上怎么也搜不到。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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

地图组件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);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

四、外部接口

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

命令组件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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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

命令组件
loadCloud(){
    let data = ...;
    this.$emit("loadCloud", data);
},

容器页面
<Layer @loadLayer="loadLayer" @loadCloud="loadCloud" @clear="clearMap"></Layer>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

五、运行结果

在这里插入图片描述

六、总结

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

参考文章:
前端爬坑日记之vue内嵌iframe并跨域通信

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载