From e63bf48f637b74a7e5b71fae600602017c44e41a Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Fri, 1 Nov 2019 14:55:38 +0800 Subject: [PATCH] feat(v3): add restore logic (WKWebview recovery) --- packages/uni-app-plus/dist/index.v3.js | 300 ++++++++++++++---- .../app-plus/service/framework/page.js | 6 +- .../service/framework/plugins/data.js | 6 +- .../service/framework/plugins/lifecycle.js | 14 +- .../service/framework/plugins/vdom-sync.js | 85 +++-- .../service/framework/webview/index.js | 81 ++++- 6 files changed, 371 insertions(+), 121 deletions(-) diff --git a/packages/uni-app-plus/dist/index.v3.js b/packages/uni-app-plus/dist/index.v3.js index 799ce2b19..dbf2032fd 100644 --- a/packages/uni-app-plus/dist/index.v3.js +++ b/packages/uni-app-plus/dist/index.v3.js @@ -204,7 +204,9 @@ var serviceContext = (function () { 'offPush', 'requireNativePlugin', 'upx2px', - 'restoreGlobal' + 'restoreGlobal', + 'getSubNVueById', + 'getCurrentSubNVue' ]; const apis = [ @@ -6125,6 +6127,77 @@ var serviceContext = (function () { } } + function wrapper$1 (webview) { + webview.$processed = true; + + webview.postMessage = function (data) { + plus.webview.postMessageToUniNView({ + type: 'UniAppSubNVue', + data + }, webview.id); + }; + let callbacks = []; + webview.onMessage = function (callback) { + callbacks.push(callback); + }; + webview.$consumeMessage = function (e) { + callbacks.forEach(callback => callback(e)); + }; + + if (!webview.__uniapp_mask_id) { + return + } + const maskColor = webview.__uniapp_mask; + let maskWebview = webview.__uniapp_mask_id === '0' ? { + setStyle ({ + mask + }) { + requireNativePlugin$1('uni-tabview').setMask({ + color: mask + }); + } + } : plus.webview.getWebviewById(webview.__uniapp_mask_id); + const oldShow = webview.show; + const oldHide = webview.hide; + const oldClose = webview.close; + + const showMask = function () { + maskWebview.setStyle({ + mask: maskColor + }); + }; + const closeMask = function () { + maskWebview.setStyle({ + mask: 'none' + }); + }; + webview.show = function (...args) { + showMask(); + return oldShow.apply(webview, args) + }; + webview.hide = function (...args) { + closeMask(); + return oldHide.apply(webview, args) + }; + webview.close = function (...args) { + closeMask(); + callbacks = []; + return oldClose.apply(webview, args) + }; + } + + function getSubNVueById (id) { + const webview = plus.webview.getWebviewById(id); + if (webview && !webview.$processed) { + wrapper$1(webview); + } + return webview + } + + function getCurrentSubNVue () { + return getSubNVueById(plus.webview.currentWebview().id) + } + let firstBackTime = 0; function quit () { @@ -6402,6 +6475,20 @@ var serviceContext = (function () { return webviewStyle } + const PAGE_CREATE = 2; + const MOUNTED_DATA = 4; + const UPDATED_DATA = 6; + const PAGE_CREATED = 10; + + const UI_EVENT = 20; + + const VD_SYNC = 'vdSync'; + + const WEBVIEW_READY = 'webviewReady'; + const VD_SYNC_CALLBACK = 'vdSyncCallback'; + const INVOKE_API = 'invokeApi'; + const WEB_INVOKE_APPSERVICE$1 = 'WEB_INVOKE_APPSERVICE'; + let preloadWebview; let id = 2; @@ -6439,6 +6526,59 @@ var serviceContext = (function () { return webview } + function onWebviewResize (webview) { + webview.addEventListener('resize', ({ + width, + height + }) => { + const landscape = Math.abs(plus.navigator.getOrientation()) === 90; + const res = { + deviceOrientation: landscape ? 'landscape' : 'portrait', + size: { + windowWidth: Math.ceil(width), + windowHeight: Math.ceil(height) + } + }; + publish('onViewDidResize', res); // API + UniServiceJSBridge.emit('onResize', res, parseInt(webview.id)); // Page lifecycle + }); + } + + function onWebviewRecovery (webview, routeOptions) { + const { + subscribe, + unsubscribe + } = UniServiceJSBridge; + + const id = webview.id; + const onWebviewRecoveryReady = function (data, pageId) { + if (id !== pageId) { + return + } + unsubscribe(WEBVIEW_READY, onWebviewRecoveryReady); + if (process.env.NODE_ENV !== 'production') { + console.log(`UNIAPP[webview][${this.id}]:onWebviewRecoveryReady ready`); + } + // 恢复目标页面 + pageId = parseInt(pageId); + const page = getCurrentPages(true).find(page => page.$page.id === pageId); + if (!page) { + return console.error(`Page[${pageId}] not found`) + } + page.$vm._$vd.restore(); + }; + + webview.addEventListener('recovery', e => { + if (process.env.NODE_ENV !== 'production') { + console.log(`UNIAPP[webview][${this.id}].recovery.reload:` + JSON.stringify({ + path: routeOptions.path, + webviewId: id + })); + } + subscribe(WEBVIEW_READY, onWebviewRecoveryReady); + }); + } + function initWebview (webview, routeOptions) { // 首页或非 nvue 页面 if (webview.id === '1' || !routeOptions.meta.isNVue) { @@ -6466,19 +6606,11 @@ var serviceContext = (function () { }); }); - webview.addEventListener('resize', ({ - width, - height - }) => { - const res = { - size: { - windowWidth: Math.ceil(width), - windowHeight: Math.ceil(height) - } - }; - publish('onViewDidResize', res); - emit('onResize', res, parseInt(webview.id)); - }); + onWebviewResize(webview); + + if (plus.os.name === 'iOS' && webview.nvue) { + onWebviewRecovery(webview, routeOptions); + } // TODO 应该结束之前未完成的下拉刷新 on(webview.id + '.startPullDownRefresh', () => { @@ -6494,9 +6626,9 @@ var serviceContext = (function () { function createPreloadWebview () { if (!preloadWebview || preloadWebview.__uniapp_route) { // 不存在,或已被使用 - preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(id++)); - if (process.env.NODE_ENV !== 'production') { - console.log(`[uni-app] preloadWebview[${preloadWebview.id}]`); + preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(id++)); + if (process.env.NODE_ENV !== 'production') { + console.log(`[uni-app] preloadWebview[${preloadWebview.id}]`); } } return preloadWebview @@ -6663,7 +6795,8 @@ var serviceContext = (function () { if (!webview) { webview = createWebview(path, routeOptions); } else { - webview = plus.webview.getWebviewById(webview.id); + webview = plus.webview.getWebviewById(webview.id); + webview.nvue = routeOptions.meta.isNVue; } if (routeOptions.meta.isTabBar) { @@ -6714,8 +6847,7 @@ var serviceContext = (function () { pages.push(pageInstance); // 首页是 nvue 时,在 registerPage 时,执行路由堆栈 - if (webview.id === '1' && routeOptions.meta.isNVue) { - webview.nvue = true; + if (webview.id === '1' && webview.nvue) { __uniConfig.onReady(function () { navigateFinish(); }); @@ -7471,7 +7603,7 @@ var serviceContext = (function () { var api = /*#__PURE__*/Object.freeze({ startPullDownRefresh: startPullDownRefresh, stopPullDownRefresh: stopPullDownRefresh, - getImageInfo: getImageInfo$1, + previewImage: previewImage$1, createAudioInstance: createAudioInstance, destroyAudioInstance: destroyAudioInstance, setAudioState: setAudioState, @@ -7533,12 +7665,12 @@ var serviceContext = (function () { chooseImage: chooseImage$1, chooseVideo: chooseVideo$1, compressImage: compressImage, + getImageInfo: getImageInfo$1, getMusicPlayerState: getMusicPlayerState, operateMusicPlayer: operateMusicPlayer, setBackgroundAudioState: setBackgroundAudioState, operateBackgroundAudio: operateBackgroundAudio, getBackgroundAudioState: getBackgroundAudioState, - previewImage: previewImage$1, operateRecorder: operateRecorder, saveImageToPhotosAlbum: saveImageToPhotosAlbum, saveVideoToPhotosAlbum: saveVideoToPhotosAlbum, @@ -7564,6 +7696,8 @@ var serviceContext = (function () { shareAppMessageDirectly: shareAppMessageDirectly, share: share, restoreGlobal: restoreGlobal, + getSubNVueById: getSubNVueById, + getCurrentSubNVue: getCurrentSubNVue, navigateBack: navigateBack$1, navigateTo: navigateTo$1, reLaunch: reLaunch$1, @@ -7815,7 +7949,7 @@ var serviceContext = (function () { 'pause', 'stop', 'ended', - 'timeupdate', + 'timeUpdate', 'prev', 'next', 'error', @@ -9181,7 +9315,9 @@ var serviceContext = (function () { callbacks$a.push(callbackId); } - function offWindowResize (callbackId) { + function offWindowResize (callbackId) { + // TODO 目前 on 和 off 即使传入同一个 function,获取到的 callbackId 也不会一致,导致不能 off 掉指定 + // 后续修复 // 此处和微信平台一致查询不到去掉最后一个 callbacks$a.splice(callbacks$a.indexOf(callbackId), 1); } @@ -9295,6 +9431,11 @@ var serviceContext = (function () { callAppHook(getApp(), 'onPageNotFound', page); } + function onResize (args, pageId) { + const page = getCurrentPages().find(page => page.$page.id === pageId); + page && callPageHook(page, 'onResize'); + } + function onPullDownRefresh (args, pageId) { const page = getCurrentPages().find(page => page.$page.id === pageId); if (page) { @@ -9352,6 +9493,7 @@ var serviceContext = (function () { on('onAppEnterBackground', onAppEnterBackground); on('onAppEnterForeground', onAppEnterForeground); + on('onResize', onResize); on('onPullDownRefresh', onPullDownRefresh); on('onTabItemTap', createCallCurrentPageHook('onTabItemTap')); @@ -9359,7 +9501,7 @@ var serviceContext = (function () { on('onNavigationBarSearchInputChanged', createCallCurrentPageHook('onNavigationBarSearchInputChanged')); on('onNavigationBarSearchInputConfirmed', createCallCurrentPageHook('onNavigationBarSearchInputConfirmed')); - on('onNavigationBarSearchInputClicked', createCallCurrentPageHook('onNavigationBarSearchInputClicked')); + on('onNavigationBarSearchInputClicked', createCallCurrentPageHook('onNavigationBarSearchInputClicked')); } function initSubscribe (subscribe, { @@ -9415,20 +9557,6 @@ var serviceContext = (function () { subscribe('onRequestComponentObserver', onRequestComponentObserver); } - const PAGE_CREATE = 2; - const MOUNTED_DATA = 4; - const UPDATED_DATA = 6; - const PAGE_CREATED = 10; - - const UI_EVENT = 20; - - const VD_SYNC = 'vdSync'; - - const WEBVIEW_READY = 'webviewReady'; - const VD_SYNC_CALLBACK = 'vdSyncCallback'; - const INVOKE_API = 'invokeApi'; - const WEB_INVOKE_APPSERVICE$1 = 'WEB_INVOKE_APPSERVICE'; - function perf (type, startTime) { /* eslint-disable no-undef */ startTime = startTime || __UniServiceStartTime__; @@ -9780,7 +9908,7 @@ var serviceContext = (function () { }; } - function wrapperEvent (event) { + function wrapperEvent(event) { event.preventDefault = noop; event.stopPropagation = noop; event.mp = event; @@ -9790,7 +9918,7 @@ var serviceContext = (function () { } const handleVdData = { - [UI_EVENT]: function onUIEvent (vdBatchEvent, vd) { + [UI_EVENT]: function onUIEvent(vdBatchEvent, vd) { vdBatchEvent.forEach(([cid, nid, event]) => { nid = String(nid); const target = vd.elements.find(target => target.cid === cid && target.nid === nid); @@ -9802,66 +9930,69 @@ var serviceContext = (function () { } }; - function onVdSync$1 (vdBatchData, vd) { + function onVdSync$1(vdBatchData, vd) { vdBatchData.forEach(([type, vdData]) => { handleVdData[type](vdData, vd); }); } class VDomSync { - constructor (pageId, pagePath) { + constructor(pageId, pagePath, pageVm) { this.pageId = pageId; this.pagePath = pagePath; + this.pageVm = pageVm; this.batchData = []; this.vms = Object.create(null); this.initialized = false; + this.pageCreateData = false; + this.elements = []; // 目前仅存储事件 element this._init(); } - _init () { + _init() { registerVdSync(this.pageId, (vdBatchData) => { onVdSync$1(vdBatchData, this); }); } - addMountedVm (vm) { + addMountedVm(vm) { vm._$mounted(); // 触发vd数据同步 - this.addVdSyncCallback(function mounted () { + this.addVdSyncCallback(function mounted() { vm.__call_hook('mounted'); }); } - addUpdatedVm (vm) { + addUpdatedVm(vm) { vm._$updated(); // 触发vd数据同步 - this.addVdSyncCallback(function mounted () { + this.addVdSyncCallback(function mounted() { vm.__call_hook('updated'); }); } - addVdSyncCallback (callback) { + addVdSyncCallback(callback) { isFn(callback) && vdSyncCallbacks.push(callback); } - getVm (id) { + getVm(id) { return this.vms[id] } - addVm (vm) { + addVm(vm) { this.vms[vm._$id] = vm; } - removeVm (vm) { + removeVm(vm) { delete this.vms[vm._$id]; } - addElement (elm) { + addElement(elm) { this.elements.indexOf(elm) === -1 && this.elements.push(elm); } - removeElement (elm) { + removeElement(elm) { const elmIndex = this.elements.indexOf(elm); if (elmIndex === -1) { return console.error(`removeElement[${elm.cid}][${elm.nid}] not found`) @@ -9869,11 +10000,23 @@ var serviceContext = (function () { this.elements.splice(elmIndex, 1); } - push (type, cid, data) { + push(type, cid, data) { this.batchData.push([type, [cid, data]]); } - flush () { + sendPageCreate(data) { + this.pageCreateData = data; + UniServiceJSBridge.publishHandler(VD_SYNC, { + data: [ + [PAGE_CREATE, data] + ], + options: { + timestamp: Date.now() + } + }, [this.pageId]); + } + + flush() { if (!this.initialized) { this.initialized = true; this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]]); @@ -9889,7 +10032,37 @@ var serviceContext = (function () { } } - destroy () { + restorePageCreate() { + this.batchData.push([PAGE_CREATE, this.pageCreateData]); + } + + restoreMountedData() { + const addMountedData = (vm) => { + if (vm._$id) { + this.push(MOUNTED_DATA, vm._$id, vm._$data); + } + // vue 中 $children 顺序不可靠,可能存在恢复误差 + vm.$children.forEach(childVm => addMountedData(childVm)); + }; + addMountedData(this.pageVm); + } + + restorePageCreated() { + this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]]); + } + + restore() { + this.initialized = true; + this.batchData.length = 0; + + this.restorePageCreate(); + this.restoreMountedData(); + this.restorePageCreated(); + + this.flush(); + } + + destroy() { this.batchData.length = 0; this.vms = Object.create(null); this.initialized = false; @@ -10025,7 +10198,7 @@ var serviceContext = (function () { return } if (this.mpType === 'page') { - this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath); + this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath, this); } if (this._$vd) { this._$id = guid(); @@ -10062,7 +10235,7 @@ var serviceContext = (function () { case B_STYLE: value = this._$normalizeStyleBinding(value); break - case V_IF: + case V_IF: case V_SHOW: case V_ELSE_IF: value = value ? 1 : 0; @@ -10199,14 +10372,7 @@ var serviceContext = (function () { const route = this.$scope.route; const pageId = this.$scope.$page.id; // 通知页面已开始创建 - UniServiceJSBridge.publishHandler(VD_SYNC, { - data: [ - [PAGE_CREATE, [pageId, route, parsePageCreateOptions(this, route)]] - ], - options: { - timestamp: Date.now() - } - }, [pageId]); + this._$vd.sendPageCreate([pageId, route, parsePageCreateOptions(this, route)]); } }, created () { diff --git a/src/platforms/app-plus/service/framework/page.js b/src/platforms/app-plus/service/framework/page.js index be4015afa..c018004f1 100644 --- a/src/platforms/app-plus/service/framework/page.js +++ b/src/platforms/app-plus/service/framework/page.js @@ -42,7 +42,8 @@ export function registerPage ({ if (!webview) { webview = createWebview(path, routeOptions) } else { - webview = plus.webview.getWebviewById(webview.id) + webview = plus.webview.getWebviewById(webview.id) + webview.nvue = routeOptions.meta.isNVue } if (routeOptions.meta.isTabBar) { @@ -93,8 +94,7 @@ export function registerPage ({ pages.push(pageInstance) // 首页是 nvue 时,在 registerPage 时,执行路由堆栈 - if (webview.id === '1' && routeOptions.meta.isNVue) { - webview.nvue = true + if (webview.id === '1' && webview.nvue) { __uniConfig.onReady(function () { navigateFinish(webview) }) diff --git a/src/platforms/app-plus/service/framework/plugins/data.js b/src/platforms/app-plus/service/framework/plugins/data.js index 839f34bd3..74a2dc3ae 100644 --- a/src/platforms/app-plus/service/framework/plugins/data.js +++ b/src/platforms/app-plus/service/framework/plugins/data.js @@ -14,7 +14,7 @@ import { import { V_IF, - V_FOR, + V_FOR, V_SHOW, V_ELSE_IF, B_CLASS, @@ -78,7 +78,7 @@ export function initData (Vue) { return } if (this.mpType === 'page') { - this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath) + this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath, this) } if (this._$vd) { this._$id = guid() @@ -115,7 +115,7 @@ function setData (id, name, value) { case B_STYLE: value = this._$normalizeStyleBinding(value) break - case V_IF: + case V_IF: case V_SHOW: case V_ELSE_IF: value = value ? 1 : 0 diff --git a/src/platforms/app-plus/service/framework/plugins/lifecycle.js b/src/platforms/app-plus/service/framework/plugins/lifecycle.js index b7f6a1dc2..4ff2ba610 100644 --- a/src/platforms/app-plus/service/framework/plugins/lifecycle.js +++ b/src/platforms/app-plus/service/framework/plugins/lifecycle.js @@ -15,11 +15,6 @@ import { } from 'uni-core/service/plugins/lifecycle' -import { - VD_SYNC, - PAGE_CREATE -} from '../../../constants' - import { ON_REACH_BOTTOM_DISTANCE } @@ -59,14 +54,7 @@ export function initLifecycle (Vue) { const route = this.$scope.route const pageId = this.$scope.$page.id // 通知页面已开始创建 - UniServiceJSBridge.publishHandler(VD_SYNC, { - data: [ - [PAGE_CREATE, [pageId, route, parsePageCreateOptions(this, route)]] - ], - options: { - timestamp: Date.now() - } - }, [pageId]) + this._$vd.sendPageCreate([pageId, route, parsePageCreateOptions(this, route)]) } }, created () { diff --git a/src/platforms/app-plus/service/framework/plugins/vdom-sync.js b/src/platforms/app-plus/service/framework/plugins/vdom-sync.js index 880d7cb38..ced7b135d 100644 --- a/src/platforms/app-plus/service/framework/plugins/vdom-sync.js +++ b/src/platforms/app-plus/service/framework/plugins/vdom-sync.js @@ -6,7 +6,9 @@ import { import { VD_SYNC, UI_EVENT, - PAGE_CREATED + PAGE_CREATE, + PAGE_CREATED, + MOUNTED_DATA } from '../../../constants' import { @@ -18,7 +20,7 @@ import { vdSyncCallbacks } from '../subscribe-handlers/on-vd-sync-callback' -function wrapperEvent (event) { +function wrapperEvent(event) { event.preventDefault = noop event.stopPropagation = noop event.mp = event @@ -28,7 +30,7 @@ function wrapperEvent (event) { } const handleVdData = { - [UI_EVENT]: function onUIEvent (vdBatchEvent, vd) { + [UI_EVENT]: function onUIEvent(vdBatchEvent, vd) { vdBatchEvent.forEach(([cid, nid, event]) => { nid = String(nid) const target = vd.elements.find(target => target.cid === cid && target.nid === nid) @@ -40,66 +42,69 @@ const handleVdData = { } } -function onVdSync (vdBatchData, vd) { +function onVdSync(vdBatchData, vd) { vdBatchData.forEach(([type, vdData]) => { handleVdData[type](vdData, vd) }) } export class VDomSync { - constructor (pageId, pagePath) { + constructor(pageId, pagePath, pageVm) { this.pageId = pageId this.pagePath = pagePath + this.pageVm = pageVm this.batchData = [] this.vms = Object.create(null) this.initialized = false + this.pageCreateData = false + this.elements = [] // 目前仅存储事件 element this._init() } - _init () { + _init() { registerVdSync(this.pageId, (vdBatchData) => { onVdSync(vdBatchData, this) }) } - addMountedVm (vm) { + addMountedVm(vm) { vm._$mounted() // 触发vd数据同步 - this.addVdSyncCallback(function mounted () { + this.addVdSyncCallback(function mounted() { vm.__call_hook('mounted') }) } - addUpdatedVm (vm) { + addUpdatedVm(vm) { vm._$updated() // 触发vd数据同步 - this.addVdSyncCallback(function mounted () { + this.addVdSyncCallback(function mounted() { vm.__call_hook('updated') }) } - addVdSyncCallback (callback) { + addVdSyncCallback(callback) { isFn(callback) && vdSyncCallbacks.push(callback) } - getVm (id) { + getVm(id) { return this.vms[id] } - addVm (vm) { + addVm(vm) { this.vms[vm._$id] = vm } - removeVm (vm) { + removeVm(vm) { delete this.vms[vm._$id] } - addElement (elm) { + addElement(elm) { this.elements.indexOf(elm) === -1 && this.elements.push(elm) } - removeElement (elm) { + removeElement(elm) { const elmIndex = this.elements.indexOf(elm) if (elmIndex === -1) { return console.error(`removeElement[${elm.cid}][${elm.nid}] not found`) @@ -107,11 +112,23 @@ export class VDomSync { this.elements.splice(elmIndex, 1) } - push (type, cid, data) { + push(type, cid, data) { this.batchData.push([type, [cid, data]]) } - flush () { + sendPageCreate(data) { + this.pageCreateData = data + UniServiceJSBridge.publishHandler(VD_SYNC, { + data: [ + [PAGE_CREATE, data] + ], + options: { + timestamp: Date.now() + } + }, [this.pageId]) + } + + flush() { if (!this.initialized) { this.initialized = true this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]]) @@ -127,7 +144,37 @@ export class VDomSync { } } - destroy () { + restorePageCreate() { + this.batchData.push([PAGE_CREATE, this.pageCreateData]) + } + + restoreMountedData() { + const addMountedData = (vm) => { + if (vm._$id) { + this.push(MOUNTED_DATA, vm._$id, vm._$data) + } + // vue 中 $children 顺序不可靠,可能存在恢复误差 + vm.$children.forEach(childVm => addMountedData(childVm)) + } + addMountedData(this.pageVm) + } + + restorePageCreated() { + this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]]) + } + + restore() { + this.initialized = true + this.batchData.length = 0 + + this.restorePageCreate() + this.restoreMountedData() + this.restorePageCreated() + + this.flush() + } + + destroy() { this.batchData.length = 0 this.vms = Object.create(null) this.initialized = false diff --git a/src/platforms/app-plus/service/framework/webview/index.js b/src/platforms/app-plus/service/framework/webview/index.js index 6bb382096..1062885e9 100644 --- a/src/platforms/app-plus/service/framework/webview/index.js +++ b/src/platforms/app-plus/service/framework/webview/index.js @@ -10,6 +10,10 @@ import { VIEW_WEBVIEW_PATH } from '../../constants' +import { + WEBVIEW_READY +} from '../../../constants' + export let preloadWebview let id = 2 @@ -47,6 +51,59 @@ export function createWebview (path, routeOptions) { return webview } +function onWebviewResize (webview) { + webview.addEventListener('resize', ({ + width, + height + }) => { + const landscape = Math.abs(plus.navigator.getOrientation()) === 90 + const res = { + deviceOrientation: landscape ? 'landscape' : 'portrait', + size: { + windowWidth: Math.ceil(width), + windowHeight: Math.ceil(height) + } + } + publish('onViewDidResize', res) // API + UniServiceJSBridge.emit('onResize', res, parseInt(webview.id)) // Page lifecycle + }) +} + +function onWebviewRecovery (webview, routeOptions) { + const { + subscribe, + unsubscribe + } = UniServiceJSBridge + + const id = webview.id + const onWebviewRecoveryReady = function (data, pageId) { + if (id !== pageId) { + return + } + unsubscribe(WEBVIEW_READY, onWebviewRecoveryReady) + if (process.env.NODE_ENV !== 'production') { + console.log(`UNIAPP[webview][${this.id}]:onWebviewRecoveryReady ready`) + } + // 恢复目标页面 + pageId = parseInt(pageId) + const page = getCurrentPages(true).find(page => page.$page.id === pageId) + if (!page) { + return console.error(`Page[${pageId}] not found`) + } + page.$vm._$vd.restore() + } + + webview.addEventListener('recovery', e => { + if (process.env.NODE_ENV !== 'production') { + console.log(`UNIAPP[webview][${this.id}].recovery.reload:` + JSON.stringify({ + path: routeOptions.path, + webviewId: id + })) + } + subscribe(WEBVIEW_READY, onWebviewRecoveryReady) + }) +} + export function initWebview (webview, routeOptions) { // 首页或非 nvue 页面 if (webview.id === '1' || !routeOptions.meta.isNVue) { @@ -74,19 +131,11 @@ export function initWebview (webview, routeOptions) { }) }) - webview.addEventListener('resize', ({ - width, - height - }) => { - const res = { - size: { - windowWidth: Math.ceil(width), - windowHeight: Math.ceil(height) - } - } - publish('onViewDidResize', res) - emit('onResize', res, parseInt(webview.id)) - }) + onWebviewResize(webview) + + if (plus.os.name === 'iOS' && webview.nvue) { + onWebviewRecovery(webview, routeOptions) + } // TODO 应该结束之前未完成的下拉刷新 on(webview.id + '.startPullDownRefresh', () => { @@ -102,9 +151,9 @@ export function initWebview (webview, routeOptions) { export function createPreloadWebview () { if (!preloadWebview || preloadWebview.__uniapp_route) { // 不存在,或已被使用 - preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(id++)) - if (process.env.NODE_ENV !== 'production') { - console.log(`[uni-app] preloadWebview[${preloadWebview.id}]`) + preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(id++)) + if (process.env.NODE_ENV !== 'production') { + console.log(`[uni-app] preloadWebview[${preloadWebview.id}]`) } } return preloadWebview -- GitLab