JSBridge と言えば、皆さんが最もよく知っているのは WeChat の WeixinJSBridge です。これを介して、各パブリック ページはバックエンド メソッドを呼び出して WeChat と対話し、ユーザーに関連機能を提供できます。 UWP で独自の JSBridge を実装する方法について話しましょう。
win10 より前は、JSBridge を実装する必要がある場合、次の 2 つのメソッドがありました。
WebView を使用したことのある人なら誰でもよく知っているはずです。 、HTML ページは window.external.notify を通じてメッセージを送信でき、クライアントは WebView.ScriptNotify イベントを使用してメッセージを受信しますが、双方は文字列を使用してのみ通信できるため、通常はメッセージ形式 (json など) を定義します。 。 UWP でこのメソッドを使用するには制限があります。つまり、サイトを .appxmanifest のコンテンツ URI に追加して、これらのドメイン名の JS スクリプトが windows.external.notify メソッドを呼び出せることをシステムに伝える必要があります。もちろんローカルjsであればそのような制限はありません。追加方法は以下の通りです。
しかし、私たちは常にいくつかの特別なニーズを抱えています。たとえば、WeChat/淘宝網アプリケーションはどうですか?ドメイン名はいつでも追加される可能性があるため、毎回マニフェストを更新してからストアを更新することはできません。 8.1 では、WebView.AllowedScriptNotifyUris を使用してアプリケーションに信頼できるサイトを動的に追加することもできますが、このインターフェイスは win10 では廃止されました。アプリケーションが信頼できるサイトを頻繁に/動的に変更する必要がない場合は、この方法を引き続き使用できます。
バックグラウンドで結果を処理した後、WebView.InvokeScript/InvokeScriptAsync メソッドを通じて現在のページの js メソッドを呼び出すことができます。
最初のパラメータは js メソッド名で、 2 番目のパラメータは、メソッドに必要なパラメータを呼び出します。
このメソッドは間違いを犯しやすいことに注意してください。例外のキャッチ:( に注意してください。生成される例外は基本的にいくつかの 0xXXXXX コードです。
1 public sealed partial class MainPage : Page 2 { 3 BridgeObject.Bridge _bridge = new BridgeObject.Bridge(); 4 5 public MainPage() 6 { 7 this.InitializeComponent(); 8 9 this.wv.ScriptNotify += Wv_ScriptNotify;10 11 this.Loaded += MainPage_Loaded;12 }13 14 private async void Wv_ScriptNotify(object sender, NotifyEventArgs e)15 {16 await (new MessageDialog(e.Value)).ShowAsync();17 18 //返回结果给html页面19 await this.wv.InvokeScriptAsync("recieve", new[] { "hehe, 我是个结果"});20 }21 22 private void MainPage_Loaded(object sender, RoutedEventArgs e)23 {24 //我们事先写好了一个本地html页面用来做测试25 this.wv.Navigate(new Uri("ms-appx-web:///assets/html/index.html", UriKind.RelativeOrAbsolute));26 }27 }View Code
htmlコード:
1 <!DOCTYPE html> 2 3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta charset="utf-8" /> 6 <title></title> 7 8 <script> 9 10 //通知后台11 function func1()12 {13 14 window.external.notify("this is a message");15 16 }17 18 //这个方法用来接收后台的结果19 function recieve(value)20 {21 output.textContent = value;22 }23 24 </script>25 </head>26 <body>27 <div style="margin-top:100px">28 <button id="fun1Btn" onclick="func1();">Call method 1</button>29 <div id="output"></div>30 </div>31 </body>32 </html>View Code
はい、正しくお読みいただけました。URL を使用して JSBridge を実装することもできます。これは、諦めた後の次のステップでもあります。以前の方法 代替プラン。タオバオには前述の問題があるため、サイトが修正されない可能性があり、アプリケーションを更新することは明らかに賢明な選択ではありません。具体的には、HTML ページがバックグラウンド コードを呼び出す必要があるたびに、ページ ジャンプが行われます。もちろん、ジャンプ URL は特定のルールに従っており、パラメーターを追加できます。その後、WebView.NavigationStarting イベントを使用してジャンプをインターセプトし、そのような一見実行可能なソリューションが登場しました。
コードは実際には非常に単純で、URL パラメーターを解析し、WebView.InvokeScript/InvokeScriptAsync メソッドを通じて結果をページに返します (このメソッドはサイトに固有のものではありません)。十分に動的で、十分シンプルであるように見えますが、実際に使用していると、WebView の URL には最大長の制限があることに突然気づきました。
1 private void Wv_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args) 2 { 3 if(args.Uri.OriginalString.StartsWith("http://our/jsbridge/url/pattern")) 4 { 5 //是一次jsbridge调用,取消本次跳转 6 args.Cancel = true; 7 8 //这里具体解析url的参数 9 }10 }View Code
このメソッドの定義は次のとおりです。 🎜>public void AddWebAllowedObject(string name, object pObject )
name は、js のオブジェクトに対応するグローバル変数名です。このメソッドを通じて html ページに渡されたオブジェクトは、js の window オブジェクトにハングされます。 pObject は渡されるオブジェクトです。
まず、新しい Windows ランタイム コンポーネント プロジェクトを作成し、新しいクラス Bridge を追加して、このクラスの特徴を確認します。
すべては、AllowForWebAttribute 機能にあります。これを使用すると、オブジェクトを Web ビューに渡すことができます。ただし、AddWebAllowedObject メソッドを NavigationStarting で呼び出す必要があることが 1 つあります。 (言いません、私は長い間 DomLoaded イベントに苦労していました...)
さあ、この奇跡を目撃する時が来ました。js でこのオブジェクトを呼び出す方法を見てみましょう。 ? (私の貧弱な HTML コードは無視してください...)
1 //这个attribute是必须的,有了他我们的对象才能传递给WebView 2 [AllowForWeb] 3 public sealed class Bridge 4 { 5 /// <summary> 6 /// 提示一条消息 7 /// </summary> 8 /// <param name="msg"></param> 9 public void showMessage(string msg)10 {11 new MessageDialog(msg).ShowAsync();12 }13 14 15 }View Code
コードは非常に簡単です。唯一説明する必要があるのは、js でメソッドを呼び出すとき、最初の文字はすべて小文字であるということです。バックグラウンドで定義した最初の文字が大文字の場合、もちろん、これも JS の使用習慣に従っている必要があります)、結果を見てみましょう。
1 public sealed partial class MainPage : Page 2 { 3 BridgeObject.Bridge _bridge = new BridgeObject.Bridge(); 4 5 public MainPage() 6 { 7 this.InitializeComponent(); 8 9 this.wv.NavigationStarting += Wv_NavigationStarting;10 11 this.Loaded += MainPage_Loaded;12 }13 14 private void MainPage_Loaded(object sender, RoutedEventArgs e)15 {16 //我们事先写好了一个本地html页面用来做测试17 this.wv.Navigate(new Uri("ms-appx-web:///assets/html/index.html", UriKind.RelativeOrAbsolute));18 }19 20 private void Wv_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)21 {22 //OURBRIDGEOBJ这个是我们的对象插入到页面之后对象的变量名,这是一个全局变量,也就是window.OURBRIDGEOBJ23 this.wv.AddWebAllowedObject("OURBRIDGEOBJ", _bridge);24 }25 }View Code
1 <!DOCTYPE html> 2 3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta charset="utf-8" /> 6 <title></title> 7 8 <script> 9 10 function func1() {11 // 首先判断我们对象是否正确插入12 if (window.OURBRIDGEOBJ) {13 //调用的我们消息函数14 window.OURBRIDGEOBJ.showMessage("呵呵呵,我是个message");15 }16 }17 </script>18 </head>19 <body>20 <div style="margin-top:100px">21 <button id="fun1Btn" onclick="func1();">Call method 1</button>22 </div>23 </body>24 </html>View Code
続行する前に、win10 より前に jsbridge 経由でバックグラウンド コードを呼び出したい場合は、非同期操作を実装する方法を考えてください。
1) まず、js 呼び出しは WebView.InvokeScript から分離されているため、通常はバックグラウンド操作が完了した後、各 js 呼び出しに対して ID を生成する必要があります。 、InvokeScript メソッドを介して 結果を返すとき、呼び出し ID を返し、どの呼び出し結果をページに伝える必要があります
3)。その後、js はこの ID に基づいてコールバックし、前の操作を続行します。
但是现在我们可以抛弃那些繁琐的步骤了,我们的Windows Runtime Component支持异步(IAsyncAction/IAsyncOperation
先给我们的类添加一个简单的异步方法。
1 //这个attribute是必须的,有了他我们的对象才能传递给WebView 2 [AllowForWeb] 3 public sealed class Bridge 4 { 5 /// <summary> 6 /// 提示一条消息 7 /// </summary> 8 /// <param name="msg"></param> 9 public void showMessage(string msg)10 {11 new MessageDialog(msg).ShowAsync();12 }13 14 public Windows.Foundation.IAsyncOperation<int> giveMeAnObject(int num)15 {16 return Task.Run(async () =>17 {18 //延迟3秒钟,模拟异步任务:)19 await Task.Delay(3000);20 21 return ++num;22 }).AsAsyncOperation();23 }24 }View Code
接下来我们在js端,用 promise.then 来等待结果。
1 <!DOCTYPE html> 2 3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta charset="utf-8" /> 6 <title></title> 7 8 <script> 9 10 function func1() {11 // 首先判断我们对象是否正确插入12 if (window.OURBRIDGEOBJ) {13 //调用的我们消息函数14 window.OURBRIDGEOBJ.showMessage("呵呵呵,我是个message");15 }16 }17 18 function func2() {19 if (window.OURBRIDGEOBJ) {20 21 //对于js来说winrt的异步操作都会对应到promise上22 var result = window.OURBRIDGEOBJ.giveMeAnObject(12);23 24 // 等待结果25 result.then(function (nextNum) {26 // nextNum就是IAsyncOperation<int>的真正返回值27 output.textContent = nextNum;28 });29 30 }31 }32 </script>33 </head>34 <body>35 <div style="margin-top:100px">36 <button id="fun1Btn" onclick="func1();">Call method 1</button>37 <button id="fun2Btn" onclick="func2();">Call method 2</button>38 <div id="output" />39 </div>40 </body>41 </html>View Code
运行起来,等待3秒之后,结果出来了!
最后如果你觉得写component限制太多的话(继承都不让用。。),可以使用接口定义方法,然后在类库中实现这些方法也是一个不错的方案,下面是一个比较简单的实现供参考。
我们的jsbridge接口,包含我们准备提供的方法。
1 /// <summary>2 /// 用来定义JSBridge中实现的方法3 /// </summary>4 public interface IBridgeMethods5 {6 IAsyncOperation<int> GiveMmeAnObject(int num);7 void ShowMessage(string message);8 }View Code
修改我们的Bridge类,所有的方法都通过上面的接口来提供。
1 //这个attribute是必须的,有了他我们的对象才能传递给WebView 2 [AllowForWeb] 3 public sealed class Bridge 4 { 5 private IBridgeMethods _methods = null; 6 7 8 /// <summary> 9 /// 提示一条消息10 /// </summary>11 /// <param name="msg"></param>12 public void ShowMessage(string msg)13 {14 _methods?.ShowMessage(msg);15 }16 17 public IAsyncOperation<int> giveMeAnObject(int num)18 {19 return _methods?.GiveMmeAnObject(num);20 }21 22 /// <summary>23 /// 初始化个方法的实现24 /// </summary>25 /// <param name="obj"></param>26 public void Init(IBridgeMethods obj)27 {28 _methods = obj;29 }30 }View Code