Android WebView 中的 VueJS (Xamarin)

Posted

技术标签:

【中文标题】Android WebView 中的 VueJS (Xamarin)【英文标题】:VueJS in Android WebView (Xamarin) 【发布时间】:2020-02-22 08:07:42 【问题描述】:

我正在尝试将使用 VueJS 开发的小型混合 C#/单页应用程序捆绑为 android 应用程序。我们有一些类,它们创建和图像并将其发送到 ESC 打印机。如果我们从服务器加载应用程序,一切正常:

theView.LoadUrl( "https://our-app.our-server.com" );

但是,当我们从 file:///android_asset/index.html 加载它时,它会启动,但页面是空的。

我在 chrome 调试器中看到,VueJS 成功创建了一些元素:

<div data-app="true" class="application app theme--light">
  <div class="application--wrap">
    <main class="v-content" data-booted="true" style="padding: 0px;">
      <div class="v-content__wrap"></div>
    </main>
  </div>
</div>

控制台中没有错误,只是一个空白屏幕。

我错过了什么?

【问题讨论】:

演示 github repo 说明问题:github.com/nsimeonov/VueInAndroidWebView 如何在应用中打开 chrome 调试器? 它不在应用程序内部——一旦你启动了带有应用程序的安卓模拟器,你可以在你的电脑上打开 chrome://inspect/#devices 你会看到模拟器和一个检查链接在它下面。查看这篇文章:developers.google.com/web/tools/chrome-devtools/… 这是另一篇,它可能是最新的——至少截图与我看到的完全匹配(不是在谷歌上——可能是因为我使用的是 Windows 而不是 MacOS)software.intel.com/en-us/articles/… 【参考方案1】:

在你的 index.html 中,尝试去掉 css 和 js 链接开头的“/”

即改变:

<script src=/js/chunk-vendors.19bfdd07.js></script>
<script src=/js/app.d04362c5.js></script>

到:

<script src=js/chunk-vendors.19bfdd07.js></script>
<script src=js/app.d04362c5.js></script>

第一个实例意味着js文件将解析为

file:///js/chunk-vendors.19bfdd07.js
file:///js/app.d04362c5.js

代替:

file:///android_asset/js/chunk-vendors.19bfdd07.js
file:///android_asset/js/app.d04362c5.js

你可以配置 vue.config.js 不输出斜线

【讨论】:

这是我尝试的第一件事,但没有成功。为了避免手动修复所有 URL,我实现了 ShouldInterceptRequest 来提供来自 android Assets 的内容。当我放一个断点时,它停在那里,正确修复了 URL,但最后页面仍然是空的。 要在构建过程中自动删除“/”,在 vue cli 3/4 项目设置中将 publicPath: './' 添加到您的 vue.config.js。 cli.vuejs.org/config/#publicpath 事实证明这是正确的方法,但是我不得不为 publicPath 和 assetsDir 使用空字符串而不是 ./【参考方案2】:

经过一番挣扎之后,我的问题的根源似乎在于构建,为 JS 文件和资产生成绝对路径。我更改了 vue.config.js 中的两个设置以使其正常工作。

请注意,以前的解决方案也有效,但它隐藏了真正的问题,而不是解决它。此外,此解决方法仅适用于 android(我没有找到如何在 ios 上执行相同操作)

这是我所做的:我将 assetsDir 和 publicPath 更改为空字符串以强制构建生成相对路径。

    module.exports = 
      assetsDir: '',
      publicPath: '',
      .....
    

另外我必须关闭路由器的历史模式并定义一个额外的路由:

    
      path: '/index.html',
      name: 'index',
      component: ..........,
    

这样,从 iOS 上的文件系统提供服务也不会出现问题。

------------------ 替代解决方法 ---------------------

以下解决方案仅适用于 android:

我想通了。这是文件:// 网址!更改为 http://localhost 就可以了(+几行代码)

