Android WebView 中的 VueJS (Xamarin)
VueJS in Android WebView (Xamarin)
我正在尝试将使用 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>
控制台中没有任何错误,只是一个空白屏幕。
我错过了什么?
在你的 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不输出斜线
经过一番努力之后,我的问题的根源似乎在于构建,为 JS 文件和资产生成绝对路径。我更改了 vue.config.js 中的两个设置以使其正常工作。
请注意,以前的解决方案也有效,但它隐藏了真正的问题而不是解决问题。此外,此解决方法仅适用于 android(我没有找到如何在 iOS 上执行相同操作)
这是我所做的:我将 assetsDir 和 publicPath 更改为空字符串以强制构建生成相对路径。
module.exports = {
assetsDir: '',
publicPath: '',
.....
}
此外,我不得不关闭路由器的历史模式并定义一条额外的路由:
{
path: '/index.html',
name: 'index',
component: ..........,
}
这样,iOS 上的文件系统服务也不会出现问题。
--------------------替代解决方法--------------------
以下解决方案仅适用于 android:
我明白了。这是 file:// 网址!更改为 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 );
}
}
这里发生了几件事。如果您打算在本地主机上提供 webview,我想您的 "I figured it out" 是正确的解决方案。
我正在尝试使用 Vue 实例在网络视图中 运行 离线。这就是 JustinHui 的回应对我有用的地方。我没有手动删除 vue cli 生成的 index.html 中的斜杠,而是转到 vue.config.js 并将 publicPath 设置为空字符串。这样就解决了。
我正在尝试将使用 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>
控制台中没有任何错误,只是一个空白屏幕。
我错过了什么?
在你的 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不输出斜线
经过一番努力之后,我的问题的根源似乎在于构建,为 JS 文件和资产生成绝对路径。我更改了 vue.config.js 中的两个设置以使其正常工作。
请注意,以前的解决方案也有效,但它隐藏了真正的问题而不是解决问题。此外,此解决方法仅适用于 android(我没有找到如何在 iOS 上执行相同操作)
这是我所做的:我将 assetsDir 和 publicPath 更改为空字符串以强制构建生成相对路径。
module.exports = {
assetsDir: '',
publicPath: '',
.....
}
此外,我不得不关闭路由器的历史模式并定义一条额外的路由:
{
path: '/index.html',
name: 'index',
component: ..........,
}
这样,iOS 上的文件系统服务也不会出现问题。
--------------------替代解决方法--------------------
以下解决方案仅适用于 android:
我明白了。这是 file:// 网址!更改为 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 );
}
}
这里发生了几件事。如果您打算在本地主机上提供 webview,我想您的 "I figured it out" 是正确的解决方案。
我正在尝试使用 Vue 实例在网络视图中 运行 离线。这就是 JustinHui 的回应对我有用的地方。我没有手动删除 vue cli 生成的 index.html 中的斜杠,而是转到 vue.config.js 并将 publicPath 设置为空字符串。这样就解决了。