首页 > 其他分享 >【最新鸿蒙应用开发】——ArkWeb2——arkts与h5交互

【最新鸿蒙应用开发】——ArkWeb2——arkts与h5交互

时间:2024-06-13 15:01:08浏览次数:9  
标签:web arkts console business webviewController ArkWeb2 h5 error webview

H5与端侧交互

1. 应用侧调用前端页面函数

应用侧可以通过runJavaScript()方法调用前端页面的JavaScript相关函数。 在下面的示例中,点击应用侧的“runJavaScript”按钮时,来触发前端页面的htmlTest()方法。

  • 前端页面代码。

<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<script>
    function htmlTest() {
        console.info('JavaScript Hello World! ');
    }
</script>
</body>
</html>
  • 应用侧代码。

// xxx.ets
import web_webview from '@ohos.web.webview';
​
@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
​
  build() {
    Column() {
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
      Button('runJavaScript')
        .onClick(() => {
           this.webviewController.runJavaScript('htmlTest()');
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
    }
  }
}

场景

xx.ets:

      Web({
        src: $rawfile('word.html'),
        controller: this.webviewCtrl
      })
        .onPageEnd(() => {
          //   当word.html网页加载完成之后,要执行这个网页中定义的js方法 writeCode
          this.webviewCtrl.runJavaScript(`writeCode(\`${this.currentCode}\`)`)
        })

引入第三方函数实现高亮效果

前端侧:

<!doctype html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Word</title>
    <link rel="stylesheet"
          href="./github.min.css">
    <script src="./highlight.min.js"></script>
    <style>
        code {
          font-size: 16px;
          line-height: 1.6
        }


    </style>
</head>
<body>
<pre><code></code></pre>
<script>
function writeCode(str) {
  const el = document.querySelector('code');
  el.innerHTML = hljs.highlightAuto(str).value;
}

</script>
</body>
</html>

效果:

2. 前端页面调用应用侧函数

开发者使用Web组件将应用侧代码注册到前端页面中,注册完成之后,前端页面中使用注册的对象名称就可以调用应用侧的函数,实现在前端页面中调用应用侧方法。

注册应用侧代码有两种方式,一种在Web组件初始化调用,使用javaScriptProxy()接口。另外一种在Web组件初始化完成后调用,使用registerJavaScriptProxy()接口。

在下面的示例中,将test()方法注册在前端页面中, 该函数可以在前端页面触发运行。

  1. javaScriptProxy()接口使用示例如下。

// xxx.ets
import web_webview from '@ohos.web.webview';
​
class testClass {
  constructor() {
  }
​
  test(): string {
    return 'ArkTS Hello World!';
  }
}
​
@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  // 声明需要注册的对象
  @State testObj: testClass = new testClass();
​
  build() {
    Column() {
      // web组件加载本地index.html页面
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
        // 将对象注入到web端
        .javaScriptProxy({
          object: this.testObj,
          name: "testObjName",
          methodList: ["test"],
          controller: this.webviewController
        })
    }
  }
}
  1. 应用侧使用registerJavaScriptProxy()接口注册。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';
​
class testClass {
  constructor() {
  }
​
  test(): string {
    return "ArkUI Web Component";
  }
​
  toString(): void {
    console.log('Web Component toString');
  }
}
​
@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();
​
  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}

说明: 使用registerJavaScriptProxy()接口注册方法时,注册后需调用refresh()接口生效。

  • index.html前端页面触发应用侧代码。

<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
        let str = testObjName.test();
        document.getElementById("demo").innerHTML = str;
        console.info('ArkTS Hello World! :' + str);
    }
</script>
</body>
</html>
复杂类型使用方法
  • 应用侧和前端页面之间传递Array。

import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';
​
class testClass {
  constructor() {
  }
​
  test(): Array<Number>{
    return [1, 2, 3, 4]
  }
​
  toString(param:String): void {
    console.log('Web Component toString' + param);
  }
}
​
@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();
​
  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}

<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
      testObjName.test().then((param)=>{testObjName.toString(param)}).catch((param)=>{testObjName.toString(param)})
    }
</script>
</body>
</html>
  • 应用侧和前端页面之间传递不带Function的Dictionary。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';
​
class student {
  name: string = ''
  age: string = ''
}
​
class testClass {
  constructor() {
  }
​
  test(): student {
    let st: student = {name:"jeck", age:"12"}
    return st
  }
​
  toString(param: ESObject): void {
    console.log('Web Component toString' + param["name"]);
  }
}
​
@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();
​
  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
        testObjName.toString(testObjName.test());
    }
