diff --git a/.changeset/chilled-radios-retire.md b/.changeset/chilled-radios-retire.md
new file mode 100644
index 000000000..2c4f91db6
--- /dev/null
+++ b/.changeset/chilled-radios-retire.md
@@ -0,0 +1,5 @@
+---
+"@qiankunjs/shared": patch
+---
+
+feat: add online solution links for qiankun
diff --git a/.dumi/pages/error/CodeSnippet.tsx b/.dumi/pages/error/CodeSnippet.tsx
new file mode 100644
index 000000000..15d3639e5
--- /dev/null
+++ b/.dumi/pages/error/CodeSnippet.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+
+export default function CodeSnippet(props) {
+ const lines = props.children.split('\n')
+ const firstTextualLine = lines.find(l => l.trim() !== l)
+ const numLeadingSpaces = firstTextualLine ? /([^\s])/.exec(firstTextualLine).index : 0
+ const formattedLines = lines.map(line => line.slice(numLeadingSpaces)).filter((line, i) => line.length > 0 || i > 0).join('\n')
+ return (
+
+
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/1.tsx b/.dumi/pages/error/codes/1.tsx
new file mode 100644
index 000000000..c2fc5ab05
--- /dev/null
+++ b/.dumi/pages/error/codes/1.tsx
@@ -0,0 +1,54 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #1: Application died in status NOT_MOUNTED: Target container with #container not existed after {props.getErrorCodeArg(0)} mounted!
+
+ This error thrown as the container DOM does not exist after the micro app is loaded. The possible reasons are:
+
+
+ 1.The root id of the micro app conflicts with other DOM, and the solution is to modify the search range of the root id.
+
+
+
vue micro app:
+
+ {`
+ function render(props = {}) {
+ const { container } = props;
+ instance = new Vue({
+ router,
+ store,
+ render: (h) => h(App),
+ }).$mount(container ? container.querySelector('#app') : '#app');
+ }
+ export async function mount(props) {
+ render(props);
+ }
+ `}
+
+
react micro app:
+
+ { `
+ function render(props) {
+ const { container } = props;
+ ReactDOM.render(, container ? container.querySelector('#root') : document.querySelector('#root'));
+ }
+ export async function mount(props) {
+ render(props);
+ }
+ export async function unmount(props) {
+ const { container } = props;
+ ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
+ }
+ `}
+
+
+ Explanation:
+
+ FAQ
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/2.tsx b/.dumi/pages/error/codes/2.tsx
new file mode 100644
index 000000000..8bc3bc787
--- /dev/null
+++ b/.dumi/pages/error/codes/2.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #2: You should not set multiply entry script in one entry html, but {props.getErrorCodeArg(0)} has at least 2 entry scripts
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/3.tsx b/.dumi/pages/error/codes/3.tsx
new file mode 100644
index 000000000..859b9b2ba
--- /dev/null
+++ b/.dumi/pages/error/codes/3.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #3: entry {props.getErrorCodeArg(0)} load failed as entry script {props.getErrorCodeArg(1)} load failed
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/4.tsx b/.dumi/pages/error/codes/4.tsx
new file mode 100644
index 000000000..524fbe3a2
--- /dev/null
+++ b/.dumi/pages/error/codes/4.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #4: entry {props.getErrorCodeArg(0)} response body is empty!
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/5.tsx b/.dumi/pages/error/codes/5.tsx
new file mode 100644
index 000000000..b394824eb
--- /dev/null
+++ b/.dumi/pages/error/codes/5.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #5: {props.getErrorCodeArg(0)} entry {props.getErrorCodeArg(1)} load failed as it not export lifecycles
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/6.tsx b/.dumi/pages/error/codes/6.tsx
new file mode 100644
index 000000000..da0e4f98d
--- /dev/null
+++ b/.dumi/pages/error/codes/6.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #6: You need to export lifecycle functions in {props.getErrorCodeArg(0)} entry
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/7.tsx b/.dumi/pages/error/codes/7.tsx
new file mode 100644
index 000000000..2b849ec56
--- /dev/null
+++ b/.dumi/pages/error/codes/7.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #7: {props.getErrorCodeArg(0)} head element not existed while accessing document.head!
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/codes/8.tsx b/.dumi/pages/error/codes/8.tsx
new file mode 100644
index 000000000..faa4f2ba2
--- /dev/null
+++ b/.dumi/pages/error/codes/8.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CodeSnippet from '../CodeSnippet'
+
+export default function ErrorCode1(props) {
+ return (
+ <>
+ #8: {props.getErrorCodeArg(0)} container {props.getErrorCodeArg(1)} element not ready while rebuilding!
+ >
+ )
+}
\ No newline at end of file
diff --git a/.dumi/pages/error/index.tsx b/.dumi/pages/error/index.tsx
new file mode 100644
index 000000000..2cb925ed4
--- /dev/null
+++ b/.dumi/pages/error/index.tsx
@@ -0,0 +1,29 @@
+/**
+ * sidebar: false
+ */
+
+import React from 'react'
+import { useLocation, useParams } from 'react-router-dom';
+import Error1 from './codes/1'
+import { useSearchParams } from 'dumi';
+const r = require.context('./codes/', false, /\.tsx$/);
+
+export default function CodeSnippet() {
+ let [searchParams, setSearchParams] = useSearchParams();
+ const code = searchParams.get('code');
+ const Comp = code && r(`./${code}.tsx`).default;
+ const errorCodeArgs = searchParams.getAll('arg');
+
+ return code ? (
+
+
+
+ ) : (Please enter the error code
)
+
+ function getErrorCodeArg(index, argName) {
+ const missingArg = argName ? `(${argName})` : `(unknown)`;
+ return errorCodeArgs.length > index ? errorCodeArgs[index] : missingArg;
+ }
+}
\ No newline at end of file
diff --git a/.dumirc.ts b/.dumirc.ts
index 73ac6f343..6d1a42f58 100644
--- a/.dumirc.ts
+++ b/.dumirc.ts
@@ -58,4 +58,5 @@ export default defineConfig({
theme: {
'@c-primary': '#6451AB',
},
+ styles: [`li a[href="/qiankun/error"],li a[href="/qiankun/error"] ~ * { display: none}]`],
});
diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts
index 6f09dad70..de0de1c60 100644
--- a/packages/loader/src/index.ts
+++ b/packages/loader/src/index.ts
@@ -121,7 +121,9 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L
if (isEntryScript(script)) {
if (foundEntryScript) {
throw new QiankunError(
+ 2,
`You should not set multiply entry script in one entry html, but ${entry} has at least 2 entry scripts`,
+ entry,
);
}
@@ -139,7 +141,12 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L
onEntryLoaded();
} else {
entryScriptLoadedDeferred.reject(
- new QiankunError(`entry ${entry} load failed as entry script ${script.src} load failed}`),
+ new QiankunError(
+ 3,
+ `entry ${entry} load failed as entry script ${script.src} load failed`,
+ entry,
+ script.src,
+ ),
);
}
}
@@ -171,5 +178,5 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L
return entryScriptLoadedDeferred.promise;
}
- throw new QiankunError(`entry ${entry} response body is empty!`);
+ throw new QiankunError(4, `entry ${entry} response body is empty!`, entry);
}
diff --git a/packages/qiankun/src/core/loadApp.ts b/packages/qiankun/src/core/loadApp.ts
index 85b528b2e..16c1d276e 100644
--- a/packages/qiankun/src/core/loadApp.ts
+++ b/packages/qiankun/src/core/loadApp.ts
@@ -10,7 +10,7 @@ import { moduleResolver as defaultModuleResolver, transpileAssets, wrapFetchWith
import { concat, isFunction, mergeWith } from 'lodash';
import type { ParcelConfigObject } from 'single-spa';
import getAddOns from '../addons';
-import { QiankunError } from '../error';
+import { QiankunError } from '@qiankunjs/shared';
import type { AppConfiguration, LifeCycleFn, LifeCycles, LoadableApp, MicroAppLifeCycles, ObjectType } from '../types';
import {
getPureHTMLStringWithoutScripts,
@@ -94,7 +94,8 @@ export default async function loadApp(
await execHooksChain(toArray(beforeLoad), app, global);
const lifecycles = await lifecyclesPromise;
- if (!lifecycles) throw new QiankunError(`${appName} entry ${entry} load failed as it not export lifecycles`);
+ if (!lifecycles)
+ throw new QiankunError(5, `${appName} entry ${entry} load failed as it not export lifecycles`, appName, entry);
const { bootstrap, mount, unmount, update } = getLifecyclesFromExports(
lifecycles,
appName,
@@ -235,7 +236,7 @@ function getLifecyclesFromExports(
return globalVariableExports;
}
- throw new QiankunError(`You need to export lifecycle functions in ${appName} entry`);
+ throw new QiankunError(6, `You need to export lifecycle functions in ${appName} entry`, appName);
}
function calcPublicPath(entry: string): string {
diff --git a/packages/qiankun/src/error.ts b/packages/qiankun/src/error.ts
index a05d3a612..9c4777461 100644
--- a/packages/qiankun/src/error.ts
+++ b/packages/qiankun/src/error.ts
@@ -1,5 +1,13 @@
export class QiankunError extends Error {
- constructor(message: string) {
- super(`[qiankun]: ${message}`);
+ constructor(code: number, message: string, ...args: string[]) {
+ let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`;
+ if (process.env.NODE_ENV === 'production') {
+ errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${
+ args.length ? `&arg=${args.join('&arg=')}` : ''
+ }`;
+ } else {
+ console.warn('args', ...args);
+ }
+ super(errorMessage);
}
}
diff --git a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts
index d03bf0933..1fffc516f 100644
--- a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts
+++ b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts
@@ -78,7 +78,11 @@ function patchDocument(sandbox: Sandbox, getContainer: () => HTMLElement): Calla
const container = getContainer();
const containerHeadElement = getContainerHeadElement(container);
if (!containerHeadElement) {
- throw new QiankunError(`${sandbox.name} head element not existed while accessing document.head!`);
+ throw new QiankunError(
+ 7,
+ `${sandbox.name} head element not existed while accessing document.head!`,
+ sandbox.name,
+ );
}
return containerHeadElement;
};
@@ -374,7 +378,10 @@ export function patchStandardSandbox(
const containerHeadElement = getContainerHeadElement(container);
if (!containerHeadElement) {
throw new QiankunError(
+ 8,
`${appName} container ${qiankunHeadTagName} element not ready while rebuilding!`,
+ appName,
+ qiankunHeadTagName,
);
}
return containerHeadElement;
diff --git a/packages/shared/src/error/QiankunError.ts b/packages/shared/src/error/QiankunError.ts
new file mode 100644
index 000000000..9c4777461
--- /dev/null
+++ b/packages/shared/src/error/QiankunError.ts
@@ -0,0 +1,13 @@
+export class QiankunError extends Error {
+ constructor(code: number, message: string, ...args: string[]) {
+ let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`;
+ if (process.env.NODE_ENV === 'production') {
+ errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${
+ args.length ? `&arg=${args.join('&arg=')}` : ''
+ }`;
+ } else {
+ console.warn('args', ...args);
+ }
+ super(errorMessage);
+ }
+}
diff --git a/packages/shared/src/reporter/QiankunError.ts b/packages/shared/src/reporter/QiankunError.ts
index a05d3a612..9c4777461 100644
--- a/packages/shared/src/reporter/QiankunError.ts
+++ b/packages/shared/src/reporter/QiankunError.ts
@@ -1,5 +1,13 @@
export class QiankunError extends Error {
- constructor(message: string) {
- super(`[qiankun]: ${message}`);
+ constructor(code: number, message: string, ...args: string[]) {
+ let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`;
+ if (process.env.NODE_ENV === 'production') {
+ errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${
+ args.length ? `&arg=${args.join('&arg=')}` : ''
+ }`;
+ } else {
+ console.warn('args', ...args);
+ }
+ super(errorMessage);
}
}