这是我参与更文挑战的第20天,活动详情查看: 更文挑战
项目问题,最近要将游戏项目布置在电视端,甲方的机顶盒是华为的(巨坑),很多权限拿不到,按键冲突也是常有的事,今天的问题是,电视端、移动端按键有限,项目中如何自定义window方法,传给后端去调用
前言
JavaScript的window对象
所有浏览器都支持 window 对象,所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员,你可以在任何地方使用window已有的属性和方法,比如
常用对象对象有location ,history ,navigator、screen
常用的方法有:window.open()、window.close() 、window.moveTo()、window.resizeTo()、alert()、confirm()、prompt()
常用的属性有:var w=window.innerWidt || document.documentElement.clientWidth || document.body.clientWidth(这个栗子具有代表性)
自定义window方法
已有的window被占用或者不足以满足某个功能(比如我现在),需要在React或者Vue里自定义window方法方便 后端去调用。
// 方式一
window.functionA = () => {}
window.functionB = () => {}
window.functionC = () => {}
// 方式二, 引用JQuery
$.extend(window, {
A:function(){},
B:function(){},
C:function(){},
})
// 方式三
(() => {
var defining = {
A: function() { },
B: function() { },
C: function() { }
};
Object.keys(defining).forEach(key => {
window[key] = defining[key];
});
})();
// 方式四(window对象下的变量是对象时)
window[变量] = function(){}
复制代码
特别注意:有的同学说方法定义失败,也许你没用箭头函数,所以别忘了 let _this = this
Vue 自定义window方法
下面方法不写在全局app.vue里,而写在组件里,好处是避免因为涉及到作用域、以及组件内属性函数的异步执行所导致 vue 的方法在第三方组件内无法执行,或者在其外部执行时存在执行顺序的问题(本意是想在第三方组件函数执行完毕后再执行我的函数)。
methods:{
// 创建电话组件
createCallComponent(phoneNum) {
if (window.callComp && !window.callComp.isDestroyed) {
return;
};
window.callComp = new UdeskCallcenterComponent({
container: document.body,
token: this.agentToken,
subDomain: this.subDomain,
showManualScreenPop: false,
onHangup: function(conversation) { // 电话挂断时执行
getCallId(conversation.call_id); // 这里调用绑定后的方法,并传入callid
window.callComp.destroy(); // 销毁电话组件
window.callComp = null; // 销毁电话组件
}
});
},
// 每次呼叫挂断时获取当前callid,后续把获取值保存到后台接口中
callIdFun(val) {
this.call_id = val;
console.log(this.call_id);
...
},
}
mounted() {
// 把vue组件的methods方法绑定到window
window['getCallId'] = val => {
this.callIdFun(val)
}
}
复制代码
在混合开发过程中,通过jsBridge方法,H5可以调用客户端(ios,android)的内部方法,同样,客户端也需要能调用H5页面里定义的js方法,但是在vue里,所有的方法都是在组件内部声明的,也只能在组件内部调用,并没有绑定window对象下面,可以使用下列方法在vue组件里定义的方法暴露给window对象。
created() {
},
mounted() {
// 将backToday方法绑定到window下面,提供给外部调用
window['backToday'] = () => {
this.KeyBack()
}
},
methods: {
KeyBack() {
// to do something
}
}
// 有些情况别忘了 let _this = this
复制代码
混合开发过程中,vue自定义的window方法如何与原生通信
因为Android无法直接调用每个Vue对象的methods里的方法,只能调用window下面的function,所以我们需要一个模型: 先将消息发至window下的function,通过window下的function调用父组件的代码,再由父组件调用子组件的代码。Vue代码:使用Vue cli直接创建一个project, 然后在config/index.js中将host由localhost改为电脑的本地Ip,以方便使用在手机端去调试.
举个栗子:
// 建立一个ChildComponent.vue的子组件,在其中定义receiveMsgFromParent(msg)函数
<template>
<div id="childComponent" style="background-color: #42b983">
<div>{{childMsg}}</div>
<br/>
<button @click="toSecondComponent">去第二个界面</button>
</div>
</template>
复制代码
<script>
export default {
name: 'childComponent',
data: function () {
return {
childMsg: "This is child component"
}
},
methods: {
receiveMsgFromParent: function (msg) {
alert("" + msg);
this.childMsg = "receive msg = " + msg;
},
toSecondComponent: function () {
this.$router.replace({path: "/SecondComponent"});
}
},
created() {
this.$parent.setComponent(this);
}
}
</script>
复制代码
在App.vue定义一个window下的function,用来让Android的WebView找到该方法
// 同时在 App.vue里定义一个setComponent(component)方法
var curComponent;
export default {
name: 'App',
components: {
ChildComponent,
},
data: function() {
return {
richtext: 'App.vue receive msg',
};
},
methods: {
setComponent: function(component) {
curComponent = component;
},
},
created() {
this.$router.push({ path: 'ChildComponent' });
},
};
window['receiveMsgFromNative'] = function(msg) {
curComponent.receiveMsgFromParent(msg);
};
复制代码
这个Window下的function如何于路由最上层的Component建立联系,每个ChildComponent在created()时都去调用App.vue的 this.$parent.setComponent(this);方法,这样就能正确的收到消息传递给路由最上层的vue组件了。
安卓部分
Android这块,根据网友反馈,建议使用使用QQ x5浏览器去加载的,他崩溃率比较低。这里注意的是如果要传递Stirng类型的数据给Js的话,需要在数据前面加上引号。
题外话
如果抛开头痛的电视端window函数,我们平常写全局方法,有哪几种方式?
// 方式一 ,直接在main.js里写
Vue.prototype.changeData = function (){ //changeData是自定义的函数名
console.log('222');
}
// 组件内直接通过this运行函数
// 方式二 ,创建一个global.js文件,然后main.js引用(推荐)
exports.install = function (Vue, options) {
Vue.prototype.text1 = function (){//全局函数1
console.log('全局函数1');
};
Vue.prototype.text2 = function (){//全局函数2
console.log('全局函数2');
};
};
// Vue.use(global);//将全局函数当做插件来进行注册,组件内仍然用this调用
复制代码
React自定义window方法
以ReactNative为例
React Native 指定页面物理返回监听,RN的项目我还没接触过,这里引用网友的栗子。注意:回调函数onBackAndroid中的return true是必不可少的。
componentWillMount(){
BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid);
}
onBackAndroid = () => {
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
BackHandler.exitApp();
return;
}
this.lastBackPressed = Date.now();
ToastAndroid.show('再按一次退出应用',ToastAndroid.SHORT);
return true;
};
onBackAndroid = () => {
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
BackHandler.exitApp();
return;
}
this.lastBackPressed = Date.now();
ToastAndroid.show('再按一次退出应用',ToastAndroid.SHORT);
return true;
};
复制代码
原生JS监听手机物理返回键
JavaScript没有监听物理返回键的API,所以只能使用 popstate 事件监听。
pushHistory();
window.addEventListener("popstate", function(e) {
window.location = 'http://www.baidu.com';
}, false);
function pushHistory() {
var state = {
title: "title",
url: "#"
};
window.history.pushState(state, "title", "#");
}
此声明函数在xback.js文件里有,在app.js里必须再声明一次,不然监听返回事件失败。
/**
* 使用 HTML5 的 History 新 API pushState 来曲线监听 Android 设备的返回按钮
* XBack.listen(function(){
alert('oh! you press the back button');
});
*/
;!function(pkg, undefined){
var STATE = 'x-back';
var element;
var onPopState = function(event){
event.state === STATE && fire();
}
var record = function(state){
history.pushState(state, null, location.href);
}
var fire = function(){
var event = document.createEvent('Events');
event.initEvent(STATE, false, false);
element.dispatchEvent(event);
}
var listen = function(listener){
element.addEventListener(STATE, listener, false);
}
;!function(){
element = document.createElement('span');
window.addEventListener('popstate', onPopState);
this.listen = listen;
record(STATE);
}.call(window[pkg] = window[pkg] || {});
}('XBack');
// 调用方式
XBack.listen(function(){
alert('back');
});
复制代码
参考资料
jsBridge文档