</script>
</body>
</html>
  • 应用侧调用前端页面的Callback。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

class testClass {
  constructor() {
  }

  test(param: Function): void {
    param("call callback");
  }

  toString(param:String): void {
    console.log('Web Component toString' + param);
  }
}

@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
        testObjName.test(function(param){testObjName.toString(param)});
    }
</script>
</body>
</html>
  • 应用侧调用前端页面Object里的Function。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

class testClass {
  constructor() {
  }

  test(param: ESObject): void {
    param.hello("call obj func");
  }

  toString(param:String): void {
    console.log('Web Component toString' + param);
  }
}

@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    // 写法1
    class Student {
        constructor(nameList) {
            this.methodNameListForJsProxy = nameList;
        }

        hello(param) {
            testObjName.toString(param)
        }
    }
    var st = new Student(["hello"])

    // 写法2
    //创建一个构造器,构造函数首字母大写
    function Obj1(){
        this.methodNameListForJsProxy=["hello"];
        this.hello=function(param){
            testObjName.toString(param)
        };
    }
    //利用构造器,通过new关键字生成对象
    var st1 = new Obj1();

    function callArkTS() {
        testObjName.test(st);
        testObjName.test(st1);
    }
</script>
</body>
</html>
  • 前端页面调用应用侧Object里的Function。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

class ObjOther {
    methodNameListForJsProxy: string[]

    constructor(list: string[]) {
        this.methodNameListForJsProxy = list
    }

    testOther(json:string): void {
        console.info(json)
    }
}

class testClass {
  ObjReturn:ObjOther
  constructor() {
    this.ObjReturn =  new ObjOther(["testOther"]);
  }

  test(): ESObject {
    return this.ObjReturn
  }

  toString(param: string): void {
    console.log('Web Component toString' + param);
  }
}

@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
      testObjName.test().testOther("call other object func");
    }
</script>
</body>
</html>

3. Promise场景

  • 第一种使用方法,在应用侧new Promise。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

class testClass {
  constructor() {
  }

  test(): Promise<string> {
      let p: Promise<string> = new Promise((resolve, reject) => {  setTimeout(() => {console.log('执行完成'); reject('fail');}, 10000);});
      return p;
  }

  toString(param:String): void {
      console.log(" " + param)
  }
}

@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
      testObjName.test().then((param)=>{testObjName.toString(param)}).catch((param)=>{testObjName.toString(param)})
    }
</script>
</body>
</html>

第二种使用方法,在前端页面new Promise。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

class testClass {
  constructor() {
  }

  test(param:Function): void {
      setTimeout( () => { param("suc") }, 10000)
  }

  toString(param:String): void {
      console.log(" " + param)
  }
}

