1# Web开发常见问题 2 3 4## H5页面如何与ArkTS交互(API 10) 5 6**问题现象** 7 8目前javaScriptProxy仅支持同步调用,异步调用无法获取执行结果。 9 10**解决措施** 11 12对javaScriptProxy和runJavaScript封装,实现JSBridge通信方案。适用于H5调用原生侧函数。使用Web组件javaScriptProxy将原生侧接口注入到H5的window对象上,通过runJavaScript接口执行JS脚本到H5中,并在回调中获取脚本执行结果。具体调用流程如下图所示: 13 14 15 16- 首先通过Web组件的javaScriptProxy属性,将JSBridgeHandle对象注册到H5的window上,作为H5调用原生的通道。当H5开始加载时,在onPageBegin生命周期中调用initJSBridge()方法初始化JSBridge。 17 ``` 18 // javaScriptProxy对象 19 public get javaScriptProxy() { 20 return { 21 object: { 22 call: this.call 23 }, 24 name: "JSBridgeHandle", 25 methodList: ['call'], 26 controller: this.controller, 27 } 28 } 29 ``` 30 31 ``` 32 // 使用Web组件加载H5页面 33 @Component 34 struct JsProxy { 35 private controller: WebviewController = new WebView.WebviewController() 36 private jsBridge: JSBridge = new JSBridge(this.controller) 37 build() { 38 Column(){ 39 Web({ src: $rawfile('index.html'), controller: this.controller }) 40 .javaScriptProxy(this.jsBridge.javaScriptProxy) 41 .onPageBegin(() => { 42 this.jsBridge.initJSBridge() 43 }) 44 } 45 } 46 } 47 ``` 48 49- 在initJSBridge方法中,通过webviewControll.runJavaScript()将JSBridge初始化脚本注入H5执行。当H5调用时,生成window.callID标识回调函数,将callID与调用参数使用JSBridgeHandle.call传到原生侧。通过JSBridgeCallback接收原生侧执行的结果,根据callID找到对应callback执行并且释放内存。 50 ``` 51 // bridgeKey与bridgeMethod动态生成H5侧调用的入口 52 bridgeKey: string = 'JSBridge' 53 bridgeMethod: string = 'call' 54 // 初始化脚本注入H5侧 55 public initJSBridge() { 56 try { 57 this.controller.runJavaScript(` 58 // 接收原生侧结果,执行callback 59 function JSBridgeCallback(id, params){ 60 window.JSBridgeMap[id](params) 61 }; 62 // 声明调用入口 63 window.${this.bridgeKey} = { 64 ${this.bridgeMethod}(method, params, callback){ 65 window.JSBridgeMap[id] = callback || (() => {}); 66 JSBridgeHandle.call(method, JSON.stringify(paramsObj)); 67 }, 68 }`) 69 } 70 } 71 ``` 72 73- JSBridgeHandle.call()是H5调用原生接口的统一入口,在该方法中根据H5调用的方法名,匹配到对应接口去调用。调用结束后通过this.callback()方法将调用结果返回H5。callback方法中使用webviewControll.runJavaScript()调用H5的JSBridgeCallback回传callID和调用结果。 74 ``` 75 // call方法调用原生侧方法,接收结果 76 private call = (fun, params) => { 77 try { 78 const paramsObj = JSON.parse(params) 79 const events = this.exposeManage.methodMap.get(fun) 80 const results = [] 81 events.forEach(callFun => { 82 results.push(callFun(paramsObj.data)) 83 }) 84 Promise.all(results.filter(i => !!i)).then(res => { 85 // 依赖于results中包含非undefined或null的元素。 86 this.callback(paramsObj.callID, res.length > 1 ? res : res[0]) 87 }) 88 } 89 } 90 91 // 通过runJavaScript调用JSBridgeCallback执行回调 92 private callback(id, data) { 93 this.controller.runJavaScript(`__JSBridgeCallback__("${id}", ${JSON.stringify(data)})`); 94 } 95 ``` 96 97 98## Web组件的onUrlLoadIntercept返回结果是否影响onInterceptRequest(API 9) 99 100**解决措施** 101 102Web组件的onUrlLoadIntercept的不同返回结果对应不同的操作: 103 104- onUrlLoadIntercept返回true则直接拦截URL请求。 105 106- onUrlLoadIntercept返回false走onInterceptRequest回调。 107 108**参考链接** 109 110[onUrlloadIntercept](../reference/apis-arkweb/arkts-basic-components-web-events.md#onurlloadinterceptdeprecated) 111 112 113## 为什么Web组件的onKeyEvent键盘事件不生效(API 9) 114 115**问题现象** 116 117Web组件设置onKeyEvent监听键盘事件,键盘按下或抬起该事件不触发。 118 119**解决措施** 120 121onKeyEvent为通用事件,当前Web组件不支持通用事件。Web组件监听键盘事件可以使用onInterceptKeyEvent回调函数。 122 123**参考链接** 124 125[onInterceptKeyEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#oninterceptkeyevent9) 126 127 128## onInterceptRequest拦截URL并自定义HTML文件,页面加载失败(API 9) 129 130**问题现象** 131 132onInterceptRequest拦截页面Web的src的链接后返回自定义HTML,但是自定义HTML文件里面的script标签里的内容没有加载。 133 134**解决措施** 135 136设置拦截器时,如果只设置setResponseData,内核将无法识别到这是个HTML文件,需要同时设置setResponseEncoding、setResponseMimeType、setResponseHeader等参数。 137 138**代码示例** 139 140``` 141Web({ src: 'www.example.com', controller: this.controller }) 142 .onInterceptRequest((event) => { 143 console.log('url:' + event.request.getRequestUrl()) 144 this.responseweb = new WebResourceResponse(); 145 var head1:Header = { 146 headerKey:"Connection", 147 headerValue:"keep-alive" 148 } 149 var length = this.heads.push(head1) 150 this.responseweb.setResponseHeader(this.heads) 151 this.responseweb.setResponseData(this.webdata) 152 this.responseweb.setResponseEncoding('utf-8') 153 this.responseweb.setResponseMimeType('text/html') 154 this.responseweb.setResponseCode(200) 155 this.responseweb.setReasonMessage('OK') 156 return this.responseweb 157}) 158``` 159 160**参考链接** 161 162[WebResourceResponse](../reference/apis-arkweb/arkts-basic-components-web-WebResourceResponse.md) 163 164 165## 如何在ArkTS代码中执行HTML内的JS函数(API 9) 166 167**解决措施** 168 169通过WebviewController中runJavaScript方法异步执行JavaScript脚本,并通过回调方式获取执行结果。 170 171> **说明:** 172> runJavaScript需要在loadUrl完成后,比如onPageEnd中调用。 173 174**参考链接** 175 176[runJavaScript](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#runjavascript) 177 178 179## 使用Web组件加载本地网页时,如何在本地网页中调用ArkTS中的函数(API 9) 180 181**解决措施** 182 1831. 准备一个html文件,例如: 184 185 ``` 186 <!DOCTYPE html> 187 <html lang="en"> 188 <head> 189 <meta charset="UTF-8"> 190 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 191 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 192 <title>Document</title> 193 </head> 194 <body> 195 <h1>标题</h1> 196 <h5 id="h5"></h5> 197 <h5 id="h6"></h5> 198 <button onclick="handleFromH5">调用Arkts的方法</button> 199 <script type="text/javascript"> 200 function handleFromH5(){ 201 let result = window.objName.test(); 202 document.getElementById('h6').innerHTML = result; 203 } 204 </script> 205 </body> 206 </html> 207 ``` 208 2092. 在ArkTs中使用JavaScriptProxy方法将ArkTs里的对象注册到H5的window对象中,然后在h5中使用window对象调用该方法。比如下面例子,在ArkTs中将testObj这个对象以别名objName注册到h5的window对象上,在上面的h5中就可以使用window.objName去访问这个对象。 210 211 ``` 212 // xxx.ets 213 import web_webview from '@ohos.web.webview' 214 @Entry 215 @Component 216 struct Index { 217 @State message: string = 'Hello World' 218 controller: web_webview.WebviewController = new web_webview.WebviewController() 219 testObj = { 220 test: (data1, data2, data3) => { 221 console.log("data1:" + data1); 222 console.log("data2:" + data2); 223 console.log("data3:" + data3); 224 return "AceString"; 225 }, 226 toString: () => { 227 console.log('toString' + "interface instead."); 228 } 229 } 230 build() { 231 Row() { 232 Column() { 233 Web({ src:$rawfile('index.html'), controller:this.controller }) 234 .javaScriptAccess(true) 235 .javaScriptProxy({ 236 object: this.testObj, 237 name: "objName", 238 methodList: ["test", "toString"], 239 controller: this.controller, 240 }) 241 } 242 .width('100%') 243 } 244 .height('100%') 245 } 246 } 247 ``` 248 249**参考链接** 250 251[javaScriptProxy](../reference/apis-arkweb/arkts-basic-components-web-i.md#javascriptproxy12) 252 253 254## Web组件domStorageAccess属性设置(API 9) 255 256**解决措施** 257 258设置是否开启文档对象模型存储接口(DOM Storage API)权限,默认未开启,控制web网页中localStorage的使用,对sessionStorage未做控制 259 260**参考链接** 261 262[domStorageAccess](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#domstorageaccess) 263 264 265## 如何解决Web组件加载的HTML页面内检测网络状态失败(API 9) 266 267**问题现象** 268 269在HTML页面内通过window.navigator.onLine获取网络状态,联网和断网情况下均为false。 270 271**解决措施** 272 273配置应用获取网络信息权限: ohos.permission.GET_NETWORK_INFO 274 275**参考链接** 276 277[GET\_NETWORK\_INFO](../security/AccessToken/permissions-for-all.md#ohospermissionget_network_info) 278 279 280## 如何自定义拼接设置User-Agent参数(API 9) 281 282**解决措施** 283 284默认User-Agent需要通过WebviewController获取。WebviewController对象必须在Web组件绑定后,才能调用WebviewController上的方法getUserAgent获取默认User-Agent。因此在页面加载前通过自定义字符串拼接修改User-Agent,可采用此方式: 285 2861. 使用\@State定义初始User-Agent,绑定到Web组件; 287 2882. 在Web组件的onUrlLoadIntercept回调中,通过WebviewController.getUserAgent()获取默认User-Agent,并修改Web组件绑定的User-Agent 289 290**代码示例** 291 292``` 293import web_webview from '@ohos.web.webview' 294@Entry 295@Component 296struct Index { 297 private controller: web_webview.WebviewController = new web_webview.WebviewController() 298 @State userAgentPa: string = '' 299 build() { 300 Row() { 301 Column() { 302 Web({ src: 'http://www.example.com', controller: this.controller }) //需要手动替换为真实网站 303 .width('100%') 304 .userAgent(this.userAgentPa) 305 .onUrlLoadIntercept((event) => { 306 let userAgent = this.controller.getUserAgent(); 307 this.userAgentPa = userAgent + ' 111111111' 308 return false; 309 }) 310 } 311 .width('100%') 312 } 313 .height('100%') 314 } 315} 316``` 317 318**参考链接** 319 320[userAgent](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#useragentdeprecated)、[getUserAgent](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#getuseragent) 321## WebView支持同层渲染吗(API 10) 322 323**解决措施** 324 3251. 支持Video、Map、Camera、Canvas、WebGL、WebView组件同层渲染。 3262. 支持将Web embed标签的id、type、src、width、height、url属性传递给原生组件。 327 328 329## WebView有哪些调试工具,调试工具的用法是什么(API 10) 330 331**解决措施** 332 333setWebDebuggingAccess()接口开启Web组件前端页面调试能力,利用DevTools工具可以在2in1上调试移动设备上的前端网页,设备需为4.1.0及以上版本。 334 335**参考链接** 336 337[使用Devtools工具调试前端页面(开发指南)](../web/web-debugging-with-devtools.md) 338 339 340## WebView如何实现网络请求拦截功能(API 10) 341 342**解决措施** 343 344可以通过onInterceptRequest()接口实现自定义资源请求响应,该能力可用于自定义Web页面响应、自定义文件资源响应等场景。当Web网页发起资源加载请求时,应用层会收到该请求消息并构造本地资源响应消息发送给Web内核,Web内核根据应用层响应信息进行页面资源加载。 345 346**参考链接** 347 348[自定义页面请求响应(开发指南)](../web/web-resource-interception-request-mgmt.md) 349 350 351## WebView和原生进行通信的方式有哪些,如何实现(API 10) 352 353**解决措施** 354 3551. Native->H5使用runJavaScript接口注入JS进行通信,H5->Native使用registerJavaScriptProy接口。先将Native方法注册至H5侧,H5再通过调用前端方法实现与Native侧的通信。 3562. runJavaScript、registerJavaScriptProy接口同时在NDK侧C API暴露。 3573. 使用onInterceptrequest接口拦截H5侧请求,同时将Native侧数据作为Response返回至H5,实现Native与H5的通信。 358 359**参考链接** 360 361[runJavaScript](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#runjavascriptext10)、[registerJavaScriptProxy](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#registerjavascriptproxy)、[javaScriptProxy](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#javascriptproxy)、[onInterceptRequest](../reference/apis-arkweb/arkts-basic-components-web-events.md#oninterceptrequest9) 362 363 364## WebView进程模型和渲染机制是什么(API 11) 365 366**解决措施** 367 3681. 进程模型:1个主进程、多个render进程。 3692. 渲染机制:web自渲染。 370 371 372## 系统目前是否支持Webrtc的功能?规格是什么? 373 374**解决措施** 375 3761. WebView支持Webrtc的P-P功能以及音视频流功能。 3772. 非WebView场景,系统不直接提供Webrtc,但会提供技术支持,比如支持三方gn+ninja交叉编译方式适配Webrtc(RR-30030985),包含以下两点: 378 * sdk支持gn+ninja交叉编译方式 379 * 提供编译样例指导 380 381 382## Webview如何设置mixcontent策略,用以解决http与https混合加载的问题? 383 384**解决措施** 385 386Webview提供mixedMode(mixedMode: MixedMode)接口,设置是否允许加载超文本传输协议(HTTP)和超文本传输安全协议(HTTPS)混合内容,默认不允许加载HTTP和HTTPS混合内容。 387 388**参考链接** 389 390[mixedmode](../reference/apis-arkweb/arkts-basic-components-web-e.md#mixedmode) 391 392 393## WebView除了设置缓存,还有什么方式可以提升渲染速度吗? 394 395**解决措施** 396 397使用prepareForPageLoad接口开启预解析。 398 399**参考链接** 400 401[prepareforpageload](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prepareforpageload10) 402 403 404## 如何预创建Web组件?如何回收web组件复用? 405 406**解决措施** 407 408通过ArkUI提供的组件动态上下树能力,实现Web组件预创建、回收复用,使用指南见参考链接。 409 410**参考链接** 411 412[动态创建Web组件](../web/web-page-loading-with-web-components.md) 413 414 415## 目前OpenHarmony是否有提供类似其他系统的JavaScript引擎能力? 416 417**解决措施** 418 419目前已支持,详情请见参考链接。 420 421**参考链接** 422 423[JSVM](../reference/common/capi-jsvm.md)