H5 웹 뷰 에서 소결 개발

20911 단어
배경
당사 의 업무 측 에서 앱 을 개발 해 야 한다 고 요구 하 였 습 니 다.그러나 시간 제한 으로 인해 케이스 app 방식, 즉 네 이 티 브 app 에 웹 뷰 를 삽입 하여 전단 페이지 를 보 여 주 는 것 만 사용 할 수 있 습 니 다.본 고 는 주로 자 바스 크 립 트 와 원생 app 간 의 통신, 그리고 웹 뷰 를 내장 하여 개발 할 때 전단 면 에서 밟 을 수 있 는 구 덩이 를 기술 하고 자 한다.
기술 구조
전단: vue + vuex + vue - router + webpack 온 가족 통 개발 백 엔 드: Node (express 프레임 워 크) 간단 한 퍼 가기 인터페이스 에서 자바 - 진짜 백 엔 드 인터페이스 로.
js 와 원생 통신
jsBridge 기술 과 네 이 티 브 앱 통신 안 드 로 이 드 전송 문과 ios 전송 문 을 사용 합 니 다. 두 플랫폼 의 초기 화 방식 이 다 르 기 때문에 개발 과정 에서 각 플랫폼 에 대해 대응 하 는 작업 을 해 야 합 니 다.구체 적 인 방법
  • 라 이브 러 리 요구 에 따라 초기 화 함수
  • //android
    function connectWebViewJavascriptBridge{
        if (window.WebViewJavascriptBridge) {
                //do your work here
            } else {
                document.addEventListener(
                    'WebViewJavascriptBridgeReady'
                    , function() {
                        //do your work here
                    },
                    false
                );
            }
    }
    //ios
    setupWebViewJavascriptBridge(function(bridge) {
    	
    	/* Initialize your app here */
    
    	bridge.registerHandler('JS Echo', function(data, responseCallback) {
    		console.log("JS Echo called with:", data)
    		responseCallback(data)
    	})
    	bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
    		console.log("JS received response:", responseData)
    	})
    })
    
  • 초기 화하 여 신부 대상 을 얻는다.원생 app 에서 정 의 된 방법 을 호출 하거나 js 방법 을 등록 하여 원생 호출
  • 할 수 있 습 니 다.
    setupWebViewJavascriptBridge(function(bridge) {
    	
    	/* Initialize your app here */
    
    	bridge.registerHandler('JS Echo', function(data, responseCallback) {
    		console.log("JS Echo called with:", data)
    		responseCallback(data) // 
    	})
    	bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
    		console.log("JS received response:", responseData)
    	})
    })
    

    Tips:
  • 안 드 로 이 드 는 IOS 초기 화 방식 과 달리 플랫폼 을 판단 한 뒤 호출 해 야 한다.또한 안 드 로 이 드 가 초기 화 될 때 추가 로 방법 을 도입 해 야 합 니 다.
  • 안 드 로 이 드 정의 방법 을 호출 할 때 반환 값 은 문자열 일 수 있 습 니 다.IOS 는 JSON 대상 이 될 수 있다.callHandler 가 필요 할 때 반환 값 을 패키지 처리 하거나 데이터 형식 을 통일 적 으로 규정 해 야 합 니 다.
  • 전체 업무 코드 문 말 제시
  • 구 덩이 를 밟다
  • bridge 속성 방법 registerHandler, callHandler 를 호출 하여 리 셋 함수 에서 페이지 논 리 를 처리 할 때 this
  • 를 사용 하 는 것 을 피 하 는 것 이 좋 습 니 다.
  • vue 구성 요소 에서 registerHandler, callHandler 리 셋 함수 에서 vue 인 스 턴 스 를 사용 할 때 인 스 턴 스 대상 을 가 져 올 수 없습니다.정확 한 방법 은 반전 함수 에서 window 대상 을 호출 하 는 방법 이 며, 이 방법 을 통 해 vue 인 스 턴 스 대상 을 사용 하 는 것 입 니 다.
  • //vue   
    mounted(){
        window['handleServicePushMessage'] = (res) => {
        	vm.handleServicePushMessage(res)
        };
        bridge.registerHandler("servicePushMessage", function (data, responseCallback) {
            handleServicePushMessage(data)
            responseCallback(data) //    App
        })
    }
    
  • 데스크 톱 푸 시 메 시 지 를 클릭 하여 App 으로 이동 하 는 자세 한 상황 에서 js 등록 방법 이 호출 될 때 중복 호출 문 제 를 일 으 킬 수 있 습 니 다.그러므로 방법 내수 에서 중복 호출 판단 을 잘 해 야 한다
  • IOS - 12.0 버 전에 서 입력 상자 가 있 는 페이지 에 서 는 입력 할 때 소프트 키보드 가 웹 뷰 를 이 고 초점 을 잃 으 면 웹 뷰 가 자동 으로 재생 되 지 않 습 니 다.앱 을 호출 하여 인터페이스 를 끌 어 올 려 야 합 니 다.
  • //  ios 12   ui       
    document.addEventListener('focusout', function (event) {
    	let curTarget = event.target || event.srcElement;
    	let isInput= ['input', 'textarea'];
    	//                
    	let curTargetTagName= curTarget.tagName.toLowerCase();
    	    if (isInput.includes(curTargetTagName)) {
        	    //    
        	    //    activeElement     
            	setTimeout(function () {
            	    let activeEle = document.activeElement;
            	    let activeEleTagName= activeEle.tagName.toLowerCase();
            	    if (!isInput.includes(activeEleTagName)) {
            	        // console.log(document.activeElement.tagName);
            	        //  app   webview
            	        performMethod('scrollTotop', null);
                        }
            	}, 200);
            }
    }, true);
    

    5. js 가 app 에 존재 하지 않 는 다 리 를 호출 할 때 이상 을 포착 할 수 없고 페이지 가 잘못 보고 되 지 않 습 니 다. 6. 네 비게 이 션 표시 줄 에 문 제 를 표시 합 니 다. 프로젝트 시간 이 촉박 하고 app 개발 자 들 이 개발 임 무 를 많이 수행 하지 않 기 때문에 경로 통 제 는 전단 에 두 고 처리 합 니 다.이때 네 비게 이 션 바 배터리 시간 표시 줄 의 적합 한 문제 가 있다.이 프로젝트 는 상단 에서 20PX 를 하향 조정 하여 처리 하고 배터리 시간 표시 줄 의 글꼴 색상 에 대한 제어 도 다 리 를 통 해 설정 합 니 다.또한 아이 폰 X 는 별도로 처리 하기에 적합 하 다.7. app 이 웹 페이지 를 불 러 올 때 js 가 원생 방법 다 리 를 즉시 호출 할 때 원생 방법 다리 가 등록 되 지 않 은 상황 이 발생 할 수 있 습 니 다.따라서 특수 한 상황 에 서 는 다리 조작 을 지연 시 켜 야 한다.
    전체 코드
    /*    */
    function (window) {
    	window.device = {};
    	var ua = navigator.userAgent;
    	var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
    	var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
    	var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
    	var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
    	device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
    	if (android) {
    		device.os = 'android';
    		device.osVersion = android[2];
    		device.android = true;
    		device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0
    	}
    	if (ipad || iphone || ipod) {
    		device.os = 'ios';
    		device.ios = true
    	}
    }(window)
    /*  Android      ,IOS   ,   IOS        */
    (function () {
    	if (window.WebViewJavascriptBridge || device.ios) {
    		return false;
    	}
    	var messagingIframe;
    	var sendMessageQueue = [];
    	var receiveMessageQueue = [];
    	var messageHandlers = {};
    	var CUSTOM_PROTOCOL_SCHEME = 'yy';
    	var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
    	var responseCallbacks = {};
    	var uniqueId = 1;
    
    	function _createQueueReadyIframe(doc) {
    		messagingIframe = doc.createElement('iframe');
    		messagingIframe.style.display = 'none';
    		doc.documentElement.appendChild(messagingIframe);
    	}
    
    	/*set default messageHandler*/
    	function init(messageHandler) {
    		if (WebViewJavascriptBridge._messageHandler) {
    			throw new Error('WebViewJavascriptBridge.init called twice');
    		}
    		WebViewJavascriptBridge._messageHandler = messageHandler;
    		var receivedMessages = receiveMessageQueue;
    		receiveMessageQueue = null;
    		for (var i = 0; i < receivedMessages.length; i++) {
    			_dispatchMessageFromNative(receivedMessages[i]);
    		}
    	}
    
    	function send(data, responseCallback) {
    		_doSend({data: data}, responseCallback);
    	}
    
    	function registerHandler(handlerName, handler) {
    		messageHandlers[handlerName] = handler;
    	}
    
    	function callHandler(handlerName, data, responseCallback) {
    		_doSend({handlerName: handlerName, data: data}, responseCallback);
    	}
    
    	/*sendMessage add message,   native   sendMessage*/
    	function _doSend(message, responseCallback) {
    		if (responseCallback) {
    			var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
    			responseCallbacks[callbackId] = responseCallback;
    			message.callbackId = callbackId;
    		}
    		sendMessageQueue.push(message);
    		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
    	}
    
    	/*    native  ,     :  sendMessageQueue   native,  android           ,    url shouldOverrideUrlLoading        */
    	function _fetchQueue() {
    		var messageQueueString = JSON.stringify(sendMessageQueue);
    		sendMessageQueue = [];
    		/*android can't read directly the return data, so we can reload iframe src to communicate with java*/
    		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
    	}
    
    	/*   native  ,*/
    	function _dispatchMessageFromNative(messageJSON) {
    		setTimeout(function () {
    			var message = JSON.parse(messageJSON);
    			var responseCallback;
    			/*java call finished, now need to call js callback function*/
    			if (message.responseId) {
    				responseCallback = responseCallbacks[message.responseId];
    				if (!responseCallback) {
    					return;
    				}
    				responseCallback(message.responseData);
    				delete responseCallbacks[message.responseId];
    			} else {/*    */
    				if (message.callbackId) {
    					var callbackResponseId = message.callbackId;
    					responseCallback = function (responseData) {
    						_doSend({responseId: callbackResponseId, responseData: responseData});
    					};
    				}
    				var handler = WebViewJavascriptBridge._messageHandler;
    				if (message.handlerName) {
    					handler = messageHandlers[message.handlerName];
    				}
    				/*    handler*/
    				try {
    					handler(message.data, responseCallback);
    				} catch (exception) {
    					if (typeof console != 'undefined') {
    						console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
    					}
    				}
    			}
    		});
    	}
    
    	/*   native  ,receiveMessageQueue             null,  */
    	function _handleMessageFromNative(messageJSON) {
    		if (receiveMessageQueue && receiveMessageQueue.length > 0) {
    			receiveMessageQueue.push(messageJSON);
    		} else {
    			_dispatchMessageFromNative(messageJSON);
    		}
    	}
    
    	var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
    		init: init,
    		send: send,
    		registerHandler: registerHandler,
    		callHandler: callHandler,
    		_fetchQueue: _fetchQueue,
    		_handleMessageFromNative: _handleMessageFromNative
    	};
    	var doc = document;
    	_createQueueReadyIframe(doc);
    	var readyEvent = doc.createEvent('Events');
    	readyEvent.initEvent('WebViewJavascriptBridgeReady');
    	readyEvent.bridge = WebViewJavascriptBridge;
    	doc.dispatchEvent(readyEvent);
    })();
    /*Android      */
    function connectWebViewJavascriptBridge(callback) {
    	if (window.WebViewJavascriptBridge) {
    		callback(WebViewJavascriptBridge)
    	} else {
    		document.addEventListener('WebViewJavascriptBridgeReady', function () {
    			callback(WebViewJavascriptBridge)
    		}, false);
    	}
    }
    /*IOS      */
    function setupWebViewJavascriptBridge(callback) {
    	if (window.WebViewJavascriptBridge) {
    		return callback(WebViewJavascriptBridge)
    	} else {
    	}
    	if (window.WVJBCallbacks) {
    		return window.WVJBCallbacks.push(callback)
    	}
    	window.WVJBCallbacks = [callback];
    	var WVJBIframe = document.createElement('iframe');
    	WVJBIframe.style.display = 'none';
    	WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
    	document.documentElement.appendChild(WVJBIframe);
    	setTimeout(function () {
    		document.documentElement.removeChild(WVJBIframe)
    	}, 0)
    }
    if(device.ios){
        setupWebViewJavascriptBridge(function(bridge){
            /*       */
            window.BRIDGE= brige;
        })
    }
    if(device.android){
        connectWebViewJavascriptBridge(function(bridge){
            /*       */
            window.BRIDGE= brige;
        })
    }
    
    

    좋은 웹페이지 즐겨찾기