diff --git a/runtime/main/app/App.ts b/runtime/main/app/App.ts index bf8089c1..1b23d10a 100644 --- a/runtime/main/app/App.ts +++ b/runtime/main/app/App.ts @@ -13,13 +13,13 @@ * limitations under the License. */ -import Page from '../page/index'; +import { pageMap, PageLinkedMap } from './map'; /** * This class defines the information of a application. */ export class App { - public static pageMap: Map = new Map(); + public static pageMap: PageLinkedMap = pageMap; private _packageName: string; private _appInstanceId: string; private _events: object; @@ -140,14 +140,6 @@ export class App { that._appGlobal[api] = timerAPIs[api]; }); } - - /** - * Get page. - * @return {Page} Page. - */ - public getAppInstance(): Page { - return App.pageMap.get(this._appInstanceId); - } } /** diff --git a/runtime/main/app/bundle.ts b/runtime/main/app/bundle.ts index d7558684..2782e9c0 100644 --- a/runtime/main/app/bundle.ts +++ b/runtime/main/app/bundle.ts @@ -32,7 +32,7 @@ import { registerCustomComponent, requireModule } from '../page/register'; -import { appMap } from './map'; +import { pageMap, appMap } from './map'; import { updateLocale, updateDpi } from './index'; import Page from '../page/index'; import { App } from './App'; @@ -58,7 +58,7 @@ export const defineFn = function(page: Page, packageName: string, name?: string, const pageRequire = (name: string) : any => { if (isModule(name)) { const appFunction = (): Page => { - return page || appMap[packageName].getAppInstance(); + return pageMap.getTop(packageName) || page; }; return requireModule(appFunction, removePrefix(name)); } diff --git a/runtime/main/app/index.ts b/runtime/main/app/index.ts index 4f30496e..50c9dfa8 100644 --- a/runtime/main/app/index.ts +++ b/runtime/main/app/index.ts @@ -22,6 +22,7 @@ import { import { appMap } from './map'; import { getPageGlobal } from './helper'; import { App } from './App'; +import { PageLinkedMap } from './map'; import Page from '../page/index'; import { destroy } from '../page/api/index'; import { mockSystemPlugin } from '../extend/systemplugin/index'; @@ -77,7 +78,7 @@ interface ParseOptions { $app_require$(name: string): void; // eslint-disable-line camelcase } -const pageMap: Map = App.pageMap; +const pageMap: PageLinkedMap = App.pageMap; /** * Create app page, run jsbundle code. @@ -96,7 +97,7 @@ export function appCreate(page: Page, options: Options, data: object, services: } const packageName: string = page.packageName; const appPage: Page = new Page(options.appInstanceId, options, packageName, data); - pageMap.set(appPage.id, appPage); + pageMap.unshift(appPage); Log.debug(`Create a page with: ${packageName}.`); appMap[packageName] = new App(packageName, options.appInstanceId); const timerAPIs: object = genTimerAPI(appPage); @@ -111,8 +112,10 @@ export function appCreate(page: Page, options: Options, data: object, services: Log.debug(`After create a page(${page.id}).`); }; + const appFunction = () => pageMap.getTop(packageName) || page; + // require in top app(instance) - const appRequireModule = name => requireModule(page || appPage, removePrefix(name)); + const appRequireModule = name => requireModule(appFunction, removePrefix(name)); const parseOptions: ParseOptions = { $app_define$: appDefine, $app_bootstrap$: appBootstrap, @@ -189,7 +192,7 @@ export function appDestroy(packageName: string): void { app.emitEvent('hook:onDestroy'); app.deleteGlobalKeys(); delete appMap[packageName]; - const appPage: Page = pageMap.get(app.appInstanceId); + const appPage: Page = pageMap[app.appInstanceId]; if (appPage) { if (appPage.doc.taskCenter.callbackIsEmpty()) { appPage.callTasks([{ @@ -198,7 +201,7 @@ export function appDestroy(packageName: string): void { args: [] }]); destroy(appPage); - pageMap.delete(appPage.id); + pageMap.remove(appPage); } else { appPage.destroyed = true; } diff --git a/runtime/main/app/map.ts b/runtime/main/app/map.ts index deb28b8a..15592740 100644 --- a/runtime/main/app/map.ts +++ b/runtime/main/app/map.ts @@ -1,19 +1,76 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +import Page from '../page/index'; + /** - * Save App. - */ -export const appMap: object = {}; + * This class defines the map for the page instances. + */ +export class PageLinkedMap { + public _map: Page[]; + + constructor() { + this._map = []; + } + + public unshift(instance: Page) { + this._map.unshift(instance); + Object.defineProperty(this, instance.id, { + configurable: true, + enumerable: true, + get: function proxyGetter() { + return proxyGet(this, instance.id); + } + }); + } + + public push(instance: Page) { + this._map.push(instance); + Object.defineProperty(this, instance.id, { + configurable: true, + enumerable: true, + get: function proxyGetter() { + return proxyGet(this, instance.id); + } + }); + } + + public remove(instance: Page) { + const index = this._map.indexOf(instance); + delete this[instance.id]; + // @ts-ignore + delete this._map.splice(index, 1); + } + + public getTop(packageName: string) { + const appMap = this._map.filter(instance => { + return instance.packageName === packageName; + }); + return appMap && appMap[appMap.length - 1]; + } +} + +function proxyGet(pageLinkedMap: PageLinkedMap, id: string) { + const index = pageLinkedMap._map.map(instance => instance.id).lastIndexOf(id); + return pageLinkedMap._map[index]; +} + +export const pageMap: PageLinkedMap = new PageLinkedMap(); + +export const appMap = {}; diff --git a/runtime/main/manage/event/bridge.ts b/runtime/main/manage/event/bridge.ts index 853cf9c1..c7466a23 100644 --- a/runtime/main/manage/event/bridge.ts +++ b/runtime/main/manage/event/bridge.ts @@ -23,6 +23,7 @@ import { Log } from '../../../utils/index'; import { App } from '../../app/App'; +import { PageLinkedMap } from '../../app/map'; import Page from '../../page'; import { fireEvent, @@ -31,7 +32,7 @@ import { destroy } from '../../page/api/index'; -const pageMap: Map = App.pageMap; +const pageMap: PageLinkedMap = App.pageMap; const eventHandlers = { /** @@ -40,7 +41,7 @@ const eventHandlers = { * @param {*} args - Args. */ fireEvent: (id: string, ...args: any[]) => { - return fireEvent(pageMap.get(id), ...args); + return fireEvent(pageMap[id], ...args); }, /** @@ -49,7 +50,7 @@ const eventHandlers = { * @param {*} args - Args */ callback: (id: string, ...args: any[]) => { - return callback(pageMap.get(id), ...args); + return callback(pageMap[id], ...args); }, /** @@ -58,7 +59,7 @@ const eventHandlers = { * @param {*} args - Args. */ fireEventSync: (id: string, ...args: any[]) => { - return fireEventSync(pageMap.get(id), ...args); + return fireEventSync(pageMap[id], ...args); } }; @@ -71,7 +72,7 @@ const eventHandlers = { export function receiveTasks(id: string, tasks: any[]): any[] | Error { id = id.toString(); Log.debug(`ReceiveTasks id ${id}, tasks: ${JSON.stringify(tasks)}`); - const page: Page = pageMap.get(id); + const page: Page = pageMap[id]; if (page && Array.isArray(tasks)) { const results = []; tasks.forEach((task) => { @@ -89,7 +90,7 @@ export function receiveTasks(id: string, tasks: any[]): any[] | Error { args: [] }]); destroy(page); - pageMap.delete(id); + pageMap.remove(page); } return results; } diff --git a/runtime/main/manage/instance/life.ts b/runtime/main/manage/instance/life.ts index 089db9ce..cc23c959 100644 --- a/runtime/main/manage/instance/life.ts +++ b/runtime/main/manage/instance/life.ts @@ -19,12 +19,13 @@ import { destroy } from '../../page/api/index'; import { App } from '../../app/App'; +import { PageLinkedMap } from '../../app/map'; import { resetTarget } from '../../reactivity/dep'; import Page from '../../page'; import { init as initApp } from '../../page/api/index'; import { appCreate, Options } from '../../app/index'; -const pageMap: Map = App.pageMap; +const pageMap: PageLinkedMap = App.pageMap; /** * Create a page. @@ -40,14 +41,14 @@ export function createInstance(id: string, code: string, options: Options, data: const { I18n, dpi } = services; resetTarget(); - let page: Page = pageMap.get(id); + let page: Page = pageMap[id]; let result: object; if (!page) { page = new Page(id, options, options.packageName, data); page.i18nService = I18n; page.dpiService = dpi; appCreate(page, options, data, services); - pageMap.set(id, page); + pageMap.push(page); result = initApp(page, code, data, services); } else { result = new Error(`invalid page id "${id}"`); @@ -65,11 +66,11 @@ export function destroyInstance(id: string): object { markupState(); } resetTarget(); - const page: Page = pageMap.get(id); + const page: Page = pageMap[id]; if (!page) { return new Error(`invalid page id '${id}'.`); } - pageMap.delete(id); + pageMap.remove(page); destroy(page); // Tell v8 to do a full Garbage Collection every eighteen times. diff --git a/runtime/main/manage/instance/misc.ts b/runtime/main/manage/instance/misc.ts index ee6742a0..92ad84fd 100644 --- a/runtime/main/manage/instance/misc.ts +++ b/runtime/main/manage/instance/misc.ts @@ -18,9 +18,10 @@ */ import { App } from '../../app/App'; +import { PageLinkedMap } from '../../app/map'; import Page from '../../page'; -const pageMap: Map = App.pageMap; +const pageMap: PageLinkedMap = App.pageMap; /** * Get an element tree of a page. @@ -28,7 +29,7 @@ const pageMap: Map = App.pageMap; * @return {Object} A virtual dom tree. */ export function getRoot(id: string): object { - const page: Page = pageMap.get(id); + const page: Page = pageMap[id]; let result: any; if (page) { const doc: any = page.doc || {}; diff --git a/runtime/main/page/entry/bundle.ts b/runtime/main/page/entry/bundle.ts index 4b79cfe2..3ce84570 100644 --- a/runtime/main/page/entry/bundle.ts +++ b/runtime/main/page/entry/bundle.ts @@ -31,7 +31,7 @@ import { registerCustomComponent, requireModule } from '../register'; -import { appMap } from '../../app/map'; +import { pageMap } from '../../app/map'; import Vm from '../../model/index'; import Page from '../index'; import {updateDpi, updateLocale} from '../../app'; @@ -61,7 +61,7 @@ export const defineFn = function(page: Page, name?: string, ...args: any[] | nul if (packageName === 'notset') { return page; } - const appPage: Page = appMap[packageName].getAppInstance(); + const appPage: Page = pageMap.getTop(packageName); return appPage || page; }; const moduleName: string = removePrefix(name); diff --git a/runtime/main/page/entry/init.ts b/runtime/main/page/entry/init.ts index 24244da5..0a0b4cea 100644 --- a/runtime/main/page/entry/init.ts +++ b/runtime/main/page/entry/init.ts @@ -81,7 +81,7 @@ export function init(page: Page, code: string | Function, data: object, services if (packageName === 'notset') { return page; } - const instance = App.pageMap.get(page.id); + const instance = App.pageMap.getTop(packageName); return instance || page; }; diff --git a/test/ut/app/index.ts b/test/ut/app/index.ts index f8ef77c3..b68635f1 100644 --- a/test/ut/app/index.ts +++ b/test/ut/app/index.ts @@ -53,12 +53,10 @@ describe('App Instance', () => { }); it('with some apis', () => { - expect(typeof app.getAppInstance).eql('function'); expect(typeof app.deleteGlobalKeys).eql('function'); }); it('run apis', () => { - expect(app.getAppInstance()).eql(appInstanceId); expect(app.deleteGlobalKeys()).to.be.undefined; }); }); diff --git a/test/ut/runtime.ts b/test/ut/runtime.ts index f43da5b0..a62a8fb5 100644 --- a/test/ut/runtime.ts +++ b/test/ut/runtime.ts @@ -36,6 +36,7 @@ import { allModules } from '../../runtime/main/page/register'; import { App } from '../../runtime/main/app/App'; +import { PageLinkedMap } from '../../runtime/main/app/map'; import Page from '../../runtime/main/page'; const expect = chai.expect; @@ -50,7 +51,7 @@ function clearRefs(json) { describe('framework entry', () => { fakeLog(); - const pageMap: Map = App.pageMap; + const pageMap: PageLinkedMap = App.pageMap; let instanceId; const options = { orientation: 'portrait', @@ -142,7 +143,7 @@ describe('framework entry', () => { } }; framework.createInstance(instanceId, code, options, null); - expect(pageMap.get(instanceId).customComponentMap).eql(expectComponent); + expect(pageMap[instanceId].customComponentMap).eql(expectComponent); }); describe('getRoot', () => {