ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用HTML/CSS/JS做App?WebView混合开发从入门到实战

用HTML/CSS/JS做App?WebView混合开发从入门到实战 很多人听到用HTML/CSS/JS做App第一反应是这能行吗答案是不仅能行而且我身边不少前端朋友已经靠这套组合拳在应用商店上架过自己的工具类App。核心就靠一个词WebView。简单说WebView就是原生系统内置的一个浏览器内核的壳。你写的HTML页面不管是本地文件还是线上地址都能放到这个壳里面运行外面再包一层原生的入口、标题栏、权限和调用能力。对前端工程师来说这几乎是零成本触碰App世界的入口对产品经理来说这也是快速验证移动端需求最省钱的方式之一。这篇文章我尽量把你从完全不知道WebView怎么用带到能独立完成一个App。内容以Android为主iOS部分我会把差异点单独讲最后再给一份高频踩坑清单全部是实操过来的经验照着抄就行。1. 思路先行搞懂WebView做App的底层逻辑1.1 WebView到底是什么WebView不是一门新技术也不是什么稀奇框架。在Android里它是一个系统级的View控件底层走的是Chromium内核和Chrome浏览器共用同一套渲染引擎逻辑。在iOS里对应的是WKWebView基于WebKit。换句话说你在WebView里加载一个网页本质上就是在一个没有地址栏、没有标签页、没有收藏栏的隐形浏览器里打开它。它解决的问题很直接原生开发成本高纯H5又拿不到系统能力WebView就站在中间当翻译。H5负责界面和交互原生负责包装和调能力。前端页面调用一个JS方法就能唤起手机摄像头、相册、扫码甚至支付。这些能力要是全用H5自己实现浏览器安全策略根本不允许。我这里用一句大白话总结WebView App是一个穿着原生外套、装着网页心脏的混合应用。外面那层壳是Activity里面跳动的就是你的HTML/JS/CSS。1.2 哪些场景适合WebView打包哪些不适合适合用WebView做的我按优先级排一下工具类App比如计算器、记账本、二维码生成、日历倒计时。这类功能复杂度和性能压力都不高重要的是快速上线和迭代。公司内部管理系统比如审批流、排班表、数据看板。反正用户是内部员工装个壳就能用H5更新不用发版本特别舒服。内容展示型应用比如产品介绍、使用手册、活动H5。基本不太需要原生交互WebView就够了。跨平台需求比较急的项目前端一套代码Android和iOS都能包。哪些场景不要硬上WebView我这里要泼一盆冷水大型游戏、3D渲染、实时音视频互动。WebView的性能天花板摆在那里一上高帧率画面就露馅。对App启动速度极度敏感的产品。哪怕你做了启动屏WebView加载首屏仍需要冷启动时间和原生页面比是有差距的。需要大量读取本地硬件能力的功能比如蓝牙连设备、NFC读写卡、USB串口。这些能力H5拿不到即便能通过插件磕磕绊绊实现体验也不如纯原生。1.3 三条主流技术路线怎么选同样是WebView做App市面上有几种走法我帮你把区别说透。第一条路纯手工打造。用Android Studio建一个工程自己写Activity自己配置WebView自己加方法供JS调用。这条路的学习成本最高但自由度也是最大的几乎没有框架限制代码量也完全可控。适合你想真正理解底层原理、或者项目对体积和性能有硬要求的场景。第二条路老牌工具Cordova也叫PhoneGap。它做的事情就是在原生壳之上帮你把JS调用原生能力的桥建好了大量插件可以直接用。好处是生态成熟十几年前就有人在用坏处是框架有点老JavaScript引擎和Android新版本适配偶尔会有坑。第三条路现代一点的Capacitor。这是Ionic团队出的工具支持你用npm管理依赖用前端工具链直接打包成原生项目。它跟我写本地WebView壳的区别在于它帮你把原生项目生成了你只需要在前端工程里写页面构建之后命令一下Android和iOS的壳就全出来了。适合前端团队不想动Java/Swift代码的情况。选型的逻辑我多说一句如果你只是自己做一个东西我强烈建议走第一条路。原因很现实用现成的框架虽然上手指但一旦出了兼容问题你会因为不知道底层原理而完全无从排查。亲手写一遍之后再用Cordova也好、Capacitor也好心里都踏实。2. 工程结构拆解一个WebView App的最小组成2.1 壳工程原生代码只负责容器一个WebView App的工程抽象到最底层其实就是三个部分一个全屏的WebView控件、一个Activity生命周期管理、一整套WebView配置。Activity表面上是页面其实是WebView的宿主容器。它负责什么时候创建WebView、什么时候加载URL、用户按返回键时是退出App还是回退网页、什么时候释放WebView。很多新手容易忽略的就是生命周期。例如App切到后台、再切回来WebView是会自己继续加载页面还是因为内存被回收而白屏这些都需要在原生层面对Activity和WebView做绑定管理。还有一件事非常关键壳工程里不要塞太多业务代码。我曾见过有人把登录逻辑、用户偏好、甚至网络请求全部写在Activity里导致壳工程越来越臃肿。正确的姿势是壳只负责三件事加载URL、提供JS桥、处理系统事件。其他的都交给页面本身。2.2 资源分两种本地内置与远程H5用WebView加载页面资源有两种存放方式很多新手在这里反复横跳。一种是本地内置。把编译好的HTML、CSS、JS文件直接打包进安装包。这样App安装完就自带页面启动加载的是file://路径速度极快没有网络依赖。缺点是版本更新麻烦你想改个页面内容需要重新发版。适合那种不常变内容的引导页、隐私政策页、离线工具页。另一种是远程加载。把页面放在服务器上WebView直接访问https://地址。好处是业务更新只改服务器用户下次打开App就是新版坏处是依赖网络弱网下体验会打折扣而且首屏加载存在白屏等待。实操中大多数项目会选择两者混用。壳和核心功能页面本地内置同时支持远程页面更新。比如我把启动页和主框架做成本地页面其余的业务模块放到远程。即使远程挂了至少还有个本地欢迎页撑着不至于完全黑屏。这个方案还有一个隐藏的好处是能快速修复问题。如果线上页面出了紧急Bug后端改掉页面重新部署就能解决大部分问题不需要用户去更新App。这也是很多公司愿意用WebView做业务页面的核心理由。2.3 最关键的一层JS与原生通信的桥WebView里加载的H5页面和Android原生是在两个隔离的世界里。H5想调用相册不可能直接调。原生想通知页面刷新数据简单但也有讲究。中间就需要一座桥Bridge。这座桥负责把两边的能力转成对方能理解的调用方式。H5侧调用window上的特定对象方法原生侧负责把这些调用映射到实际能力上再把结果回调给JS。这座桥同时还要约定数据格式比如回调函数的命名、返回值的序列化方式。我可以负责任地说做好这座桥WebView App就成功了一大半。很多初学者写WebView只学会了加载页面不会做通信结果页面和原生各玩各的跟放了个网页入口在App里没什么区别。后面我会专门用一章的篇幅把这座桥怎么搭讲透。3. 实操落地用Android Studio手写一个WebView壳3.1 环境准备与项目创建这一章我们直接动手。你需要的工具就两样Android Studio、一个安卓手机或者模拟器。JDK、SDK这些默认你会装不做赘述。咱们从新建工程这一步开始。打开Android Studio新建项目选择Empty Views Activity。项目名我建议就叫WebViewShell这样一看就知道是个空壳。Language选Java还是Kotlin都行我的例子用Java写逻辑更直白Kotlin熟悉的话自己翻译一下也很简单。新建之后的默认目录结构里其实已经替你建好了MainActivity和一个布局文件activity_main.xml。我们要做的是在布局里放一个WebView控件然后在MainActivity里把它找出来并配置好。3.2 写一个能跑的最小WebView页面先改布局。打开res/layout/activity_main.xml把默认的TextView和模板代码全清掉替换成下面这个?xml version1.0 encodingutf-8? WebView xmlns:androidhttp://schemas.android.com/apk/res/android android:idid/webview android:layout_widthmatch_parent android:layout_heightmatch_parent /这段代码就是让整个界面变成一整个WebView没有多余装饰。如果你后面想自己加标题栏、加底部按钮再把WebView放在它们中间就行这里是最纯粹的形态。接着打开MainActivity.java最基础的代码长这样import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebSettings; import android.webkit.WebViewClient; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private WebView webView; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView findViewById(R.id.webview); WebSettings settings webView.getSettings(); // 开启JavaScript支持这是H5页面正常运行的前提 settings.setJavaScriptEnabled(true); // 页面里的链接默认在当前WebView打开而不是跳到系统浏览器 webView.setWebViewClient(new WebViewClient()); webView.loadUrl(https://example.com); } }看到这里其实一个能显示网页的App已经完成了。你把example.com换成任意你想展示的网址安装到手机就是一个只能显示指定网页的壳App。很多新手在这时候会兴奋但同时也要意识到这才是万里长征第一步。因为你还没处理返回键、还没加加载动画、还没做通信桥、还没管理好生命周期。3.3 必须改的WebSettings配置清单WebSettings是WebView的总开关。每次我写WebView壳第一步就是把下面这些配置统一过一遍。配置不对后面八成会出各种诡异问题。settings.setJavaScriptEnabled(true); // 必须开启否则大部分H5页面直接白屏或报错 settings.setDomStorageEnabled(true); // H5的localStorage/sessionStorage靠它关闭后很多站点无法记忆状态 settings.setDatabaseEnabled(true); // 有的页面会用到Web SQL等顺手打开 settings.setCacheMode(WebSettings.LOAD_DEFAULT); // 按需使用缓存不追求极速的话这是最稳的 settings.setUseWideViewPort(true); // 让页面按视口宽度自适应尤其是PC端页面 settings.setLoadWithOverviewMode(true); // 与上面配合避免页面默认缩得很小 settings.setAllowFileAccess(true); // 加载本地HTML时必开 settings.setAllowContentAccess(true); // 允许从content://加载内容 settings.setSupportZoom(false); // App内一般不建议用户缩放容易造成布局错乱 settings.setBuiltInZoomControls(false); // 同上关闭内置缩放控件 settings.setDisplayZoomControls(false); settings.setSupportMultipleWindows(false); // 不支持多窗口避免target_blank弹新窗导致体验怪异这里重点说两个让我吃过亏的配置。一是setDomStorageEnabled。很多前端人在H5页面里喜欢用localStorage存token、存用户偏好如果这个开关没开你的页面逻辑很可能在一个比较隐晦的位置报错但浏览器控制台又不方便直接看排查起来特别费劲。我建议每次开WebView之前直接无条件打开它。二是cacheMode。如果你在开发阶段用的是LOAD_CACHE_ELSE_NETWORK改完CSS后就老是看不到新效果因为页面被WebView的缓存死死拴住了。所以我自己区分得很明确开发阶段用LOAD_NO_CACHE每次强制从服务器拉取上生产再改成LOAD_DEFAULT让WebView自己决定走缓存还是走网络。3.4 正确加载本地HTML和远程URL远程URL加载很简单上面例子已经写了loadUrl()。本地HTML加载稍微有点讲究。本地HTML文件通常放在app/src/main/assets目录下。你把这个目录当作H5页面的根目录页面文件、JS文件、CSS文件、图片资源全都往里放。放好后用下面的方式加载// 加载assets根目录下的index.html webView.loadUrl(file:///android_asset/index.html);有几个问题我提前帮你踩了。第一本地页面的相对路径引用要正确。比如你在index.html里引用./css/style.css那么assets目录下必须真实存在css/style.css大小写也要准确。第二Android的assets目录里JavaScript读取本地资源时偶尔会遇到路径编码问题尤其是中文文件名尽量全用英文命名。第三如果本地HTML里调用了远程接口请求数据会涉及跨域问题你需要确认服务端允许跨域或者干脆把请求代理到原生层。还有更进阶的玩法把本地HTML作为兜底。比如先加载本地页面同时后台去检查远程服务器上的新版本如果远程能访问就跳转过去。这样即使断网App也不至于白屏。做这个功能时我的做法是先从接口读取版本号用version字段和本地的版本字段比对再决定指向本地还是远程。4. 打通双向通道JS调原生、原生调JS4.1 原生调用JSevaluateJavascript的正确姿势原生往页面里传数据最推荐的方式是evaluateJavascript。签名如下webView.evaluateJavascript(javascript:window.callJs(带过来的值), new ValueCallbackString() { Override public void onReceiveValue(String value) { // JS执行完毕后返回的结果 } });这段代码会去执行页面上定义的window.callJs方法。如果页面里写好了callJs方法就能收到参数。以前有人喜欢用loadUrl(javascript:...)来执行但官方已经废弃了这种姿势因为loadUrl本身是加载地址的用来执行JS既不符合语义执行时机和返回值处理也不好能不用尽量不用。回到正题。evaluateJavascript这个方法的执行时机很关键。WWebView一设置页面马上就调用这个方法是没用的因为JS环境还没准备好。稳妥的做法是在WebViewClient的onPageFinished回调里执行。等到页面加载完成、window对象已就绪你再去注入数据就不会扑空。另外结果回调里的value是一个JSON字符串如果你返回的是一个字符串变量注意它可能包含引号转义。这个细节很容易让人掉坑里。4.2 JS调用原生JavascriptInterface注入对象JS调用原生靠的是addJavascriptInterface。它的作用是把一个Java对象暴露给H5页面让H5通过window.这个对象的属性名访问到Java方法。先建一个Java类作为桥import android.content.Context; import android.widget.Toast; import android.webkit.JavascriptInterface; public class JsBridge { private Context context; public JsBridge(Context context) { this.context context; } JavascriptInterface public void showToast(String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } JavascriptInterface public void closeApp() { // 终止当前Activity或其他逻辑 } }然后把它塞进WebViewwebView.addJavascriptInterface(new JsBridge(this), Android);从这一刻起H5页面里只需要这样调用window.Android.showToast(这是从JS传过来的内容);注意方法上加JavascriptInterface注解是必须的。没有这个注解JS侧永远拿不到这个方法。另外Android 4.2之后系统强制要求这个注解就是为了防止JavascriptInterface被任意方法调用造成的安全漏洞。不加你会在真机上发现JS调用时直接报NotAllowedToCall这个错误。这个方法桥的底层原理简单理解是系统把Java对象的功能映射成了JS对象的方法JS侧调用后参数序列化传回原生层执行完毕后再把结果传回JS。整个过程在WebView的UI线程和JS线程之间做了切换所以不要在JS接口方法里做太耗时的工作主线程卡住会让页面掉帧甚至ANR。4.3 实战做一个唤起原生相册的完整例子光讲理论没意思我拿一个常见需求来做完整演示页面里一个按钮点一下唤起原生相册选完图片后把图片路径回传给页面并让页面用Image对象显示出来。第一步在JsBridge里加入选择图片的方法JavascriptInterface public void openAlbum() { // 简化处理在主线程中执行Activity跳转 ((Activity) context).runOnUiThread(new Runnable() { Override public void run() { Intent intent new Intent(Intent.ACTION_PICK); intent.setType(image/*); ((Activity) context).startActivityForResult(intent, REQUEST_CODE_IMAGE); } }); }第二步在Activity的onActivityResult里接收结果拿到图片路径后再用evaluateJavascript回传给H5Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode REQUEST_CODE_IMAGE resultCode RESULT_OK) { Uri uri data.getData(); String path getRealPathFromUri(uri); String js javascript:window.onImageSelected( path ); webView.evaluateJavascript(js, null); } }H5侧对应的JS函数window.onImageSelected function(path) { document.getElementById(preview).src path; };这一整套流程走通你会发现WebView App和普通网页的差异其实就在于多了这一层传输带。有了这一层你能调用的系统能力就远远超过普通网页了。5. 从能跑到好用高频坑点与排查方案5.1 白屏与加载失败白屏是WebView开发里遇到的第一大玄学问题。很多时候页面代码没问题就是一片空白。按我的经验原因通常出在下面四个地方。第一网络权限没开。新建Android工程时如果你没有在AndroidManifest.xml里加上那行权限声明App访问任何网络资源都会静默失败页面自然白屏。检查一下uses-permission android:nameandroid.permission.INTERNET /第二HTTPS证书问题。WebView默认是从信任的CA列表里验证证书的如果你们域名用的是自签名证书或者过期的证书页面会直接加载失败。调试阶段可以让WebView暂时忽略证书校验但生产环境千万别这么干有严重的安全风险。第三本地资源路径错乱。加载assets目录下的HTML时如果JS和CSS的相对路径写错页面会只显示很简陋的HTML骨架样式全丢。这种问题没有捷径多刷新几遍页面按F12看网络请求逐个排查资源状态码。第四不显示控制台日志。WebView里跑前端代码最烦的就是不知道JS报了什么错。我的调试手段是在WebViewClient的onConsoleMessage或onReceivedError里把信息透传出来平时开发就把这些日志打进Logcat配合Logcat过滤一眼就能看见是哪个JS文件出了语法错误。5.2 需要ChromeClient的场景上传文件与能力调用很多人只写了WebViewClient没写WebChromeClient于是遇到了两个非常典型的坑文件上传按钮没反应、页面里的弹窗没法显示。文件上传的坑尤其多。H5页面里有个点击后正常浏览器会弹出文件选择器但WebView默认不弹。你要在WebChromeClient里重写onShowFileChooser然后手动唤起系统的文件选择Intent。代码大致是这样webView.setWebChromeClient(new WebChromeClient() { Override public boolean onShowFileChooser(WebView webView, ValueCallbackUri[] filePathCallback, FileChooserParams fileChooserParams) { openFileChooser(filePathCallback); return true; } });至于WebChromeClient的另一个职责比如onJsAlert、onJsConfirm、onProgressChanged这些如果你页面里用到window.alert、window.confirm没有ChromeClient它们也会被静默吞掉。所以我的建议是从一开始就不要细分WebViewClient和WebChromeClient直接把两个都配置好。5.3 返回键、生命周期与内存泄漏几乎每个用户都会在WebView App里按返回键。默认行为是直接退出Activity但用户的直觉是先返回上一页。处理方式很简单Override public void onBackPressed() { if (webView.canGoBack()) { webView.goBack(); } else { super.onBackPressed(); } }这个逻辑通俗易懂但真实项目里可以做得更细。比如你希望在退到第一页后再按一次返回键才真正退出App那就需要维护一个是否已到最后一级的状态配合Toast提示用户再按一次退出程序来防止误触。生命周期这块让很多人栽过跟头。WebView是会吃掉大量内存的组件如果你在onDestroy里不调用webView.destroy()Activity明明退出了WebView里运行的JS、渲染线程、网络请求却不会立刻释放时间一长就会出现内存泄漏甚至OOM。我的建议是在onPause时暂停所有JS执行在onResume时恢复在onDestroy时先让WebView从父容器移除再destroy。5.4 前端侧适配安全区、滚动与浏览器特性原生壳搭好之后网页前端本身也需要适配App环境。这里有几个和浏览器环境不一样的地方。iPhone的刘海屏、Android的挖孔屏都会导致页面内容被遮挡。前端需要利用安全区适配。HTML里设置viewport-fitcoverCSS里使用env(safe-area-inset-bottom)为主题栏留出空间。很多前端新人不知道这一项导致在最新款手机上按钮被底部的横条遮住半截。WebView里页面滚动也是一门学问。默认情况下WebView内部是自己的滚动体系跟外层的ScrollView嵌套使用时会互相打架。我的经验是WebView页面自己做内部滚动外层不套ScrollView一旦套了你会发现拖动手势混乱、惯性消失、下拉刷新每次都误触发。还有一件容易被忽略的事就是touch事件和点击穿透。有时候你在本地开发浏览器里点击交互一切正常一到WebView里点击位置偏移了半厘米。这往往是因为壳里的Activity用了非标准的窗口模式或者页面上有内边距导致坐标计算错乱。排查思路是先看页面的meta viewport配置再检查Activity窗口的设置。5.5 生产环境的安全与加固最后提醒几件和安全相关的事。addJavascriptInterface虽然好用但也等于给H5打开了一扇通往原生能力的大门。如果你的App加载的是不可信的第三方URL而暴露的JS桥方法足够多恶意页面就可能在用户不知情的情况下调用本地能力。所以我给自己定的规矩是生产环境的WebView只加载自家域名白名单不在JS桥里暴露危险能力同时对页面URL做严格校验。还有一个前端经常会忽略的隐患页面里的链接不要直接跳系统浏览器。默认情况下WebView只对自己的页面回调用WebViewClient处理如果不设置setWebViewClient点击一个带target_blank的链接就会跳到系统浏览器。用户会觉得这个App怎么老是跳出去体验非常差。解决办法是把target_blank强制在当前WebView里打开或者拦截后先询问用户。6. 多端与进阶iOS怎么处理、还有哪些优化手段6.1 iOS端WKWebView的对应写法Android那边讲透了iOS其实原理一样就是API换了一些。现在的iOS开发者不会再使用老旧的UIWebView了Apple已经明确弃用它新项目一律用WKWebView。iOS侧加载本地HTML用的是WKWebView的loadFileURL方法加载远程URL用loadRequest。JS调用原生不再是Java对象注入而是通过WKScriptMessageHandler注册一个name前端用window.webkit.messageHandlers.xxx.postMessage()来传参。原生调用JS操作系统提供的是evaluateJavaScript方法可以拿到JS的返回值。iOS比Android多一个比较麻烦的限制App内WKWebView不能直接访问设备的相册除非通过原生确定性的权限弹窗。在Android里有输入框就能唤起相册在iOS里你会发现点击input typefile按钮后经常没反应这个时候你要检查是否在Info.plist里配置了相册权限描述也就是Privacy - Photo Library Usage Description。这个信息缺失会让你在选择图片时应用直接崩溃体验很糟糕。6.2 用Capacitor/Cordova提升效率手动搭建原生壳的好处是透明但代价是每个系统都要写一遍。如果你的业务要同时上Android和iOS我建议在理解原理之后直接改用Capacitor。Capacitor的工作流大概是你先用Vue、React、或者纯HTML做一个前端项目安装capacitor/core和capacitor/cli然后运行npx cap init初始化一个原生项目再npx cap add android添加Android平台运行npx cap copy把前端资源同步过去。之后用Android Studio打开生成的工程就能打包。它在底层做的事和我前面手写的那个壳基本一致只不过把配置、桥接方法、常用插件全部封装好了。你如果需要使用摄像头、存储、推送之类的能力直接在npm里搜索capacitor/camera之类的插件装上就能用。这种效率是手写原生壳无法比的。不过我也要说句公道话Cordova的生态虽然老但覆盖面很广老项目中还很常见。如果你接手的是别人维护了很久的老项目大概率是Cordova而不是Capacitor。掌握原理的前提下两条路都能走顺。实在纠结的时候优先选社区活跃度更高的Capacitor文档和排坑经验都更新。6.3 性能优化与缓存策略WebView页面性能优化一个重要方向是缓存。你可以让WebView把静态资源缓存到本地第二次进入时加载速度会明显提升。常用的配置是settings.setAppCacheEnabled(true); settings.setAppCachePath(getCacheDir().getAbsolutePath()); settings.setCacheMode(WebSettings.LOAD_DEFAULT);同时在前端侧配合使用合适的缓存头比如给HTML设置no-cache给JS、CSS、图片设置长缓存。这样页面本身每次都检查更新静态资源却能直接走本地缓存。还有一个小技巧给页面加载加一个加载中的友好状态。以前我用一个简单的ProgressBar放在顶部后来发现并不够用。现在我的做法是在页面加载开始时显示一个纯原生的启动骨架屏onProgressChanged里更新进度条onPageFinished之后再隐藏。这样用户不会看到一段尴尬的全白画面感知上App启动会快很多。最后推荐一个非常实用的做法首屏前置。在App冷启动阶段先把最核心的HTML页面放到内存里预加载等用户真正要打开时页面已经渲染好了。这需要你在原生壳里维护一个常驻的WebView实例再做一个域名白名单校验防止预加载的页面被意外篡改。我个人在实际操作中最大的体会是WebView App做得是否靠谱不取决于你前端页面写得多炫而取决于壳工程对生命周期的管理、对通信桥的规范、对安全边界的意识。这三样东西扎牢了后面换技术、换框架都不慌。最后再分享一个小经验如果你打算把这个壳App上架到应用市场记得提前准备隐私政策页面。很多平台审核时会要求App必须有明确、可访问的隐私说明用WebView做的App最容易在这里漏掉。反正我吃了两次亏之后已经习惯把隐私页面做成本地HTML放在assets里了永远不掉线。这套技能前端学会了基本就等于多了一条上架App的路原生开发者学会了也会发现设计H5和原生协作的架构其实特别有成就感。我建议你今晚就可以动手试着搭一个最简壳跑起来之后你就明白整个体系是怎么转的了。
返回列表