博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【5+】跨webview多页面 触发事件(二)
阅读量:6423 次
发布时间:2019-06-23

本文共 10556 字,大约阅读时间需要 35 分钟。

上一章我们了解到通过webview evalJS的方法来跨页面通知事件,但是在其中还是有需要优化的地方,接下来我们慢慢的来分析。

上节回顾:

代码:

//页面通知class Broadcast{    /**     * 构造器函数     */    constructor(){            }        /**     * 事件监听     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     * @return {Broadcast} this     */    on(eventName, callback){        document.addEventListener(eventName, e => {            callback.call(e, e.detail)        })        return this    }        /**     * 事件触发     * @param {String} eventName 事件名称     * @param {Object} data 参数     * @return {Broadcast} this     */    emit(eventName, data){        // 获取所有的webview        var all = plus.webview.all()        // 遍历全部页面        for(var w in all){            // 挨个来evalJS            all[w].evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {                detail:JSON.parse('${JSON.stringify(data)}'),                bubbles: true,                cancelable: true            }));`)        }        return this    }        }

自定义需要通知页面

可以看到,之前我们emit发送通知时,是对所有的webview进行获取通知,但是有时候我们并不想通知所有的页面,而且通知别人的时候也不想通知自己啊,怎么办,在这里我们在emit方法参数多加一个配置项