@Entry
@Component
struct Index {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      Button('refresh')
        .onClick(() => {
          try {
            this.webviewController.refresh();
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
      let funpromise
      var p = new Promise(function(resolve, reject){funpromise=(param)=>{resolve(param)}})
      testObjName.test(funpromise)
      p.then((param)=>{testObjName.toString(param)})
    }
</script>
</body>
</html>

4. 建立应用侧与前端页面数据通道

前端页面和应用侧之间可以用createWebMessagePorts()接口创建消息端口来实现两端的通信

在下面的示例中,应用侧页面中通过createWebMessagePorts方法创建消息端口,再把其中一个端口通过postMessage()接口发送到前端页面,便可以在前端页面和应用侧之间互相发送消息。

  • 应用侧代码。

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

@Entry
@Component
struct WebComponent {
  controller: web_webview.WebviewController = new web_webview.WebviewController();
  ports: web_webview.WebMessagePort[] = [];
  @State sendFromEts: string = 'Send this message from ets to HTML';
  @State receivedFromHtml: string = 'Display received message send from HTML';

  build() {
    Column() {
      // 展示接收到的来自HTML的内容
      Text(this.receivedFromHtml)
      // 输入框的内容发送到HTML
      TextInput({placeholder: 'Send this message from ets to HTML'})
        .onChange((value: string) => {
          this.sendFromEts = value;
        })

      Button('postMessage')
        .onClick(() => {
          try {
            // 1、创建两个消息端口。
            this.ports = this.controller.createWebMessagePorts();
            // 2、在应用侧的消息端口(如端口1)上注册回调事件。
            this.ports[1].onMessageEvent((result: web_webview.WebMessage) => {
              let msg = 'Got msg from HTML:';
              if (typeof(result) === 'string') {
                console.info(`received string message from html5, string is: ${result}`);
                msg = msg + result;
              } else if (typeof(result) === 'object') {
                if (result instanceof ArrayBuffer) {
                  console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
                  msg = msg + 'lenght is ' + result.byteLength;
                } else {
                  console.info('not support');
                }
              } else {
                console.info('not support');
              }
              this.receivedFromHtml = msg;
            })
            // 3、将另一个消息端口(如端口0)发送到HTML侧,由HTML侧保存并使用。
            this.controller.postMessage('__init_port__', [this.ports[0]], '*');
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })

      // 4、使用应用侧的端口给另一个已经发送到html的端口发送消息。
      Button('SendDataToHTML')
        .onClick(() => {
          try {
            if (this.ports && this.ports[1]) {
              this.ports[1].postMessageEvent(this.sendFromEts);
            } else {
              console.error(`ports is null, Please initialize first`);
            }
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: $rawfile('xxx.html'), controller: this.controller })
    }
  }
}
  • 前端页面代码。
<!--xxx.html-->
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebView Message Port Demo</title>
</head>
<body>
    <h1>WebView Message Port Demo</h1>
    <div>
        <input type="button" value="SendToEts" onclick="PostMsgToEts(msgFromJS.value);"/><br/>
        <input id="msgFromJS" type="text" value="send this message from HTML to ets"/><br/>
    </div>
    <p class="output">display received message send from ets</p>
</body>
<script>
var h5Port;
var output = document.querySelector('.output');
window.addEventListener('message', function (event) {
    if (event.data === '__init_port__') {
        if (event.ports[0] !== null) {
            h5Port = event.ports[0]; // 1. 保存从应用侧发送过来的端口。
            h5Port.onmessage = function (event) {
              // 2. 接收ets侧发送过来的消息。
              var msg = 'Got message from ets:';
              var result = event.data;
              if (typeof(result) === 'string') {
                console.info(`received string message from html5, string is: ${result}`);
                msg = msg + result;
              } else if (typeof(result) === 'object') {
                if (result instanceof ArrayBuffer) {
                  console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
                  msg = msg + 'lenght is ' + result.byteLength;
                } else {
                  console.info('not support');
                }
              } else {
                console.info('not support');
              }
              output.innerHTML = msg;
            }
        }
    }
})

// 3. 使用h5Port向应用侧发送消息。
function PostMsgToEts(data) {
    if (h5Port) {
      h5Port.postMessage(data);
    } else {
      console.error('h5Port is null, Please initialize first');
    }
}
</script>
</html>

5. 管理页面跳转及浏览记录导航

历史记录导航

在前端页面点击网页中的链接时,Web组件默认会自动打开并加载目标网址。当前端页面替换为新的加载链接时,会自动记录已经访问的网页地址。可以通过forward()backward()接口向前/向后浏览上一个/下一个历史记录。

在下面的示例中,点击应用的按钮来触发前端页面的后退操作。

// xxx.ets
import web_webview from '@ohos.web.webview';

@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  build() {
    Column() {
      Button('loadData')
        .onClick(() => {
          if (this.webviewController.accessBackward()) {
            this.webviewController.backward();
          }
        })
      Web({ src: 'https://www.example.com/cn/', controller: this.webviewController})
    }
  }
}

如果存在历史记录,accessBackward()接口会返回true。同样,您可以使用accessForward()接口检查是否存在前进的历史记录。如果您不执行检查,那么当用户浏览到历史记录的末尾时,调用forward()backward()接口时将不执行任何操作。

 页面跳转

当点击网页中的链接需要跳转到应用内其他页面时,可以通过使用Web组件的onUrlLoadIntercept()接口来实现。

在下面的示例中,应用首页Index.ets加载前端页面route.html,在前端route.html页面点击超链接,可跳转到应用的ProfilePage.ets页面。

  1. 应用首页Index.ets页面代码。

// index.ets
import web_webview from '@ohos.web.webview';
import router from '@ohos.router';
@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();

  build() {
    Column() {
      Web({ src: $rawfile('route.html'), controller: this.webviewController })
        .onUrlLoadIntercept((event) => {
          if (event) {
            let url: string = event.data.getRequestUrl();
            if (url.indexOf('native://') === 0) {
              // 跳转其他界面
              router.pushUrl({ url:url.substring(9) })
              return true;
            }
          }
          return false;
        })
    }
  }
}

参考文献: OpenHarmoney应用开发文档

标签:web,arkts,console,business,webviewController,ArkWeb2,h5,error,webview
From: https://blog.csdn.net/m0_68038853/article/details/139613087

相关文章