HarmonyOS鸿蒙Next中Init error. The WebviewController must be associated with a Web component
HarmonyOS鸿蒙Next中Init error. The WebviewController must be associated with a Web component
import webview from '@ohos.web.webview';
import { hilog } from '@kit.PerformanceAnalysisKit';
import type { TabRoute, QueryParam } from '../common/AppConfig';
import { AppConfig, buildWebUrl } from '../common/AppConfig';
import { STORAGE_KEYS } from '../common/AppState';
import type { UserSession } from '../models/UserSession';
import type { UserInfo } from '../models/UserSession';
import { NativeBridgeProxy } from '../common/NativeBridgeProxy';
const TAG = 'SmartStoreWebContainer';
interface WebUserPayload {
token: string;
userInfo: UserInfo | null;
nativeShell: boolean;
}
@Component
export struct WebContainer {
@Prop route: TabRoute = AppConfig.tabs[0];
@StorageLink(STORAGE_KEYS.session) private session: UserSession | null = null;
@State private isLoading: boolean = true;
@State private webSrc: string = 'about:blank';
@State private loadFailed: boolean = false;
@State private loadErrorText: string = '';
private controller: webview.WebviewController = new webview.WebviewController();
private bridgeRegistered: boolean = false;
private controllerAttached: boolean = false;
private buildWebSrc(): string {
this.loadFailed = false;
this.loadErrorText = '';
const query: QueryParam[] = [];
if (this.session?.token) {
query.push({ key: 'token', value: this.session.token });
}
const url = buildWebUrl(this.route.path, query);
hilog.info(0x0001, TAG, `buildWebSrc route=${this.route.id} url=${url}`);
return url;
}
private buildBridgeScript(payload: WebUserPayload): string {
const payloadStr = JSON.stringify(payload);
return `
(function() {
try {
const bridgePayload = ${payloadStr};
const bridge = {
navigateToNativePage: function(pageName, params) {
console.log('[NativeBridge] navigateToNativePage', pageName, params);
},
switchMenu: function(menuItem) {
console.log('[NativeBridge] switchMenu', menuItem);
},
nativeLogin: function() { console.log('[NativeBridge] nativeLogin'); },
logout: function() { console.log('[NativeBridge] logout'); },
getUserData: function() { return JSON.stringify(bridgePayload); },
setTabBarVisible: function(visible) { console.log('[NativeBridge] setTabBarVisible', visible); },
goBack: function() { console.log('[NativeBridge] goBack'); },
testConnection: function() { return 'NativeBridge(HarmonyOS) connected'; }
};
if (!window.NativeBridge) {
window.NativeBridge = bridge;
}
if (!window.AndroidBridge) {
window.AndroidBridge = bridge;
}
} catch (error) {
console.error('[NativeBridge] inject failed', error);
}
})();
`;
}
private injectSessionToWeb(): void {
if (!this.session?.token) {
hilog.warn(0x0001, TAG, '[SESSION_INJECT] skipped: missing token');
return;
}
const payload: WebUserPayload = {
token: this.session.token,
userInfo: this.session.userInfo ?? null,
nativeShell: true,
};
hilog.info(0x0001, TAG, `[SESSION_INJECT] payload: ${JSON.stringify(payload)}`); // 增加日志打印payload内容
const script = `
(function() {
try {
window.__NATIVE_USER__ = ${JSON.stringify(payload)};
window.__NATIVE_APP_DATA__ = ${JSON.stringify(payload)};
localStorage.setItem('token', ${JSON.stringify(payload.token)});
localStorage.setItem('auth_token', ${JSON.stringify(payload.token)});
sessionStorage.setItem('token', ${JSON.stringify(payload.token)});
sessionStorage.setItem('auth_token', ${JSON.stringify(payload.token)});
console.log('[SESSION_INJECT] token injected len=${payload.token.length}');
} catch (error) {
console.error('[SESSION_INJECT] failed', error);
}
})();
`;
hilog.info(0x0001, TAG, `[SESSION_INJECT] script: ${script}`); // 增加日志打印script内容
const bridgeScript = this.buildBridgeScript(payload);
hilog.info(0x0001, TAG, `[SESSION_INJECT] bridgeScript: ${bridgeScript}`); // 增加日志打印bridgeScript内容
const runnable = this.controller as JavaScriptRunnable;
if (!runnable.runJavaScript) {
hilog.error(0x0001, TAG, '[SESSION_INJECT] runJavaScript unavailable');
return;
}
if (!this.controllerAttached) {
hilog.warn(0x0001, TAG, '[SESSION_INJECT] controller not attached, retry in 80ms');
setTimeout(() => this.injectSessionToWeb(), 80);
return;
}
runnable.runJavaScript(bridgeScript).catch((error: Error) => {
hilog.error(0x0001, TAG, `[BRIDGE_INJECT] error: ${error}`);
});
runnable.runJavaScript(script).catch((error: Error) => {
hilog.error(0x0001, TAG, `[SESSION_INJECT] runJavaScript error: ${error}`);
});
}
aboutToAppear(): void {
hilog.info(0x0001, TAG, `[aboutToAppear] called.`);
this.isLoading = true;
this.controllerAttached = false;
this.bridgeRegistered = false;
this.webSrc = this.buildWebSrc();
webview.WebviewController.setWebDebuggingAccess(true);
}
aboutToRender(): void {
const nextSrc = this.buildWebSrc();
if (nextSrc !== this.webSrc) {
this.webSrc = nextSrc;
}
}
private ensureBridge(): void {
if (!this.controllerAttached) {
hilog.warn(0x0001, TAG, 'ensureBridge controller not attached, retry in 80ms');
setTimeout(() => this.ensureBridge(), 80);
return;
}
if (this.bridgeRegistered) {
return;
}
const registerProxy = this.controller.registerJavaScriptProxy;
if (!registerProxy) {
hilog.warn(0x0001, TAG, 'registerJavaScriptProxy unavailable');
return;
}
try {
const proxy = new NativeBridgeProxy();
const methods = [
'navigateToNativePage',
'switchMenu',
'nativeLogin',
'logout',
'getUserData',
'setTabBarVisible',
'goBack',
'testConnection',
];
registerProxy(proxy, 'NativeBridge', methods);
registerProxy(proxy, 'AndroidBridge', methods);
this.bridgeRegistered = true;
hilog.info(0x0001, TAG, 'NativeBridge registered');
} catch (error) {
hilog.error(0x0001, TAG, `bridge register error ${error}`);
}
}
private onControllerAttached(controller?: webview.WebviewController): void {
// 控制器已绑定 Web 组件,此时才可安全注册桥接;使用回调提供的 controller,避免引用不一致
if (controller) {
this.controller = controller;
}
this.controllerAttached = true;
this.bridgeRegistered = false;
hilog.info(0x0001, TAG, `[onControllerAttached] controller attached`);
// 延迟注册,确保底层完全就绪
setTimeout(() => this.ensureBridge(), 60);
}
build() {
Column() {
if (this.isLoading) {
Progress({ value: 0, type: ProgressType.Ring })
.width(36)
.height(36)
.margin({ bottom: 8 });
}
if (this.loadFailed) {
Column() {
Text('页面加载失败')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 });
if (this.loadErrorText) {
Text(this.loadErrorText)
.fontSize(12)
.fontColor('#666666')
.textAlign(TextAlign.Center);
}
Button('重试')
.type(ButtonType.Capsule)
.margin({ top: 12 })
.onClick(() => {
this.isLoading = true;
this.loadFailed = false;
this.webSrc = this.buildWebSrc();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
Web({ src: this.webSrc, controller: this.controller })
.javaScriptAccess(true)
.domStorageAccess(true)
.onPageBegin((event) => {
this.isLoading = true;
this.loadFailed = false;
// 每次新页面开始时重置桥接状态,等待 onControllerAttached 再注册
this.bridgeRegistered = false;
if (event) {
hilog.info(0x0001, TAG, `[PAGE_BEGIN] route=${this.route.id} url=${event.url}`);
}
})
.onPageEnd((event) => {
this.isLoading = false;
if (event) {
hilog.info(0x0001, TAG, `[PAGE_END] route=${this.route.id} url=${event.url}`);
}
// 先确保桥接,再注入会话
setTimeout(() => this.ensureBridge(), 50);
setTimeout(() => this.injectSessionToWeb(), 120);
})
.onControllerAttached(() => this.onControllerAttached())
.onErrorReceive((event) => {
if (event && event.error) {
hilog.error(0x0001, TAG, `[WEB_ERROR] ${event.error.getErrorCode()} ${event.error.getErrorInfo()}`);
this.loadFailed = true;
this.loadErrorText = `${event.error.getErrorCode()}: ${event.error.getErrorInfo()}`;
this.isLoading = false;
}
})
.onHttpErrorReceive((event) => {
if (event && event.response) {
hilog.error(0x0001, TAG, `[HTTP_ERROR] ${event.response.getResponseCode()}`);
hilog.error(0x0001, TAG, `[HTTP_ERROR_DETAIL] ${event.response.getResponseData()}`);
}
})
.width('100%')
.height('100%')
.onConsole((event) => {
// 处理普通日志
hilog.info(0x0001, TAG, `[JS Console] ${event.message.getMessage()}`);
return true;
})
}
.width('100%')
.height('100%')
}
}
interface JavaScriptRunnable {
runJavaScript?: (code: string) => Promise<string>;
}
interface JavaScriptProxyCapable {
registerJavaScriptProxy?: (
bridge: object,
namespace: string,
methodList: Array<string>,
asyncMethodList?: Array<string>,
permission?: string
) => void;
}
我已经反复试过很多次,onControllerAttached中覆盖controller也不行,不覆盖也不行,真不知道是什么问题了;
nativeBridge不成功,一直报这个,解决不了了: Init error. The WebviewController must be associated with a Web component
这是日志:

PAGE_END后调用也不行,反复试各种方式都不行。需要帮助
更多关于HarmonyOS鸿蒙Next中Init error. The WebviewController must be associated with a Web component的实战教程也可以访问 https://www.itying.com/category-93-b0.html
这边看了下提供的代码,不是很完整无法运行,直接把缺失的代码直接注释掉,加载了一个本地的网页是没有问题的,根据报错看就是WebviewController未和Web关联就直接使用了,这边无法运行原始代码不能够很好的排查,是否可以提供下可运行复现的demo呢。
import webview from '@ohos.web.webview';
import { hilog } from '@kit.PerformanceAnalysisKit';
// import type { TabRoute, QueryParam } from '../common/AppConfig';
// import { AppConfig, buildWebUrl } from '../common/AppConfig';
// import { STORAGE_KEYS } from '../common/AppState';
// import type { UserSession } from '../models/UserSession';
// import type { UserInfo } from '../models/UserSession';
// import { NativeBridgeProxy } from '../common/NativeBridgeProxy';
const TAG = 'SmartStoreWebContainer';
interface WebUserPayload {
token: string;
// userInfo: UserInfo | null;
nativeShell: boolean;
}
@Entry
@Component
export struct WebContainer {
// @Prop route: TabRoute = AppConfig.tabs[0];
// @StorageLink(STORAGE_KEYS.session) private session: UserSession | null = null;
@State private isLoading: boolean = true;
@State private webSrc: string = 'about:blank';
@State private loadFailed: boolean = false;
@State private loadErrorText: string = '';
private controller: webview.WebviewController = new webview.WebviewController();
private bridgeRegistered: boolean = false;
private controllerAttached: boolean = false;
private buildWebSrc(): string {
this.loadFailed = false;
this.loadErrorText = '';
// const query: QueryParam[] = [];
// if (this.session?.token) {
// query.push({ key: 'token', value: this.session.token });
// }
// const url = buildWebUrl(this.route.path, query);
// hilog.info(0x0001, TAG, `buildWebSrc route=${this.route.id} url=${url}`);
return '';
}
private buildBridgeScript(payload: WebUserPayload): string {
const payloadStr = JSON.stringify(payload);
return `
(function() {
try {
const bridgePayload = ${payloadStr};
const bridge = {
navigateToNativePage: function(pageName, params) {
console.log('[NativeBridge] navigateToNativePage', pageName, params);
},
switchMenu: function(menuItem) {
console.log('[NativeBridge] switchMenu', menuItem);
},
nativeLogin: function() { console.log('[NativeBridge] nativeLogin'); },
logout: function() { console.log('[NativeBridge] logout'); },
getUserData: function() { return JSON.stringify(bridgePayload); },
setTabBarVisible: function(visible) { console.log('[NativeBridge] setTabBarVisible', visible); },
goBack: function() { console.log('[NativeBridge] goBack'); },
testConnection: function() { return 'NativeBridge(HarmonyOS) connected'; }
};
if (!window.NativeBridge) {
window.NativeBridge = bridge;
}
if (!window.AndroidBridge) {
window.AndroidBridge = bridge;
}
} catch (error) {
console.error('[NativeBridge] inject failed', error);
}
})();
`;
}
private injectSessionToWeb(): void {
// if (!this.session?.token) {
// hilog.warn(0x0001, TAG, '[SESSION_INJECT] skipped: missing token');
// return;
// }
const payload: WebUserPayload = {
token: '',
// userInfo: this.session.userInfo ?? null,
nativeShell: true,
};
hilog.info(0x0001, TAG, `[SESSION_INJECT] payload: ${JSON.stringify(payload)}`); // 增加日志打印payload内容
const script = `
(function() {
try {
window.__NATIVE_USER__ = ${JSON.stringify(payload)};
window.__NATIVE_APP_DATA__ = ${JSON.stringify(payload)};
localStorage.setItem('token', ${JSON.stringify(payload.token)});
localStorage.setItem('auth_token', ${JSON.stringify(payload.token)});
sessionStorage.setItem('token', ${JSON.stringify(payload.token)});
sessionStorage.setItem('auth_token', ${JSON.stringify(payload.token)});
console.log('[SESSION_INJECT] token injected len=${payload.token.length}');
} catch (error) {
console.error('[SESSION_INJECT] failed', error);
}
})();
`;
hilog.info(0x0001, TAG, `[SESSION_INJECT] script: ${script}`); // 增加日志打印script内容
const bridgeScript = this.buildBridgeScript(payload);
hilog.info(0x0001, TAG, `[SESSION_INJECT] bridgeScript: ${bridgeScript}`); // 增加日志打印bridgeScript内容
const runnable = this.controller as JavaScriptRunnable;
if (!runnable.runJavaScript) {
hilog.error(0x0001, TAG, '[SESSION_INJECT] runJavaScript unavailable');
return;
}
if (!this.controllerAttached) {
hilog.warn(0x0001, TAG, '[SESSION_INJECT] controller not attached, retry in 80ms');
setTimeout(() => this.injectSessionToWeb(), 80);
return;
}
runnable.runJavaScript(bridgeScript).catch((error: Error) => {
hilog.error(0x0001, TAG, `[BRIDGE_INJECT] error: ${error}`);
});
runnable.runJavaScript(script).catch((error: Error) => {
hilog.error(0x0001, TAG, `[SESSION_INJECT] runJavaScript error: ${error}`);
});
}
aboutToAppear(): void {
hilog.info(0x0001, TAG, `[aboutToAppear] called.`);
this.isLoading = true;
this.controllerAttached = false;
this.bridgeRegistered = false;
this.webSrc = this.buildWebSrc();
webview.WebviewController.setWebDebuggingAccess(true);
}
aboutToRender(): void {
const nextSrc = this.buildWebSrc();
if (nextSrc !== this.webSrc) {
this.webSrc = nextSrc;
}
}
private ensureBridge(): void {
if (!this.controllerAttached) {
hilog.warn(0x0001, TAG, 'ensureBridge controller not attached, retry in 80ms');
setTimeout(() => this.ensureBridge(), 80);
return;
}
if (this.bridgeRegistered) {
return;
}
const registerProxy = this.controller.registerJavaScriptProxy;
if (!registerProxy) {
hilog.warn(0x0001, TAG, 'registerJavaScriptProxy unavailable');
return;
}
try {
// const proxy = new NativeBridgeProxy();
const methods = [
'navigateToNativePage',
'switchMenu',
'nativeLogin',
'logout',
'getUserData',
'setTabBarVisible',
'goBack',
'testConnection',
];
// registerProxy(proxy, 'NativeBridge', methods);
// registerProxy(proxy, 'AndroidBridge', methods);
this.bridgeRegistered = true;
hilog.info(0x0001, TAG, 'NativeBridge registered');
} catch (error) {
hilog.error(0x0001, TAG, `bridge register error ${error}`);
}
}
private onControllerAttached(controller?: webview.WebviewController): void {
// 控制器已绑定 Web 组件,此时才可安全注册桥接;使用回调提供的 controller,避免引用不一致
if (controller) {
this.controller = controller;
}
this.controllerAttached = true;
this.bridgeRegistered = false;
hilog.info(0x0001, TAG, `[onControllerAttached] controller attached`);
// 延迟注册,确保底层完全就绪
setTimeout(() => this.ensureBridge(), 60);
}
build() {
Column() {
if (this.isLoading) {
Progress({ value: 0, type: ProgressType.Ring })
.width(36)
.height(36)
.margin({ bottom: 8 });
}
if (this.loadFailed) {
Column() {
Text('页面加载失败')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 });
if (this.loadErrorText) {
Text(this.loadErrorText)
.fontSize(12)
.fontColor('#666666')
.textAlign(TextAlign.Center);
}
Button('重试')
.type(ButtonType.Capsule)
.margin({ top: 12 })
.onClick(() => {
this.isLoading = true;
this.loadFailed = false;
this.webSrc = this.buildWebSrc();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
Web({ src: this.webSrc, controller: this.controller })
.javaScriptAccess(true)
.domStorageAccess(true)
.onPageBegin((event) => {
this.isLoading = true;
this.loadFailed = false;
// 每次新页面开始时重置桥接状态,等待 onControllerAttached 再注册
this.bridgeRegistered = false;
if (event) {
// hilog.info(0x0001, TAG, `[PAGE_BEGIN] route=${this.route.id} url=${event.url}`);
}
})
.onPageEnd((event) => {
this.isLoading = false;
if (event) {
// hilog.info(0x0001, TAG, `[PAGE_END] route=${this.route.id} url=${event.url}`);
}
// 先确保桥接,再注入会话
setTimeout(() => this.ensureBridge(), 50);
setTimeout(() => this.injectSessionToWeb(), 120);
})
.onControllerAttached(() => this.onControllerAttached())
.onErrorReceive((event) => {
if (event && event.error) {
hilog.error(0x0001, TAG, `[WEB_ERROR] ${event.error.getErrorCode()} ${event.error.getErrorInfo()}`);
this.loadFailed = true;
this.loadErrorText = `${event.error.getErrorCode()}: ${event.error.getErrorInfo()}`;
this.isLoading = false;
}
})
.onHttpErrorReceive((event) => {
if (event && event.response) {
hilog.error(0x0001, TAG, `[HTTP_ERROR] ${event.response.getResponseCode()}`);
hilog.error(0x0001, TAG, `[HTTP_ERROR_DETAIL] ${event.response.getResponseData()}`);
}
})
.width('100%')
.height('100%')
.onConsole((event) => {
// 处理普通日志
hilog.info(0x0001, TAG, `[JS Console] ${event.message.getMessage()}`);
return true;
})
}
.width('100%')
.height('100%')
}
}
interface JavaScriptRunnable {
runJavaScript?: (code: string) => Promise<string>;
}
interface JavaScriptProxyCapable {
registerJavaScriptProxy?: (
bridge: object,
namespace: string,
methodList: Array<string>,
asyncMethodList?: Array<string>,
permission?: string
) => void;
}
更多关于HarmonyOS鸿蒙Next中Init error. The WebviewController must be associated with a Web component的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
已经解决了,registerJavaScriptProxy不能解构到局部使用。
这边修改了哪部分?解构到局部使用是指哪块?
try {
const proxy = new NativeBridgeProxy();
const methods = [
'navigateToNativePage',
'switchMenu',
'nativeLogin',
'logout',
'getUserData',
'setTabBarVisible',
'goBack',
'testConnection',
];
this.controller.registerJavaScriptProxy(proxy, 'NativeBridge', methods);
this.controller.registerJavaScriptProxy(proxy, 'AndroidBridge', methods);
this.bridgeRegistered = true;
hilog.info(0x0001, TAG, 'NativeBridge registered');
} catch (error) {
hilog.error(0x0001, TAG, `bridge register error ${error}`);
}
1、this.controller.registerJavaScriptProxy()没有返回值
2、registerJavaScriptProxy简述:
- 注入JavaScript对象到window对象中,并在window对象中调用该对象的同步方法。
const registerProxy = this.controller.registerJavaScriptProxy;
//没有返回值,这样操作是不行的
if (!registerProxy) {
}
问题解决了,registerJavaScriptProxy不能解构到局部使用。
我使用onControllerAttached返回的参数controller是空的
该错误(错误码17100001)表明 WebviewController 未与 Web 组件完成绑定时调用了其非静态方法(如 loadUrl() 或 runJavaScript())。以下是详细分析和解决方案:
🔍 错误原因
- 时序问题:在 WebviewController 与 Web 组件绑定前(即触发
onControllerAttached回调前),调用了控制器的非静态方法。 - 生命周期风险:在
onPageShow等可能早于绑定的生命周期中调用方法,导致时序无法保证。
【背景知识】 WebviewController可以控制Web组件各种行为。一个WebviewController对象只能控制一个Web组件,且必须在Web组件和WebviewController绑定后,才能调用WebviewController上的方法(静态方法除外)。绑定成功的标志是触发onControllerAttached,未绑定前调用接口会抛出js-error异常。
【问题定位】 17100001解释为WebviewController没有和具体的Web组件关联,可以通过onControllerAttached()接口进行检查。
【分析结论】 WebviewController在没有和具体的Web组件绑定的情况下调用了runJavaScript()、loadUrl()非静态方法,导致系统抛出错误码17100001。 【修改建议】
- 使用loadUrl()加载指定的Url,可以在onControllerAttached()回调里调用,因为WebviewController成功绑定到Web组件时触发onControllerAttached()回调。
- 使用runJavaScript()执行JavaScript脚本,为了避免页面生命周期onPageShow回调函数中无法确认WebviewController与Web组件绑定时序关系,需要在loadUrl完成后执行。建议在Web组件onPageEnd回调函数中(此时WebviewController已绑定)调用WebviewController.runJavaScript()。
能帮我看一下我是哪里出现的问题吗?
我已经在pageEnd里面再调用了,
在HarmonyOS Next中,该错误表示WebviewController初始化失败,未与Web组件正确绑定。必须确保在声明WebviewController时通过webview.WebviewController关联到对应Web组件,并在aboutToAppear生命周期前完成绑定。检查ArkTS代码中是否正确定义了Web组件ID,并通过$符号建立关联关系。
在 HarmonyOS Next 中,WebviewController 必须在与 Web 组件关联后才能调用其方法。从你的代码和日志来看,问题可能出现在 Web 组件初始化完成前就尝试使用控制器。以下是几个关键点:
-
控制器初始化时机:在
aboutToAppear中创建WebviewController实例是合理的,但确保在onControllerAttached回调触发前不调用任何控制器方法。 -
onControllerAttached回调的使用:你已正确使用此回调来标记控制器已关联。但注意,在onPageEnd中直接调用ensureBridge和injectSessionToWeb可能仍在控制器未完全就绪时执行。建议将所有依赖控制器的操作移至onControllerAttached内或通过状态机确保执行顺序。 -
延迟调用问题:使用
setTimeout延迟执行ensureBridge和injectSessionToWeb是一种规避方式,但若延迟时间不足或控制器未及时附加,仍会失败。可以尝试增加延迟或通过onControllerAttached内的标志同步状态。 -
代码检查:确认
Web组件的controller属性正确绑定到this.controller,且没有在构建过程中意外重置或覆盖。
建议简化流程:在 onControllerAttached 中设置标志后,统一在此回调中触发后续操作,避免分散在多个生命周期或事件中。如果问题持续,检查是否有其他代码干扰了控制器的关联状态。

