mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 03:31:45 +08:00
refactor(HMR): Refactor livesync propagaton
This commit is contained in:
@ -10,7 +10,6 @@ import {
|
|||||||
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
|
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
|
||||||
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
|
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
|
||||||
} from "./application-common";
|
} from "./application-common";
|
||||||
import { ModuleType } from "../ui/core/view/view-common";
|
|
||||||
|
|
||||||
// First reexport so that app module is initialized.
|
// First reexport so that app module is initialized.
|
||||||
export * from "./application-common";
|
export * from "./application-common";
|
||||||
@ -231,7 +230,7 @@ class IOSApplication implements IOSApplicationDefinition {
|
|||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): void {
|
public _onLivesync(context?: ModuleContext): void {
|
||||||
// Handle application root module
|
// Handle application root module
|
||||||
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== ModuleType.style;
|
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== "style";
|
||||||
|
|
||||||
// Set window content when:
|
// Set window content when:
|
||||||
// + Application root module is changed
|
// + Application root module is changed
|
||||||
|
6
tns-core-modules/module.d.ts
vendored
6
tns-core-modules/module.d.ts
vendored
@ -66,11 +66,7 @@ declare function clearTimeout(timeoutId: number): void;
|
|||||||
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number;
|
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number;
|
||||||
declare function clearInterval(intervalId: number): void;
|
declare function clearInterval(intervalId: number): void;
|
||||||
|
|
||||||
declare enum ModuleType {
|
declare type ModuleType = "markup" | "script" | "style";
|
||||||
markup = "markup",
|
|
||||||
script = "script",
|
|
||||||
style = "style"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a module context for Hot Module Replacement.
|
* Define a module context for Hot Module Replacement.
|
||||||
|
@ -37,18 +37,25 @@ function ensureAnimationModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ModuleType {
|
|
||||||
markup = "markup",
|
|
||||||
script = "script",
|
|
||||||
style = "style"
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CSSType(type: string): ClassDecorator {
|
export function CSSType(type: string): ClassDecorator {
|
||||||
return (cls) => {
|
return (cls) => {
|
||||||
cls.prototype.cssType = type;
|
cls.prototype.cssType = type;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function viewMatchesModuleContext(
|
||||||
|
view: ViewDefinition,
|
||||||
|
context: ModuleContext,
|
||||||
|
types: ModuleType[]): boolean {
|
||||||
|
|
||||||
|
return context &&
|
||||||
|
view._moduleName &&
|
||||||
|
context.type &&
|
||||||
|
types.some(type => type === context.type) &&
|
||||||
|
context.path &&
|
||||||
|
context.path.includes(view._moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator {
|
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator {
|
||||||
const stateEventNames = pseudoClasses.map(s => ":" + s);
|
const stateEventNames = pseudoClasses.map(s => ":" + s);
|
||||||
const listeners = Symbol("listeners");
|
const listeners = Symbol("listeners");
|
||||||
@ -147,62 +154,36 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
||||||
}
|
}
|
||||||
|
|
||||||
_rootModalViews.forEach(v => v.closeModal());
|
if (this._handleLivesync(context)) {
|
||||||
_rootModalViews.length = 0;
|
|
||||||
|
|
||||||
if (context && context.type && context.path) {
|
|
||||||
// Handle local styles
|
|
||||||
if (context.type === ModuleType.style) {
|
|
||||||
return this._changeLocalStyles(context.path);
|
|
||||||
}
|
|
||||||
// Handle module markup and script changes
|
|
||||||
else {
|
|
||||||
return this.changeModule(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public _changeLocalStyles(contextPath: string): boolean {
|
|
||||||
if (!this.changeStyles(this, contextPath)) {
|
|
||||||
eachDescendant(this, (child: ViewBase) => {
|
|
||||||
this.changeStyles(child, contextPath);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not reset activity/window content for local styles changes
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeStyles(view: ViewBase, contextPath: string): boolean {
|
|
||||||
if (traceEnabled()) {
|
|
||||||
traceWrite(`${view}.${view._moduleName}`, traceCategories.Livesync);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (view._moduleName && contextPath.includes(view._moduleName)) {
|
|
||||||
(<this>view).changeCssFile(contextPath);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeModule(context: ModuleContext): boolean {
|
let handled = false;
|
||||||
eachDescendant(this, (child: ViewBase) => {
|
this.eachChildView((child) => {
|
||||||
if (traceEnabled()) {
|
if (child._onLivesync(context)) {
|
||||||
traceWrite(`${child}.${child._moduleName}`, traceCategories.Livesync);
|
handled = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle changes in module's Page
|
|
||||||
if (child._moduleName && context.path.includes(child._moduleName) && child.page) {
|
|
||||||
child.page._onLivesync(context);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not reset activity/window content for module changes
|
public _handleLivesync(context?: ModuleContext): boolean {
|
||||||
return true;
|
if (traceEnabled()) {
|
||||||
|
traceWrite(`${this}._handleLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle local CSS
|
||||||
|
if (viewMatchesModuleContext(this, context, ["style"])) {
|
||||||
|
if (traceEnabled()) {
|
||||||
|
traceWrite(`Change Handled: Changing CSS for ${this}`, traceCategories.Livesync);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeCssFile(context.path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupAsRootView(context: any): void {
|
_setupAsRootView(context: any): void {
|
||||||
|
13
tns-core-modules/ui/core/view/view.d.ts
vendored
13
tns-core-modules/ui/core/view/view.d.ts
vendored
@ -32,6 +32,17 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator;
|
|||||||
*/
|
*/
|
||||||
export function CSSType(type: string): ClassDecorator;
|
export function CSSType(type: string): ClassDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param view The view
|
||||||
|
* @param context The ModuleType
|
||||||
|
* @param type Type of the ModuleType to be matched
|
||||||
|
*/
|
||||||
|
export function viewMatchesModuleContext(
|
||||||
|
view: View,
|
||||||
|
context: ModuleContext,
|
||||||
|
type: ModuleType[]): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denotes a length number that is in device independent pixel units.
|
* Denotes a length number that is in device independent pixel units.
|
||||||
*/
|
*/
|
||||||
@ -686,7 +697,7 @@ export abstract class View extends ViewBase {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_changeLocalStyles(contextPath: string): boolean;
|
_handleLivesync(context?: { type: string, path: string }): boolean;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -3,12 +3,13 @@ import { Frame as FrameDefinition, NavigationEntry, BackstackEntry, NavigationTr
|
|||||||
import { Page } from "../page";
|
import { Page } from "../page";
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import { getAncestor } from "../core/view/view-common";
|
import { getAncestor, viewMatchesModuleContext } from "../core/view/view-common";
|
||||||
import { View, CustomLayoutView, isIOS, isAndroid, traceEnabled, traceWrite, traceCategories, Property, CSSType } from "../core/view";
|
import { View, CustomLayoutView, isIOS, isAndroid, traceEnabled, traceWrite, traceCategories, Property, CSSType } from "../core/view";
|
||||||
import { createViewFromEntry } from "../builder";
|
import { createViewFromEntry } from "../builder";
|
||||||
import { profile } from "../../profiling";
|
import { profile } from "../../profiling";
|
||||||
|
|
||||||
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
||||||
|
import { getModuleName } from "../../utils/utils";
|
||||||
export * from "../core/view";
|
export * from "../core/view";
|
||||||
|
|
||||||
export enum NavigationType {
|
export enum NavigationType {
|
||||||
@ -574,7 +575,38 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(): boolean {
|
public _onLivesync(context?: ModuleContext): boolean {
|
||||||
|
if (super._onLivesync(context)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
if (!context) {
|
||||||
|
return this.legacyLivesync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _handleLivesync(context?: ModuleContext): boolean {
|
||||||
|
if (super._handleLivesync(context)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle markup/script changes in currentPage
|
||||||
|
if (this.currentPage &&
|
||||||
|
viewMatchesModuleContext(this.currentPage, context, ["markup", "script"])) {
|
||||||
|
|
||||||
|
traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync);
|
||||||
|
|
||||||
|
this.replacePage(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private legacyLivesync(): boolean {
|
||||||
// Reset activity/window content when:
|
// Reset activity/window content when:
|
||||||
// + Changes are not handled on View
|
// + Changes are not handled on View
|
||||||
// + There is no ModuleContext
|
// + There is no ModuleContext
|
||||||
@ -609,6 +641,27 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
this.navigate(newEntry);
|
this.navigate(newEntry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected replacePage(context: ModuleContext): void {
|
||||||
|
// Set NavigationType.replace for HMR.
|
||||||
|
// In IOS on `viewDidAppear()` this will be set to NavigationType.forward.
|
||||||
|
this.navigationType = NavigationType.replace;
|
||||||
|
const currentBackstackEntry = this._currentEntry;
|
||||||
|
const contextModuleName = getModuleName(context.path);
|
||||||
|
|
||||||
|
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
||||||
|
const newBackstackEntry: BackstackEntry = {
|
||||||
|
entry: currentBackstackEntry.entry,
|
||||||
|
resolvedPage: newPage,
|
||||||
|
navDepth: currentBackstackEntry.navDepth,
|
||||||
|
fragmentTag: currentBackstackEntry.fragmentTag,
|
||||||
|
frameId: currentBackstackEntry.frameId
|
||||||
|
};
|
||||||
|
|
||||||
|
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
|
||||||
|
this.performNavigation(navContext);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFrameById(id: string): FrameBase {
|
export function getFrameById(id: string): FrameBase {
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks,
|
AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks,
|
||||||
AndroidFragmentCallbacks, BackstackEntry, NavigationTransition
|
AndroidFragmentCallbacks, BackstackEntry, NavigationTransition
|
||||||
} from ".";
|
} from ".";
|
||||||
import { ModuleType } from "../../ui/core/view/view-common";
|
|
||||||
import { Page } from "../page";
|
import { Page } from "../page";
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
@ -90,7 +89,7 @@ export function reloadPage(context?: ModuleContext): void {
|
|||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
const rootView: View = callbacks.getRootView();
|
const rootView: View = callbacks.getRootView();
|
||||||
// Handle application root module
|
// Handle application root module
|
||||||
const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== ModuleType.style;
|
const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== "style";
|
||||||
|
|
||||||
// Reset activity content when:
|
// Reset activity content when:
|
||||||
// + Application root module is changed
|
// + Application root module is changed
|
||||||
@ -339,44 +338,6 @@ export class Frame extends FrameBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): boolean {
|
|
||||||
if (traceEnabled()) {
|
|
||||||
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._currentEntry || !this._currentEntry.entry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context && context.type && context.path) {
|
|
||||||
// Handle local styls changes when app root is Frame
|
|
||||||
if (context.type === ModuleType.style) {
|
|
||||||
return this._changeLocalStyles(context.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set NavigationType.replace for HMR.
|
|
||||||
this.navigationType = NavigationType.replace;
|
|
||||||
const currentBackstackEntry = this._currentEntry;
|
|
||||||
const contextModuleName = getModuleName(context.path);
|
|
||||||
|
|
||||||
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
|
||||||
const newBackstackEntry: BackstackEntry = {
|
|
||||||
entry: currentBackstackEntry.entry,
|
|
||||||
resolvedPage: newPage,
|
|
||||||
navDepth: currentBackstackEntry.navDepth,
|
|
||||||
fragmentTag: currentBackstackEntry.fragmentTag,
|
|
||||||
frameId: currentBackstackEntry.frameId
|
|
||||||
};
|
|
||||||
|
|
||||||
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
|
|
||||||
this.performNavigation(navContext);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// Fallback
|
|
||||||
return super._onLivesync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public _navigateCore(newEntry: BackstackEntry) {
|
public _navigateCore(newEntry: BackstackEntry) {
|
||||||
super._navigateCore(newEntry);
|
super._navigateCore(newEntry);
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition
|
iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition
|
||||||
} from ".";
|
} from ".";
|
||||||
import { Page } from "../page";
|
import { Page } from "../page";
|
||||||
import { ModuleType } from "../../ui/core/view/view-common";
|
|
||||||
import { profile } from "../../profiling";
|
import { profile } from "../../profiling";
|
||||||
|
|
||||||
//Types.
|
//Types.
|
||||||
@ -64,44 +63,6 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): boolean {
|
|
||||||
if (traceEnabled()) {
|
|
||||||
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._currentEntry || !this._currentEntry.entry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context && context.type && context.path) {
|
|
||||||
// Handle local styls changes when app root is Frame
|
|
||||||
if (context.type === ModuleType.style) {
|
|
||||||
return this._changeLocalStyles(context.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set NavigationType.replace for HMR.
|
|
||||||
// When `viewDidAppear()` set to NavigationType.forward.
|
|
||||||
this.navigationType = NavigationType.replace;
|
|
||||||
const currentBackstackEntry = this._currentEntry;
|
|
||||||
|
|
||||||
const contextModuleName = utils.getModuleName(context.path);
|
|
||||||
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
|
||||||
const newBackstackEntry: BackstackEntry = {
|
|
||||||
entry: currentBackstackEntry.entry,
|
|
||||||
resolvedPage: newPage,
|
|
||||||
navDepth: currentBackstackEntry.navDepth,
|
|
||||||
fragmentTag: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
|
|
||||||
this.performNavigation(navContext);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// Fallback
|
|
||||||
return super._onLivesync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public _navigateCore(backstackEntry: BackstackEntry) {
|
public _navigateCore(backstackEntry: BackstackEntry) {
|
||||||
// NavigationType.replace for HMR.
|
// NavigationType.replace for HMR.
|
||||||
|
@ -99,10 +99,6 @@ export class PageBase extends ContentView implements PageDefinition {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): boolean {
|
|
||||||
return this.frame ? this.frame._onLivesync(context) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
|
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
|
||||||
this._navigationContext = context;
|
this._navigationContext = context;
|
||||||
|
Reference in New Issue
Block a user