Xamarin.Forms UWP:设置WebView.Source为HtmlWebViewSource时脚本执行两次
我之前在做Xamarin.Forms UWP项目时也踩过这个坑,用HtmlWebViewSource加载HTML字符串时脚本会执行两次,换成UrlWebViewSource就完全正常。咱们来拆解下原因和靠谱的解决办法:
问题成因
这个问题本质是Xamarin.Forms对UWP平台WebView的封装逻辑导致的:
- 当你设置
HtmlWebViewSource.Html属性时,Xamarin.Forms的绑定机制会先触发一次WebView的加载操作;紧接着,UWP原生WebView在处理本地HTML内容时,内部的导航完成事件或初始化逻辑会再次触发页面渲染,导致脚本被执行两次。 - 另外,Xamarin.Forms在处理
HtmlWebViewSource时,会把HTML字符串转成临时资源(比如内存流或本地临时文件),这个转换过程也会间接触发WebView的二次加载,进而重复执行脚本。
解决方法
方案一:自定义渲染器(最彻底,推荐)
直接绕过Xamarin.Forms的默认封装,用UWP原生WebView的NavigateToString方法加载HTML,从根源避免重复加载:
- 在共享项目中创建自定义WebView类
using Xamarin.Forms; namespace YourAppNamespace { public class CustomHtmlWebView : WebView { public static readonly BindableProperty HtmlContentProperty = BindableProperty.Create( nameof(HtmlContent), typeof(string), typeof(CustomHtmlWebView), string.Empty); public string HtmlContent { get => (string)GetValue(HtmlContentProperty); set => SetValue(HtmlContentProperty, value); } } }
- 在UWP项目中实现自定义渲染器
using Xamarin.Forms.Platform.UWP; using YourAppNamespace; using YourAppNamespace.UWP.Renderers; using Windows.UI.Xaml.Controls; [assembly: ExportRenderer(typeof(CustomHtmlWebView), typeof(CustomHtmlWebViewRenderer))] namespace YourAppNamespace.UWP.Renderers { public class CustomHtmlWebViewRenderer : WebViewRenderer { protected override void OnElementChanged(ElementChangedEventArgs<WebView> e) { base.OnElementChanged(e); if (e.NewElement is CustomHtmlWebView customWebView) { // 直接用原生方法加载HTML字符串 Control.NavigateToString(customWebView.HtmlContent); // 监听内容变更,实时更新 customWebView.PropertyChanged += CustomWebView_PropertyChanged; } if (e.OldElement is CustomHtmlWebView oldWebView) { oldWebView.PropertyChanged -= CustomWebView_PropertyChanged; } } private void CustomWebView_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == CustomHtmlWebView.HtmlContentProperty.PropertyName && sender is CustomHtmlWebView customWebView) { Control.NavigateToString(customWebView.HtmlContent); } } } }
- 使用自定义WebView
在代码后置里替换原生WebView:
var customWebView = new CustomHtmlWebView { HtmlContent = @"<html> <body> <script>console.log('脚本仅执行一次');</script> </body> </html>" }; MainPage = new ContentPage { Content = customWebView };
方案二:前端层面拦截(临时应急方案)
如果不想改原生渲染器,可以在HTML脚本里加一个全局标记,确保脚本只执行一次:
<html> <body> <script> // 用全局变量标记是否已执行 if (!window.hasExecutedScript) { window.hasExecutedScript = true; // 你的业务逻辑代码 console.log('脚本仅执行一次'); } </script> </body> </html>
这个方法适合快速测试,但如果页面有刷新或导航操作,可能会失效,所以还是推荐自定义渲染器的方案。
内容的提问来源于stack exchange,提问作者jbyrd
相关产品推荐
相关产品推荐

