You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,从根源避免重复加载:

  1. 在共享项目中创建自定义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);
        }
    }
}
  1. 在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);
            }
        }
    }
}
  1. 使用自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:18:18