提交 956af9e4 编写于 作者: fxy060608's avatar fxy060608

feat(app): support eventChannel

上级 7147c358
......@@ -8,6 +8,7 @@ declare namespace Page {
options: Record<string, any>
meta: UniApp.PageRouteMeta
openType: UniApp.OpenType
eventChannel: unknown
statusBarStyle?: 'dark' | 'light'
}
}
......
import { EventChannel } from '@dcloudio/uni-shared'
import { UniLifecycleHooks } from '@dcloudio/uni-vue/src/apiLifecycle'
import { ComponentCustomProperties, ComponentInternalInstance } from 'vue'
declare module '@vue/runtime-core' {
......@@ -8,6 +9,7 @@ declare module '@vue/runtime-core' {
}
// 目前 H5,APP 平台 getCurrentPages 中获取的 page 对象调整为 vm 对象
$getAppWebview?: () => PlusWebviewWebviewObject
getOpenerEventChannel: () => EventChannel
$page: Page.PageInstance['$page']
$mpType?: 'app' | 'page'
__isTabBar: boolean
......
......@@ -1160,7 +1160,70 @@ var serviceContext = (function (vue) {
const ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED = 'onNavigationBarSearchInputFocusChanged';
// framework
const ON_APP_ENTER_FOREGROUND = 'onAppEnterForeground';
const ON_APP_ENTER_BACKGROUND = 'onAppEnterBackground';
const ON_APP_ENTER_BACKGROUND = 'onAppEnterBackground';
class EventChannel {
constructor(id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach((name) => {
this.on(name, events[name]);
});
}
}
emit(eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args);
}
fns.forEach((opt) => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter((opt) => opt.type !== 'once');
}
on(eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once(eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off(eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return;
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
}
else {
delete this.listener[eventName];
}
}
_clearCache(eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName, ...cacheArgs.shift()]);
}
}
}
_addListener(eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type,
});
}
}
const isObject = (val) => val !== null && typeof val === 'object';
class BaseFormatter {
......@@ -1842,7 +1905,7 @@ var serviceContext = (function (vue) {
}
return pullToRefresh;
}
function initPageInternalInstance(openType, url, pageQuery, meta) {
function initPageInternalInstance(openType, url, pageQuery, meta, eventChannel) {
const { id, route } = meta;
return {
id: id,
......@@ -1852,6 +1915,7 @@ var serviceContext = (function (vue) {
options: pageQuery,
meta,
openType,
eventChannel,
statusBarStyle: meta.navigationBar.titleColor === '#000000' ? 'dark' : 'light',
};
}
......@@ -10571,7 +10635,7 @@ var serviceContext = (function (vue) {
const instance = vue.getCurrentInstance();
const pageVm = instance.proxy;
initPageVm(pageVm, __pageInstance);
addCurrentPage(initScope(__pageId, pageVm));
addCurrentPage(initScope(__pageId, pageVm, __pageInstance));
vue.onMounted(() => {
invokeHook(pageVm, ON_READY);
// TODO preloadSubPackages
......@@ -10585,7 +10649,7 @@ var serviceContext = (function (vue) {
};
return component;
}
function initScope(pageId, vm) {
function initScope(pageId, vm, pageInstance) {
const $getAppWebview = () => {
return plus.webview.getWebviewById(pageId + '');
};
......@@ -10593,6 +10657,12 @@ var serviceContext = (function (vue) {
vm.$scope = {
$getAppWebview,
};
vm.getOpenerEventChannel = () => {
if (!pageInstance.eventChannel) {
pageInstance.eventChannel = new EventChannel(pageId);
}
return pageInstance.eventChannel;
};
return vm;
}
......@@ -10693,7 +10763,7 @@ var serviceContext = (function (vue) {
return preloadWebviews[url];
}
function registerPage({ url, path, query, openType, webview, }) {
function registerPage({ url, path, query, openType, webview, eventChannel, }) {
// fast 模式,nvue 首页时,会在nvue中主动调用registerPage并传入首页webview,此时初始化一下首页(因为此时可能还未调用registerApp)
if (webview) {
initEntry();
......@@ -10710,7 +10780,9 @@ var serviceContext = (function (vue) {
webview = undefined;
}
else {
// TODO eventChannel
if (eventChannel) {
_webview.__page__.$page.eventChannel = eventChannel;
}
addCurrentPage(_webview.__page__);
if ((process.env.NODE_ENV !== 'production')) {
console.log(formatLog('uni-app', `reuse preloadWebview(${path},${_webview.id})`));
......@@ -10738,11 +10810,11 @@ var serviceContext = (function (vue) {
initWebview(webview, path, query, routeOptions.meta);
const route = path.substr(1);
webview.__uniapp_route = route;
const pageInstance = initPageInternalInstance(openType, url, query, routeOptions.meta);
const pageInstance = initPageInternalInstance(openType, url, query, routeOptions.meta, eventChannel);
initNVueEntryPage(webview);
if (webview.nvue) {
// nvue 时,先启用一个占位 vm
const fakeNVueVm = createNVueVm(webview, pageInstance);
const fakeNVueVm = createNVueVm(parseInt(webview.id), webview, pageInstance);
initPageVm(fakeNVueVm, pageInstance);
addCurrentPage(fakeNVueVm);
}
......@@ -10787,12 +10859,13 @@ var serviceContext = (function (vue) {
});
}
}
function createNVueVm(webview, pageInstance) {
function createNVueVm(pageId, webview, pageInstance) {
return {
$: {},
onNVuePageCreated(vm, curNVuePage) {
vm.$ = {}; // 补充一个 nvue 的 $ 对象,模拟 vue3 的,不然有部分地方访问了 $
vm.$getAppWebview = () => webview;
vm.getOpenerEventChannel = curNVuePage.getOpenerEventChannel;
// 替换真实的 nvue 的 vm
initPageVm(vm, pageInstance);
const pages = getAllPages();
......@@ -10807,11 +10880,17 @@ var serviceContext = (function (vue) {
$getAppWebview() {
return webview;
},
getOpenerEventChannel() {
if (!pageInstance.eventChannel) {
pageInstance.eventChannel = new EventChannel(pageId);
}
return pageInstance.eventChannel;
},
};
}
const $navigateTo = (args, { resolve, reject }) => {
const { url, animationType, animationDuration } = args;
const { url, events, animationType, animationDuration } = args;
const { path, query } = parseUrl(url);
const [aniType, aniDuration] = initAnimation(path, animationType, animationDuration);
navigate(path, () => {
......@@ -10819,6 +10898,7 @@ var serviceContext = (function (vue) {
url,
path,
query,
events,
aniType,
aniDuration,
})
......@@ -10827,13 +10907,13 @@ var serviceContext = (function (vue) {
}, args.openType === 'appLaunch');
};
const navigateTo = defineAsyncApi(API_NAVIGATE_TO, $navigateTo, NavigateToProtocol, NavigateToOptions);
function _navigateTo({ url, path, query, aniType, aniDuration, }) {
// TODO eventChannel
function _navigateTo({ url, path, query, events, aniType, aniDuration, }) {
// 当前页面触发 onHide
invokeHook(ON_HIDE);
const eventChannel = new EventChannel(getWebviewId() + 1, events);
return new Promise((resolve) => {
showWebview(registerPage({ url, path, query, openType: 'navigateTo' }), aniType, aniDuration, () => {
resolve(undefined);
showWebview(registerPage({ url, path, query, openType: 'navigateTo', eventChannel }), aniType, aniDuration, () => {
resolve({ eventChannel });
});
});
}
......
......@@ -736,8 +736,8 @@
function normalizeViewMethodName(pageId, name) {
return pageId + "." + name;
}
function subscribeViewMethod(pageId) {
UniViewJSBridge.subscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API), onInvokeViewMethod);
function subscribeViewMethod(pageId, wrapper2) {
UniViewJSBridge.subscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API), wrapper2 ? wrapper2(onInvokeViewMethod) : onInvokeViewMethod);
}
function registerViewMethod(pageId, name, fn) {
name = normalizeViewMethodName(pageId, name);
......@@ -16835,21 +16835,20 @@
});
}
const pageVm = { $el: document.body };
function wrapperViewMethod(fn) {
return (...args) => {
onPageReady(() => {
fn.apply(null, args);
});
};
}
function initViewMethods() {
const pageId = getCurrentPageId();
subscribeViewMethod(pageId);
registerViewMethod(pageId, "requestComponentInfo", wrapperViewMethod((args, publish) => {
subscribeViewMethod(pageId, (fn) => {
return (...args) => {
onPageReady(() => {
fn.apply(null, args);
});
};
});
registerViewMethod(pageId, "requestComponentInfo", (args, publish) => {
requestComponentInfo(pageVm, args.reqs, publish);
}));
registerViewMethod(pageId, API_PAGE_SCROLL_TO, wrapperViewMethod(pageScrollTo));
registerViewMethod(pageId, API_LOAD_FONT_FACE, wrapperViewMethod(loadFontFace));
});
registerViewMethod(pageId, API_PAGE_SCROLL_TO, pageScrollTo);
registerViewMethod(pageId, API_LOAD_FONT_FACE, loadFontFace);
}
window.uni = uni$1;
window.UniViewJSBridge = UniViewJSBridge$1;
......
import { ON_HIDE, parseUrl } from '@dcloudio/uni-shared'
import { EventChannel, ON_HIDE, parseUrl } from '@dcloudio/uni-shared'
import { getRouteMeta, invokeHook } from '@dcloudio/uni-core'
import {
API_NAVIGATE_TO,
......@@ -13,12 +13,13 @@ import { ANI_DURATION, ANI_SHOW } from '../../constants'
import { navigate, RouteOptions } from './utils'
import { showWebview } from './webview'
import { registerPage } from '../../framework/page'
import { getWebviewId } from '../../framework/webview/utils'
export const $navigateTo: DefineAsyncApiFn<API_TYPE_NAVIGATE_TO> = (
args,
{ resolve, reject }
) => {
const { url, animationType, animationDuration } = args
const { url, events, animationType, animationDuration } = args
const { path, query } = parseUrl(url)
const [aniType, aniDuration] = initAnimation(
path,
......@@ -32,6 +33,7 @@ export const $navigateTo: DefineAsyncApiFn<API_TYPE_NAVIGATE_TO> = (
url,
path,
query,
events,
aniType,
aniDuration,
})
......@@ -50,6 +52,7 @@ export const navigateTo = defineAsyncApi<API_TYPE_NAVIGATE_TO>(
)
interface NavigateToOptions extends RouteOptions {
events: Record<string, any>
aniType: string
aniDuration: number
}
......@@ -58,19 +61,20 @@ function _navigateTo({
url,
path,
query,
events,
aniType,
aniDuration,
}: NavigateToOptions): Promise<undefined> {
// TODO eventChannel
}: NavigateToOptions): Promise<void | { eventChannel: EventChannel }> {
// 当前页面触发 onHide
invokeHook(ON_HIDE)
const eventChannel = new EventChannel(getWebviewId() + 1, events)
return new Promise((resolve) => {
showWebview(
registerPage({ url, path, query, openType: 'navigateTo' }),
registerPage({ url, path, query, openType: 'navigateTo', eventChannel }),
aniType,
aniDuration,
() => {
resolve(undefined)
resolve({ eventChannel })
}
)
})
......
import { ComponentPublicInstance } from 'vue'
import { hasOwn } from '@vue/shared'
import {
EventChannel,
formatLog,
NAVBAR_HEIGHT,
ON_REACH_BOTTOM_DISTANCE,
......@@ -25,7 +26,7 @@ interface RegisterPageOptions {
query: Record<string, string>
openType: UniApp.OpenType
webview?: PlusWebviewWebviewObject
// eventChannel: unknown
eventChannel?: EventChannel
}
export function registerPage({
......@@ -34,6 +35,7 @@ export function registerPage({
query,
openType,
webview,
eventChannel,
}: RegisterPageOptions) {
// fast 模式,nvue 首页时,会在nvue中主动调用registerPage并传入首页webview,此时初始化一下首页(因为此时可能还未调用registerApp)
if (webview) {
......@@ -56,7 +58,9 @@ export function registerPage({
}
webview = undefined
} else {
// TODO eventChannel
if (eventChannel) {
_webview.__page__.$page.eventChannel = eventChannel
}
addCurrentPage(_webview.__page__)
if (__DEV__) {
console.log(
......@@ -97,14 +101,19 @@ export function registerPage({
openType,
url,
query,
routeOptions.meta
routeOptions.meta,
eventChannel
)
initNVueEntryPage(webview)
if ((webview as any).nvue) {
// nvue 时,先启用一个占位 vm
const fakeNVueVm = createNVueVm(webview, pageInstance)
const fakeNVueVm = createNVueVm(
parseInt(webview.id!),
webview,
pageInstance
)
initPageVm(fakeNVueVm, pageInstance)
addCurrentPage(fakeNVueVm)
} else {
......@@ -162,6 +171,7 @@ function initNVueEntryPage(webview: PlusWebviewWebviewObject) {
}
function createNVueVm(
pageId: number,
webview: PlusWebviewWebviewObject,
pageInstance: Page.PageInstance['$page']
) {
......@@ -170,6 +180,7 @@ function createNVueVm(
onNVuePageCreated(vm: ComponentPublicInstance, curNVuePage: unknown) {
;(vm as any).$ = {} // 补充一个 nvue 的 $ 对象,模拟 vue3 的,不然有部分地方访问了 $
vm.$getAppWebview = () => webview
vm.getOpenerEventChannel = (curNVuePage as any).getOpenerEventChannel
// 替换真实的 nvue 的 vm
initPageVm(vm, pageInstance)
const pages = getAllPages()
......@@ -184,5 +195,11 @@ function createNVueVm(
$getAppWebview() {
return webview
},
getOpenerEventChannel() {
if (!pageInstance.eventChannel) {
pageInstance.eventChannel = new EventChannel(pageId)
}
return pageInstance.eventChannel as EventChannel
},
} as unknown as ComponentPublicInstance
}
import { initPageVm, invokeHook } from '@dcloudio/uni-core'
import { formatLog, ON_READY, ON_UNLOAD } from '@dcloudio/uni-shared'
import {
EventChannel,
formatLog,
ON_READY,
ON_UNLOAD,
} from '@dcloudio/uni-shared'
import {
ComponentPublicInstance,
getCurrentInstance,
......@@ -22,7 +27,13 @@ export function setupPage(component: VuePageComponent) {
const instance = getCurrentInstance()!
const pageVm = instance.proxy!
initPageVm(pageVm, __pageInstance as Page.PageInstance['$page'])
addCurrentPage(initScope(__pageId as number, pageVm))
addCurrentPage(
initScope(
__pageId as number,
pageVm,
__pageInstance as Page.PageInstance['$page']
)
)
onMounted(() => {
invokeHook(pageVm, ON_READY)
// TODO preloadSubPackages
......@@ -37,7 +48,11 @@ export function setupPage(component: VuePageComponent) {
return component
}
function initScope(pageId: number, vm: ComponentPublicInstance) {
function initScope(
pageId: number,
vm: ComponentPublicInstance,
pageInstance: Page.PageInstance['$page']
) {
const $getAppWebview = () => {
return plus.webview.getWebviewById(pageId + '')
}
......@@ -45,5 +60,11 @@ function initScope(pageId: number, vm: ComponentPublicInstance) {
vm.$scope = {
$getAppWebview,
}
vm.getOpenerEventChannel = () => {
if (!pageInstance.eventChannel) {
pageInstance.eventChannel = new EventChannel(pageId)
}
return pageInstance.eventChannel as EventChannel
}
return vm
}
import { EventChannel } from '@dcloudio/uni-shared'
import { extend } from '@vue/shared'
import { ComponentPublicInstance, getCurrentInstance } from 'vue'
import { rpx2px } from './util'
......@@ -108,7 +109,8 @@ export function initPageInternalInstance(
openType: UniApp.OpenType,
url: string,
pageQuery: Record<string, any>,
meta: UniApp.PageRouteMeta
meta: UniApp.PageRouteMeta,
eventChannel?: EventChannel
): Page.PageInstance['$page'] {
const { id, route } = meta
return {
......@@ -119,6 +121,7 @@ export function initPageInternalInstance(
options: pageQuery,
meta,
openType,
eventChannel,
statusBarStyle:
meta.navigationBar.titleColor === '#000000' ? 'dark' : 'light',
}
......
......@@ -505,7 +505,7 @@ function normalizePullToRefreshRpx(pullToRefresh) {
}
return pullToRefresh;
}
function initPageInternalInstance(openType, url, pageQuery, meta) {
function initPageInternalInstance(openType, url, pageQuery, meta, eventChannel) {
const { id, route } = meta;
return {
id,
......@@ -515,6 +515,7 @@ function initPageInternalInstance(openType, url, pageQuery, meta) {
options: pageQuery,
meta,
openType,
eventChannel,
statusBarStyle: meta.navigationBar.titleColor === "#000000" ? "dark" : "light"
};
}
......
......@@ -479,8 +479,8 @@ const viewMethods = Object.create(null);
function normalizeViewMethodName(pageId, name) {
return pageId + "." + name;
}
function subscribeViewMethod(pageId) {
UniViewJSBridge.subscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API), onInvokeViewMethod);
function subscribeViewMethod(pageId, wrapper2) {
UniViewJSBridge.subscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API), wrapper2 ? wrapper2(onInvokeViewMethod) : onInvokeViewMethod);
}
function unsubscribeViewMethod(pageId) {
UniViewJSBridge.unsubscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API));
......@@ -956,7 +956,7 @@ function normalizePullToRefreshRpx(pullToRefresh) {
}
return pullToRefresh;
}
function initPageInternalInstance(openType, url, pageQuery, meta) {
function initPageInternalInstance(openType, url, pageQuery, meta, eventChannel) {
const { id: id2, route } = meta;
return {
id: id2,
......@@ -966,6 +966,7 @@ function initPageInternalInstance(openType, url, pageQuery, meta) {
options: pageQuery,
meta,
openType,
eventChannel,
statusBarStyle: meta.navigationBar.titleColor === "#000000" ? "dark" : "light"
};
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册