Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/1.1.0 #27

Merged
merged 13 commits into from
Nov 13, 2019
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ice/stark",
"version": "1.0.0",
"version": "1.1.0",
"description": "Icestark is a JavaScript library for multiple projects, Ice workbench solution.",
"scripts": {
"build": "rm -rf lib && tsc",
Expand Down Expand Up @@ -46,6 +46,8 @@
"@commitlint/cli": "^7.5.2",
"@commitlint/config-conventional": "^7.5.0",
"@ice/spec": "^0.1.4",
"@testing-library/react": "^9.3.2",
"@testing-library/jest-dom": "^4.2.3",
"@types/jest": "^24.0.12",
"@types/node": "^12.0.0",
"@types/path-to-regexp": "^1.7.0",
Expand All @@ -54,13 +56,11 @@
"@types/url-parse": "^1.4.3",
"codecov": "^3.4.0",
"eslint": "^5.16.0",
"stylelint": "^10.1.0",
"husky": "^2.2.0",
"jest": "^24.7.1",
"jest-dom": "^3.4.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-testing-library": "^7.0.0",
"stylelint": "^10.1.0",
"ts-jest": "^24.0.2",
"typescript": "^3.4.4"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/icestark-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
"@commitlint/cli": "^7.5.2",
"@commitlint/config-conventional": "^7.5.0",
"@ice/spec": "^0.1.4",
"@testing-library/jest-dom": "^4.2.3",
"@types/jest": "^24.0.12",
"@types/node": "^12.0.0",
"codecov": "^3.4.0",
"eslint": "^5.16.0",
"stylelint": "^10.1.0",
"husky": "^2.2.0",
"jest": "^24.7.1",
"jest-dom": "^3.4.0",
"stylelint": "^10.1.0",
"ts-jest": "^24.0.2",
"typescript": "^3.4.4"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/icestark-app/tests/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'jest-dom/extend-expect';
import '@testing-library/jest-dom/extend-expect';

import {
getBasename,
Expand Down
57 changes: 30 additions & 27 deletions src/AppRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AppHistory } from './appHistory';
import { loadAssets, emptyAssets } from './handleAssets';
import { ICESTSRK_NOT_FOUND } from './constant';
import { setCache, getCache } from './cache';

const statusElementId = 'icestarkStatusContainer';
Expand All @@ -23,6 +23,25 @@ interface AppRouteState {
// "hashbang" - “ajax crawlable” (deprecated by Google) hashes like #!/ and #!/sunshine/lollipops
type hashType = 'hashbang' | 'noslash' | 'slash';

interface match<Params extends { [K in keyof Params]?: string } = {}> {
params: Params;
isExact: boolean;
path: string;
url: string;
}

interface Location<Query extends { [K in keyof Query]?: string } = {}> {
pathname: string;
query: Query;
hash: string;
}

export interface AppRouteComponentProps<Params extends { [K in keyof Params]?: string } = {}> {
match: match<Params>;
location: Location;
history: AppHistory;
}

export interface AppConfig {
title?: string;
hashType?: boolean | hashType;
Expand All @@ -35,11 +54,12 @@ export interface AppConfig {

export interface AppRouteProps extends AppConfig {
path: string | string[];
url: string | string[];
url?: string | string[];
useShadow?: boolean;
ErrorComponent?: any;
LoadingComponent?: any;
NotFoundComponent?: any;
component?: React.ReactElement;
render?: (props?: AppRouteComponentProps) => React.ReactElement;
forceRenderCount?: number;
onAppEnter?: (appConfig: AppConfig) => void;
onAppLeave?: (appConfig: AppConfig) => void;
Expand All @@ -60,8 +80,6 @@ function getAppConfig(appRouteProps: AppRouteProps): AppConfig {
'useShadow',
'ErrorComponent',
'LoadingComponent',
'NotFoundComponent',
'useShadow',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个为什么移除掉了?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useShadow 重复了,NotFoundComponent 的渲染逻辑放到 AppRouter 层了,原来都交由 AppRoute 自行处理,所以需要透传 NotFoundComponent 属性。 改之后渲染层级上更合理,后续 LoadingComponent、NotFoundComponent 逐步迁移过去(这块迁移成本稍微高一点,预计1.2.0版本完成)

'onAppEnter',
'onAppLeave',
];
Expand All @@ -84,8 +102,6 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta

private unmounted: boolean = false;

private triggerNotFound: boolean = false;

static defaultProps = {
useShadow: false,
exact: false,
Expand Down Expand Up @@ -129,13 +145,11 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta
*/
renderChild = (prevAppConfig?: AppConfig): void => {
const {
path,
url,
title,
rootId,
ErrorComponent,
LoadingComponent,
NotFoundComponent,
useShadow,
onAppEnter,
onAppLeave,
Expand All @@ -162,7 +176,7 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta
this.removeElementFromBase(rootId);
let rootElement: any = this.appendElementToBase(rootId);

// Prevent duplicate creation of shadowRoot
// prevent duplicate creation of shadowRoot
if (useShadow && !rootElement.shadowRoot) {
rootElement = rootElement.attachShadow
? rootElement.attachShadow({ mode: 'open', delegatesFocus: false })
Expand All @@ -171,25 +185,15 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta

setCache('root', rootElement);

// Empty useless assets before loading
// empty useless assets before loading
emptyAssets(useShadow);

// Handle NotFound
if (path === ICESTSRK_NOT_FOUND && url === ICESTSRK_NOT_FOUND) {
// loadAssets callback maybe slower than render NotFoundComponent
this.triggerNotFound = true;
this.renderStatusElement(NotFoundComponent);
return;
}

this.triggerNotFound = false;

if (title) document.title = title;

// Generate bundleList
// generate bundleList
const bundleList: string[] = Array.isArray(url) ? url : [url];

// Handle loading
// handle loading
this.setState({ cssLoading: true });
this.renderStatusElement(LoadingComponent);

Expand All @@ -202,13 +206,12 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta
if (err) {
// Handle error
this.renderStatusElement(ErrorComponent, { err });
this.removeElementFromBase(rootId);
this.setState({ cssLoading: false });
return true;
}

if (!this.triggerNotFound) {
// loadAssets callback maybe slower than render NotFoundComponent
this.removeElementFromBase(statusElementId);
}
this.removeElementFromBase(statusElementId);

return this.unmounted;
},
Expand Down
61 changes: 42 additions & 19 deletions src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import * as urlParse from 'url-parse';
import AppRoute, { AppConfig, AppRouteProps } from './AppRoute';
import { AppConfig, AppRouteProps, AppRouteComponentProps } from './AppRoute';
import appHistory from './appHistory';
import matchPath from './matchPath';
import { recordAssets } from './handleAssets';
import { ICESTSRK_NOT_FOUND } from './constant';
Expand Down Expand Up @@ -51,13 +52,25 @@ function getHashPath(hash: string = '/'): string {
return searchIndex === -1 ? hashPath : hashPath.substr(0, searchIndex);
}

/**
* Render Component, compatible with Component and <Component>
*/
function renderComponent(Component: any, props = {}): React.ReactElement {
return React.isValidElement(Component) ? (
React.cloneElement(Component, props)
) : (
<Component {...props} />
);
}

export default class AppRouter extends React.Component<AppRouterProps, AppRouterState> {
private originalPush: OriginalStateFunction = window.history.pushState;

private originalReplace: OriginalStateFunction = window.history.replaceState;

static defaultProps = {
ErrorComponent: <div>js bundle loaded error</div>,
onRouteChange: () => {},
ErrorComponent: ({ err }) => <div>{err}</div>,
NotFoundComponent: <div>NotFound</div>,
useShadow: false,
};
Expand All @@ -76,15 +89,21 @@ export default class AppRouter extends React.Component<AppRouterProps, AppRouter
this.handleRouteChange(location.href, 'init');

// render NotFoundComponent eventListener
window.addEventListener('icestark:not-found', () => {
this.setState({ url: ICESTSRK_NOT_FOUND });
});
window.addEventListener('icestark:not-found', this.triggerNotFound);
}

componentWillUnmount() {
this.unHijackHistory();
window.removeEventListener('icestark:not-found', this.triggerNotFound);
}

/**
* Trigger NotFound
*/
triggerNotFound = () => {
this.setState({ url: ICESTSRK_NOT_FOUND });
};

/**
* Hijack window.history
*/
Expand All @@ -103,7 +122,7 @@ export default class AppRouter extends React.Component<AppRouterProps, AppRouter
};

/**
* Unhijacking history
* Unhijack window.history
*/
unHijackHistory = (): void => {
window.history.pushState = this.originalPush;
Expand Down Expand Up @@ -157,7 +176,6 @@ export default class AppRouter extends React.Component<AppRouterProps, AppRouter
const { url, forceRenderCount } = this.state;

const { pathname, query, hash } = urlParse(url, true);
const { localUrl } = query;

let match: any = null;
let element: any;
Expand Down Expand Up @@ -187,25 +205,30 @@ export default class AppRouter extends React.Component<AppRouterProps, AppRouter
onAppEnter,
onAppLeave,
};
if (localUrl) {
extraProps.url = localUrl;
}

if (match) {
const { path, basename } = element.props as AppRouteProps;
const { path, basename, render, component } = element.props as AppRouteProps;

const commonProps: AppRouteComponentProps = {
location: { pathname, query, hash },
match,
history: appHistory,
};

if (component) {
return renderComponent(component, commonProps);
}

if (render && typeof render === 'function') {
return render(commonProps);
}

// render AppRoute
setCache('basename', basename || (Array.isArray(path) ? path[0] : path));

return React.cloneElement(element, extraProps);
}

return (
<AppRoute
path={ICESTSRK_NOT_FOUND}
url={ICESTSRK_NOT_FOUND}
NotFoundComponent={NotFoundComponent}
useShadow={useShadow}
/>
);
return renderComponent(NotFoundComponent, {});
}
}
15 changes: 10 additions & 5 deletions src/appHistory.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
const appHistory = {
push: (url: string) => {
export interface AppHistory {
push(path: string): void;
replace(path: string): void;
}

const appHistory: AppHistory = {
push: (path: string) => {
window.history.pushState(
{
forceRender: true,
},
null,
url,
path,
);
},
replace: (url: string) => {
replace: (path: string) => {
window.history.replaceState(
{
forceRender: true,
},
null,
url,
path,
);
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/handleAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function loadAsset(
element.addEventListener(
'error',
() => {
callback(isCss ? undefined : new Error(`JS asset loaded error: ${url}`));
callback(isCss ? undefined : `js asset loaded error: ${url}`);
},
false,
);
Expand Down
Loading