同样的原因,为什么当您直接从文件系统打开某些页面时它们就无法工作。我记得,为了查看一些网页,我必须创建一个本地网络服务器,否则我几乎看不到任何东西,因为当你以这种方式打开页面时,浏览器的限制更多。一旦我将其更改为 http://localhost 并修复了我的 ShouldInterceptRequest 以从 android Assets 为“http://localhost”提供任何请求并且一切正常。

不知道为什么(还) - 我必须以这种方式加载索引:

wv.LoadDataWithBaseURL( "http://localhost", new StreamReader( Assets.Open( "index.html" ) ).ReadToEnd(), "text/html", "UTF-8", null );

而不是

wv.LoadUrl( "http://localhost/index.html" );

当请求 http://localhost 时,我还必须重写 WebViewClient 以从资产中提供内容:


    // view client to serve the web from assets folder correctly
    public class MyWebViewClient : WebViewClient 

        private AssetManager m_Assets;

        public MyWebViewClient( AssetManager assets ) 
            m_Assets = assets;
        

        public static string GetMimeType( String url ) 
            String type = null;
            String extension = url.Split( '.' ).Last();
            if ( extension != null ) 
                if ( extension == "css" ) 
                    return "text/css";
                 else if ( extension == "js" ) 
                    return "text/javascript";
                 else if ( extension == "png" ) 
                    return "image/png";
                 else if ( extension == "gif" ) 
                    return "image/gif";
                 else if ( extension == "jpg" ) 
                    return "image/jpeg";
                 else if ( extension == "jpeg" ) 
                    return "image/jpeg";
                 else if ( extension == "woff" ) 
                    return "application/font-woff";
                 else if ( extension == "woff2" ) 
                    return "application/font-woff2";
                 else if ( extension == "ttf" ) 
                    return "application/x-font-ttf";
                 else if ( extension == "eot" ) 
                    return "application/vnd.ms-fontobject";
                 else if ( extension == "svg" ) 
                    return "image/svg+xml";
                

                type = "text/html";
            

            return type;
        

        public override WebResourceResponse ShouldInterceptRequest( WebView view, IWebResourceRequest request ) 

            if ( request.Url != null && request.Url.Path != null && request.Url.Host == "localhost" ) 
                var mimeType = GetMimeType( request.Url.Path );
                var fileUrl = request.Url.Path
                    .Replace( "file://", "" )
                    .Replace( "android_asset/", "" )
                    .Trim( '/' );

                string extension = request.Url.Path.Split( '.' ).Last();;
                string fileEncoding = "application/octet-stream";
                if ( extension.EndsWith( "html" ) || extension.EndsWith( "js" ) || extension.EndsWith( "css" ) )
                    fileEncoding = "UTF-8";

                try 
                    return new WebResourceResponse( mimeType, fileEncoding, m_Assets.Open( fileUrl ) );
                 catch 
                    // ignore
                
            

            return base.ShouldInterceptRequest( view, request );
        
    

【讨论】:

【参考方案3】:

这里发生了几件事。如果您要在本地主机上提供 webview,我想您的“我想通了”是正确的解决方案。

我正在尝试使用 Vue 实例在 webview 中脱机运行。这就是 JustinHui 的回应对我有用的地方。我没有从 vue cli 生成的 index.html 中手动删除斜线,而是转到 vue.config.js 并将 publicPath 设置为空字符串。这样就解决了。

【讨论】:

这正是我所需要的。谢谢。 我试过这个,但只有从localhost 服务对我有用。 file:// urls 只加载了页面的一部分并且没有执行所有脚本......当我在桌面机器上的浏览器中打开“index.html”时也会发生同样的情况 后续 - 将 publicPath 设置为 ./ 没有帮助,但将其设置为空字符串实际上有效。还将 assetsDir 设置为空字符串并从路由器的历史模式切换到哈希模式。

以上是关于Android WebView 中的 VueJS (Xamarin)的主要内容,如果未能解决你的问题,请参考以下文章

android webview中的按钮弹框无法点击

Android - webview 中的本地图像

Android安全开发之WebView中的地雷

Android:WebView/BaseInputConnection 中的退格键

多进程中的 Android Pie (9.0) WebView

Android 下的 WebView 中数据如何保存