/**     * 事件触发     * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     * @param {Object} options 其它配置参数     */    emit(eventName, data, {        self = false, // 是否通知自己,默认不通知        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象    } = {}) {        //code...    }

然后我们针对传进来的拓展参数,进行逻辑判断,得到最终我们需要通知的webview list

/**     * 事件触发     * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     * @param {Object} options 其它配置参数     */    emit(eventName, data, {        self = false, // 是否通知自己,默认不通知        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象    } = {}) {        let all = []        // 获取 特定 webview 数组        if(views.length > 0) {            // 如果是string 类型,则统一处理获取为 webview对象            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)        } else {            // 不特定通知的webview数组时,直接获取全部已存在的webview            all = plus.webview.all()        }        // 如果不需要通知到当前webview 则过滤        if(!self) {            let v =  plus.webview.currentWebview()            all = all.filter(item => item.id !== v.id)        }        // 遍历所有需要通知的页面        for(let v of all) {            v.evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {                detail:JSON.parse('${JSON.stringify(data)}'),                bubbles: true,                cancelable: true            }));`)        }    }

如何调用

new Broadcast().emit('say',{    name: 'newsning',    age: 26},{    self: true, // 通知当前页面 默认不通知    views: ['A.html','C.html'] // 默认通知所有页面,但不包括当前页面})// 如上代码就只通知到了3个页面, 当前页面, A页面, C页面

事件 - [ 订阅 | 发布 | 取消 ]

如果你遇到那种还需要移除监听事件,亦或者Once只监听一次的事件,再或是你看个代码不爽

clipboard.png

ok!我们来撸一套简单的 守望先锋模式,哦不,是观察者模式

事件订阅

瞧瞧我们之前的代码,on方法是直接把传进来的函数作为调用,这样子在外部调用时移除事件就没路子了,包括Once也很是蛋疼

/**     * 事件监听     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     * @return {Broadcast} this     */    on(eventName, callback){        document.addEventListener(eventName, e => {            callback.call(e, e.detail)        })        return this    }

我们先来定义好2个专门放置事件的存储对象,碧如 :

// 事件列表    const events = {        // 事件名称 : 事件方法数组        },    // 单次事件列表    events_one = {    }

之后我们修改一下on方法,并新增一个once方法

/**     * 事件监听     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     */    on(eventName, callback) {        // 获取已存在的事件列表        if(!events[eventName]) {            events[eventName] = []        }        // 添加至数组        events[eventName].push(callback)    }    /**     * 事件监听 (单次)     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     */    once(eventName, callback) {        // 获取已存在的单次事件列表        if(!events_one[eventName]) {            events_one[eventName] = []        }        // 添加至数组        events_one[eventName].push(callback)    }

酱紫,每次添加事件时,都会放入我们的事件列表中,但是!我们并没有给任何dom添加事件,而仅仅是放入所对应的事件列表中,奇怪了,看看我们之前的添加事件方法

clipboard.png

给document监听一个事件

clipboard.png

触发document事件

nonono , 我们不这么借助document亦或者其它dom的事件监听,还记得上一章的 evalJS('faqme()')么?我们就用亲切的函数来触发事件

事件发布

在事件订阅当中,我们仅仅只是把事件放入了事件列表中,我们该如何触发?

编写一个静态方法,用来触发当前页面的事件, 然后通过

static _emitSelf(eventName, data) {        if(typeof data === 'string') {            data = JSON.parse(data)        }        // 获取全部事件列表 和 单次事件列表,并且合并        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]        // 遍历触发        for(let f of es) {            f && f.call(f, data)        }        // 单次事件清空        events_one[eventName] = []    }

再配合修改一下 emit 里面的 evalJS

/**     * 事件触发     * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     * @param {Object} options 其它配置参数     */    emit(eventName, data, {        self = false, // 是否通知自己,默认不通知        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象    } = {}) {        let all = []        // 获取 特定 webview 数组        if(views.length > 0) {            // 如果是string 类型,则统一处理获取为 webview对象            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)        } else {            // 不特定通知的webview数组时,直接获取全部已存在的webview            all = plus.webview.all()        }        // 如果不需要通知到当前webview 则过滤        if(!self) {            let v =  plus.webview.currentWebview()            all = all.filter(item => item.id !== v.id)        }        // 遍历所有需要通知的页面        for(let v of all) {            /            这里是重点, 调用Broadcast的静态方法            /            v.evalJS(`Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`)        }    }

这样子,就巧妙的触发了每个webview页面 相对应的事件,并且单次事件也得到了清除

事件移除

我们知道前面的事件订阅只是将事件存起来了,事件移除相应的就是把事件列表清空

static _offSelf(eventName) {        //清空事件列表        events[eventName] = []        events_one[eventName] = []    }

最后收尾

所定义的2个静态方法,触发 和 移除 事件,我们在内部代理2个相应的方法

/**     * 当前页面事件触发      * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     */    emitSelf(eventName) {        Broadcast._emitSelf(eventName, data)    }        /**     * 清空当前页面事件      * @param {String} eventName 事件名称     */    offSelf(eventName) {        Broadcast._offSelf(eventName)    }

最后,成果已经出现

A.html

var b = new Broadcast()                        b.on('say', function(data){                alert(JSON.stringify(data))                                // 删除本页面say事件                //b.offSelf('say')            })                        b.once('say', function(data){                //单次                alert('单次:'+JSON.stringify(data))            })

B.html

new Broadcast().emit('say', {                from: '我是B啊',                id: 666            })

最后附上源码:

/** * 5+ Broadcast.js by NewsNing 宁大大  */// 获取当前webviewconst getIndexView = (() => {        // 缓存        let indexView = null        return(update = false) => {            if(update || indexView === null) {                indexView = plus.webview.currentWebview()            }            return indexView        }    })(),    // 获取全部webview     getAllWebview = (() => {        // 缓存        let allView = null        return(update = false) => {            if(update || allView === null) {                allView = plus.webview.all()            }            return allView        }    })()// 事件列表const events = {    },    // 单次事件列表    events_one = {    }//页面通知类class Broadcast {    /**     * 构造器函数     */    constructor() {    }    /**     * 事件监听     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     */    on(eventName, callback) {        // 获取已存在的事件列表        if(!events[eventName]) {            events[eventName] = []        }        // 添加至数组        events[eventName].push(callback)    }    /**     * 事件监听 (单次)     * @param {String} eventName 事件名称     * @param {Function} callback 事件触发后执行的回调函数     */    once(eventName, callback) {        // 获取已存在的单次事件列表        if(!events_one[eventName]) {            events_one[eventName] = []        }        // 添加至数组        events_one[eventName].push(callback)    }    /**     * 事件触发     * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     * @param {Object} options 其它配置参数     */    emit(eventName, data, {        self = false, // 是否通知自己,默认不通知        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象    } = {}) {        let jsstr = `Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`        this._sendMessage(jsstr, self, views)    }    /**     * 当前页面事件触发      * @param {String} eventName 事件名称     * @param {Object} data 传参参数值     */    emitSelf(eventName) {        Broadcast._emitSelf(eventName, data)    }    /**     * 事件关闭移除     * @param {String} eventName 事件名称     * @param {Object} options 其它配置参数     */    off(eventName, {        self = false, // 是否通知自己,默认不通知        views = [] // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象    } = {}) {        let jsstr = `Broadcast && Broadcast._offSelf && Broadcast._offSelf('${eventName}')`        this._sendMessage(jsstr, self, views)    }    /**     * 清空当前页面事件       * @param {String} eventName 事件名称     */    offSelf(eventName) {        Broadcast._offSelf(eventName)    }    /**     * 页面通知     * @param {String} jsstr 需要运行的js代码     * @param {Boolean} self 是否通知自己,默认不通知     * @param {Array} views 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象     */    _sendMessage(        jsstr = '',        self = false,        views = []    ) {        let all = []        // 获取 特定 webview 数组        if(views.length > 0) {            // 如果是string 类型,则统一处理获取为 webview对象            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)        } else {            // 不特定通知的webview数组时,直接获取全部已存在的webview            all = getAllWebview(true)        }        // 如果不需要通知到当前webview 则过滤        if(!self) {            let v = getIndexView()            all = all.filter(item => item.id !== v.id)        }        // 遍历全部页面        for(let v of all) {            v.evalJS(jsstr)        }    }    static _emitSelf(eventName, data) {        if(typeof data === 'string') {            data = JSON.parse(data)        }        // 获取全部事件列表 和 单次事件列表,并且合并        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]        // 遍历触发        for(let f of es) {            f && f.call(f, data)        }        // 单次事件清空        events_one[eventName] = []    }    static _offSelf(eventName) {        //清空事件列表        events[eventName] = []        events_one[eventName] = []    }}

您也可以通过babel在线转化成es5

clipboard.png

最后您还可以在github上看到一些其它5+ Api封装的源码

class Man{    constructor(){        this.name = 'newsning'    }    say(){        console.log('天行健, 君子以自强不息. ')    }}

转载地址:http://cvgra.baihongyu.com/

你可能感兴趣的文章
一例HP ADG数据恢复成功(8×73GB SCSI)
查看>>
虚拟化系列-Citrix XenServer 6.1 XenMotion与HA
查看>>
TFS创建团队项目(三)
查看>>
对发展的一点小感想
查看>>
示例化讲解RIP路由更新机制
查看>>
eclipse不能自动编译工程的解决方法
查看>>
Powershell管理系列(九)删除Exchange用户邮箱中多余的电子邮件地址
查看>>
Swt/Jface进度条
查看>>
.NET建议使用的大小写命名原则
查看>>
Git:错误:error:src refspec master does not match any
查看>>
SSIS 数据类型和类型转换
查看>>
Oracle数据库“Specified cast is农田valid”
查看>>
数据层新思路,写数据库无关的数据层 ORM在数据库内做更为合适
查看>>
armv8(aarch64)linux内核中flush_dcache_all函数详细分析【转】
查看>>
房地产英语 Real estate词汇
查看>>
python接口自动化测试(八)-unittest-生成测试报告
查看>>
第 26 章 MySQL
查看>>
How far away ?(DFS)
查看>>
C#中三种截屏方式总结
查看>>
EF架构~LinqToEntity里实现left join的一对一与一对多
查看>>