web页面基于事件转发的webSocket同步技术分享

2020-04-26  本文已影响0人  Yezzle

本人在做websockect同步的项目积累了一些经验,整理出来即是方便自己日后查看,也可以分享给大家一起讨论学习,很多出代码都是要记一下的,所以下面贴代码可能会比较多。本文主要内容包含了一下技术点:

  1. iframe注入脚本
  2. iframe与父级页面的交互
  3. 鼠标事件转发原理
  4. 在express中使用sockect.io库配合前端交互

下面一个一个的来

1. iframe注入脚本

//通过代码动态创建一个iframe
let myFrame = document.createElement('iframe')
setProps(frame, {
            id: frameId,
            src: "www.baidu.com", // 设置iframe加载的页面,不可跨域
            scrolling: 'no',  // 禁用滚动条
            frameborder: '0', // 禁用边框
            width:'' + scaleW, // 根据情况适当缩放
            height: '' + scaleH,
            class: someCondition ? 'frame-class1' : 'frame-class2'
        })
document.body.appendChild(myFrame)
frame.onload = function (e) {
            var insertJsTag = frame.contentDocument.createElement('script');
            insertJsTag.setAttribute('src', "https://mycdn.com/xxx.js") 
            var jqTag = frame.contentDocument.createElement('script'); // 插入jquery库
            jqTag.setAttribute('src', "https://mycdn.com/jquery.min.js")
            var styleTag = frame.contentDocument.createElement('style');
            styleTag.innerText = 'body{ overflow: hidden !important }'
            frame.contentDocument.body.appendChild(jqTag);
            frame.contentDocument.body.appendChild(insertJsTag);
            frame.contentDocument.body.appendChild(styleTag);
        }
function setProps(element, props){
        if(!element) return;
        for (const key in props) {
            if (props.hasOwnProperty(key)) {
                const prop = props[key];
                element.setAttribute(key, prop);
            }
        }
    }

2. iframe与父级页面的交互

页面之间的信息交流主要通过拿到目标的window对象,然后调用postMessage实现的:

// 发送方
function postMessageToFrame(frameId,messageStr) {
        var frame = getFrameId(frameId);
        if (Object.prototype.toString.call(message) != '[object String]') message = JSON.stringify(message);
        /* 在子窗口中通过window.parent拿到父级窗口的window对象 
        *  比如 window.parent.postMessage(JSON.stringify(messageObj), "*")
        * postMessage第二个参数是目标iframe的origin,如果不对应,则无法介绍到消息
        */
        frame.contentWindow.postMessage(messageStr, location.origin); // 
    }

// 接收方:
    window.addEventListener('message', function (e) {
        if (!e.data) return;
        var msg = JSON.parse(e.data)
        switch (msg.method) {
            case 'action':
                handleFrameAction(msg);
                break;
            case 'onpage': 
                user.page = msg.page;
                break;
           default: // do something
        }
    });

3. 鼠标事件转发原理

鼠标事件转发主要解决了两个问题: 1. 事件发生元素转化成位派发生成事件的元素 2. 事件生成与派发。 第一点其实详细说就是,要在转发页面和起始鼠标事件产生页面选中同一个对应的元素,这样,我们在对应的元素上派发对应的事件,以达到产生相同鼠标事件效果的目的。这里通过使用jquery来生成元素的选择器字符串,传递到目标页面 然后找到对应的元素:

    function makeSelector(el) {
        var tag, index, stack = [];
        for (; el.parentNode; el = el.parentNode) {
            tag = el.tagName;
            if (tag != "HTML") {
                index = $(el).prevAll().length + 1;
                if (tag == "BODY") {
                    stack.unshift(tag);
                } else {
                    stack.unshift(tag + ':nth-child(' + index + ')');
                }
            }
        }
        return stack.join(' > ');
    }

// 找到对应元素
document.querySelector(selectorStr)

加下来是事件的派发:

//获取事件
document.addEventListener('click', function(ev){
      e.type = ev.type;
      e.pageX = ev.pageX;
      e.pageY = ev.pageY;
      e.srcElement = mackSelector(ev.srcElement);
      e.clientX = ev.clientX;
      e.clientY = ev.clientY;
      window.parent.postMessage(JSON.stringify({event: e}))
}, {capture:true, passive: true})

// 目标页面中派发事件:
handleMessage(msgStr){
      const msgObj = JSON.parse(msgStr);
      const {event} = msgObj;
      let e = new Event(event.type);
      let srcElement = document.querySelector(event.srcElement);
      e.initEvent(event.type, true, true);
      e.clientX = event.clientX;
      e.clientY = event.clientY;
      e.pageX = event.pageX;
      e.pageY = event.pageY;
      if(srcElement&&srcElement.dispatchEvent){
            srcElement.dispatchEvent(e)
      }
}

4. 在express中使用sockect.io库配合前端交互

首先需要安装expresssocket.io

npm i express socket.io

创建服务端:

var express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    io = require('socket.io').listen(server);

io.on('connection', (socket) => {
    socket.emit('open', '连接服务器成功'); // 通知客户端已连接
    
    socket.on('someAction', (msg) => { // 监听客户端的消息并响应
        console.log(msg)
        socket.emit(JSON.stringify({msg: 'this is a response message'}))
    });

    //监听出退事件
    socket.on('disconnect', function () {
           // 客户端断开连接
      })
})
// express基本配置
app.configure(() => {
    app.set('port', 10086);
    app.use(express.logger('dev'));
});

app.configure('development', function () {
    app.use(express.errorHandler());
});

server.listen(app.get('port'), ()  => {
    console.log("Express server listening on port " + app.get('port'));
})

在页面中使用socket.io:

<script src="./node_modules/socket.io-client/dist/socket.io.js"> </script>
<script> 
    const socketUrl = "localhost:10086"
    socket = io.connect(socketUrl);
    socket.on('open', () => {
        console.log('连接服务器成功')
    })

    socket.on('connection', (msg) => {
        console.log(JSON.parse(msg).msg) // this is a response message
    })
</script>
上一篇下一篇

猜你喜欢

热点阅读