V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
karott7
V2EX  ›  程序员

做 App + H5 混合应用的请进,想听听看看

  •  
  •   karott7 · 15 天前 · 1794 次点击
    公司是做 app 的,然后有些复杂的或者活动页面会用 H5 做,然后嵌套在 app 里。
    想知道有多少人选择离线包方案,相比于打开 webview 加载 H5 使用 HTTP 缓存策略,你们觉得哪种方案更好?

    - 离线包方案:将整个 H5 资源包打包到服务器,启动 app 进入首页时空闲期间下载离线包
    - webview + http 缓存方案就不解释了
    33 条回复    2022-11-25 22:12:32 +08:00
    StarainX
        1
    StarainX  
       14 天前
    使用 H5 的优势之一是可以实时更新吧, 下载离线包的更新策略不大好控制
    多数还是偏向于 webview + http 缓存的模式吧
    wu67
        2
    wu67  
       14 天前
    webview + h5, 可以不更新 app 就能直接改页面, 前提是你们没有用到 native js. 这种情况下体验比较接近原生应用.
    stefanieewu
        3
    stefanieewu  
       14 天前
    所谓离线包不就是提前下载打包出来的资源么?跟 webview + http 缓存方案有啥区别?
    RightHand
        4
    RightHand  
       14 天前 via Android
    都离线包了为啥不做原生 app 呢,h5 就是为了时效性才用的
    karott7
        5
    karott7  
    OP
       14 天前
    @RightHand @StarainX @stefanieewu @wu67
    我公司做的 app 是面向手机低端用户,他们手机不太好,网络也差,所以就经常首次加载白屏,我那时候技术不行,不会用 http 缓存以及解决首屏加载时白屏问题,所以当时就选择了离线包方案。
    - 离线包方案也还行,进入 app 首页就全量加载所有 H5 资源,用户网络不好也能加载页面。缺点是随着 H5 项目增大,我觉得每次全量加载浪费带宽流量,当然也可以做增量,但这不是重新实现 http 缓存么;你要真说用户网络很差提前加载页面,但现在页面都要请求,有页面没请求响应不也一样么
    - 使用 http 缓存我觉得应该是很好的方案,index.html 设置 no-store ,css/js/image 等其他资源设置比较长的有效期,用户按需加载,我觉得对于绝大多数情况表现都好;对于某些场景,可以在页面空闲时间预加载其他页面资源

    就是跟同事就这两个方案意见不一,来看看大家怎么选的
    okakuyang
        6
    okakuyang  
       14 天前
    当然是离线包好控制了。离线包是一个压缩文件,比 http 请求几十次要稳定的多。
    july1995
        7
    july1995  
       14 天前 via iPhone
    歪个楼请教个问题,大家用 app 嵌套 h5 的时候,你们在移动端怎么调试的?
    stardust21
        8
    stardust21  
       14 天前
    可以进入首页后按 url 列表下载资源,然后本地有资源的情况容器拦截请求直接返回本地资源,本地没有的走在线加载,这样就不需要打一个全量包了。
    kop1989smurf
        9
    kop1989smurf  
       14 天前
    @karott7 #5 离线方案的优势是预加载,预部署。要靠业务来维护预加载的逻辑。如果是每次 application 的 onCreate 都全量重新异步加载,那其实这就不叫什么“离线”。顶多算是异步方案。

    http 缓存机制的问题就是不可控。
    比如你 app 的 launcher 需要一个新的静态 html 广告,这时候如果是纯 http 缓存机制就会吃瘪。

    @july1995 #7 这个调试指的是 ui 方面还是业务方面?
    ui 方面可以直接通过 chrome/safari 真机调试,恕不赘述。

    业务方面,且是 Vue 或 React 的话,建议 js 这边模拟一个 JSBridge ,先通过模拟的 JSBridge 调通,再走真机。否则“编译”过的 js 在真机上确实不好调试。
    karott7
        10
    karott7  
    OP
       14 天前
    @stardust21 你这个想法其实就是 http cache 的逻辑,http 缓存都是存在你的手机或者浏览器本地,https://stackoverflow.com/questions/61824427/where-is-the-storage-location-of-the-browsers-http-cache-disk-or-memory
    karott7
        11
    karott7  
    OP
       14 天前
    @kop1989smurf
    1. 先说 app 需要新的静态 html 广告的问题,我觉得这个属于特殊场景,我也不是做客户端的,不过启动广告一般都是用 cdn 提速加载吧,靠默认的加载肯定不满足需求,另外这中广告如果是图片的话走缓存的话也就第一次加载慢点,后面都是缓存
    2. 说下 http 缓存机制不可控在哪?请举例
    karott7
        12
    karott7  
    OP
       14 天前
    @july1995 我是全局设置一个元素和一个状态,请求后的数据如果要看,就打印成 json 放在当前页面后面
    Bijiabo
        13
    Bijiabo  
       14 天前
    有点好奇如何比较好的定义“空闲期间”,希望能够给一些思路
    kop1989smurf
        14
    kop1989smurf  
       14 天前
    @karott7 #11
    “启动广告一般都是用 cdn 提速加载吧,靠默认的加载肯定不满足需求,另外这中广告如果是图片的话走缓存的话也就第一次加载慢点,后面都是缓存”

    你已经把离线 html 的优势都忽略了,我不太明白你还让我举例什么。

    换句话说就是:
    依赖 http 缓存机制,能够实现预加载(未使用,先缓存)么?
    不能的话,就会导致首次使用体验不好。
    于此同时,过期控制等等其实两种方法都存在,只不过一个在业务代码中,一个在 header 里。

    但话说回来,既然对性能不敏感,争论离线 html ,还是信赖 http 缓存机制,有什么意义呢?
    kop1989smurf
        15
    kop1989smurf  
       14 天前
    @Bijiabo #13
    本帖的“空闲期间”其实非常宽泛,可以是任何时候。因为用离线 html 的初衷就是要预加载。
    你现在下的,是下一次活动,或者下一次版本更新的内容。所以完全可以无视 ui 状态静默下载。

    至于说紧急更新校验,可以放在 app 的类似 onLaunch 事件中。或者首个页面的 onCreate 中。
    lisongeee
        16
    lisongeee  
       14 天前   ❤️ 3
    > index.html 设置 no-store ,css/js/image

    应该是 index.html 使用协商缓存,css/js/image 的 url 带上 hash 使用永久强缓存
    kop1989smurf
        17
    kop1989smurf  
       14 天前
    @karott7 #11 而且你一会说是老手机,弱网环境,一会又说首次加载不重要,我就有点不理解了。
    karott7
        18
    karott7  
    OP
       14 天前
    @Bijiabo 其实也没有确切的定义,我会在进入首页后用 setTimeout(preload, 500) 去加载其他页面资源,比如使用 import ,vite 会给你生成 <link rel="modulepreload" src="" />
    kop1989smurf
        19
    kop1989smurf  
       14 天前
    当然,还有第三种选择:Service Worker
    但我个人并没有实践过,而且也与本文的焦点不重合,恕不赘述。
    https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers
    karott7
        20
    karott7  
    OP
       14 天前
    @kop1989smurf
    1. 首先不是来争论哪个方案绝对胜利的,我只是表达 http 缓存方案大多数情况下完全满足需求,像首屏广告这样没有请求的静态资源走离线包提前获取很合理。

    2. 没说首次加载不重要,对于 spa 应用,页面都是懒加载,首屏资源体积如果控制得好,几百 kb 一般用户加载也不慢,我只是觉得和服务器有交互的页面没必要走离线包

    先睡了,明天再讨论
    kop1989smurf
        21
    kop1989smurf  
       14 天前
    @karott7 #20 如果要不是“必须二选一”这种话题的话,我同意你此楼的看法。
    确实在基建很完备的中国,很大程度上,单纯离线 html 的 hybrid app 意义不大。(尤其楼主这种离线 html 生命周期只有一次 launcher 的情况下,其实是并没有发挥离线 html 真正的优势)

    所以相对而言,二者兼而有之,不同的页面根据业务需求用不同的缓存逻辑,是一个更加合理的方案。
    但其实这也有弊端,比如会导致跳转逻辑复杂度变高,缓存逻辑复杂等等。
    这就要看楼主根据 app 的业务来取舍了。

    欢迎楼主继续交流。
    SolidZORO
        22
    SolidZORO  
       14 天前 via iPhone   ❤️ 1
    sw + 自己实现一套刷新 sw 的机制。

    两年的最佳实践。
    RightHand
        23
    RightHand  
       14 天前 via Android
    @karott7 你这是两个问题,用了 webview 就别说性能问题了。你应该想要解决的的弱网环境首次加载的问题,如果细说 h5 的及时加载其实是 html 中的多媒体文件加载速度。另外缓存的方案一定讲得是命中率,全量缓存是命中率高了,但对用户及不友好。你说的那些场景也不过是优化这个命中率而已。再说增量缓存控制的问题,首先是命中率问题。再着肯定是原生来控制缓存更灵活,那么既然都原生控制了为什么不用原生来实现呢,原生配合 js 调度的问题复杂度太高了( Web 前端大佬我见过不少,app 大佬更不少,但是同时都懂的大佬基本见过)。所以目前的方案基本都是尽量取双方优点。如果一个样式,效果,逻辑能撑一个版本发布那就原生实现,不能则考虑 h5 实现(无缓存或只缓存多媒体信息)
    zpxshl
        24
    zpxshl  
       14 天前 via Android
    用了 xxx 就别在意性能了...真是二极管的思维啊。
    woxihejinghao
        25
    woxihejinghao  
       14 天前
    离线包,web 加载完成后性能还可以啊。但是做这个要做好版本管理,下载新的清理旧的,MD5 校验,验证资源的可靠性。
    RightHand
        26
    RightHand  
       14 天前 via Android
    @zpxshl 求大佬出个全平台且性能比肩原生的框架,既要 又要
    kamilic
        27
    kamilic  
       14 天前
    @karott7 你说得很对,我们公司也做过,我自己的结论也是跟你差不多。
    我们公司比较多单页面应用,html / js / css 加载基本不需要时间,FCP 快了不少。
    对于页面需要请求接口的项目,接口响应速度慢的话一样的慢,LCP / FMP 还是慢,这种离线包方案只能在那些相对静态的页面中起到「秒开」的效果。

    总结起来,我更喜欢爱奇艺那边的那套 service-worker 机制,前端自己可以控制缓存逻辑。
    https://zhuanlan.zhihu.com/p/148931732

    其他离线包的方案,横竖看都觉得是对 service-worker 的拙劣模仿 🤣
    Mather
        28
    Mather  
       14 天前
    利益相关,暂不能透露具体是哪款 APP 。

    先说说背景:
    项目因为原生实现某种功能难度较大,加上投入、历史遗留等其他因素限制,选择了“App + H5 ”混合应用的混合式方案对 APP 中的业务实现进行改造升级。改造项目进行时,对这个主题做了思考。

    项目经过两年的几次迭代,分别试过楼主所说的两种方案:
    • “webview + http 缓存”
    • “离线包”

    上面两种方案,我们在日活跃 5k 左右的项目上实验了两年左右。
    最后因为“内容可控”和要“快速修改”的要求,和面对“网络劫持”的问题,选择了“离线包”方案。

    ---------------------------------------------

    在项目完成后,我对方案做了技术总结,分析了它们的优劣:

    • 在线服务,“webview + http 缓存” 方案

    项目研发时,我们不约而同地想到在线服务的加载形式,即客户端使用域名的形式访问提供加载资源的 HTTP 服务器。

    在线服务与 Web 应用程序别无二致,使得我们有更多的时间投入到设计应用生命周期和程序 API 中去。

    可是随着项目上线若干个月,在线服务存在的缺陷便暴露出来:

    - 没有预加载:首次打开的体验很差,所有文件都要从网络请求
    - 缓存不可控:缓存的大小和策略由系统 webview 控制。缓存的清理逻辑不可控,往往加-载缓存几张图片后,重要的 HTML/JS/CSS 缓存就被清除了
    - 网络劫持:页面被运营商或其他第三方劫持,将长时间缓存劫持的页面,导致整个功能失效

    • 离线加载,楼主的“离线包”方案

    离线加载方式能较好的解决在线服务存在的若干问题。

    离线加载方式把功能模块的页面和资源打包在客户端中,Webview 组件加载本地资源文件,避免了联网因素的产生。 亦可将页面文件压缩成离线包,在自定义时机把离线包下载下来,做解压、校验等工作,达到在线更新 /热更新的效果。

    - 离线包:前端项目构建产出的文件打包成 zip 格式,并以 ${project}_${date}_${verison}.zip 命名方式交付至客户端,存放在 App 项目内作为资源引用,称为离线包。

    - 下载包:与离线包的产出相似,下载包定义为某个离线包的迭代版本,文件将存放于服务器或 CDN 上,便于客户端需要时下载。

    ---------------------------------------------

    总的来说选哪个方案,考虑方案的优劣,最终要从投入产出比来考虑,以及面向立项中主要问题出发,才能解决实际问题。
    从生产中解放出来,陪陪你爱的人,做些美好的事情。
    Mather
        29
    Mather  
       14 天前
    @july1995
    Android 的 webview 实现,使用腾讯浏览服务( X5 内核),有专门的 TBS Studio 工具能帮助开发者分析和优化网页的设计,主要功能有网页 Inspector 调试,网页性能分析等。

    iOS 端在打包 APP 时候,开启调试模式,使用 Mac Safari 进行联机调试,具体可百度搜索“safari 调试 webview”。

    --------------------------

    参考资料
    腾讯浏览服务: https://x5.tencent.com/
    TBS Studio: https://x5.tencent.com/tbs/guide/debug/season1.html
    zpxshl
        30
    zpxshl  
       14 天前
    @RightHand webview 性能是比原生差,不代表用 webview 实现的就完全不注重性能。 都是综合考虑各种取舍的。
    按你的逻辑,使用非原生方案的就没必要提性能问题了,再差都得接受吗。
    别的不说,站在用户体验上看,用 h5 实现的业务,离线包能大幅度减少首次打开的时间。 至于为啥要用 h5 ,自然有别的考虑。 在意性能不代表性能就是绝对第一优先级。

    我们之前就做过类似的混合开发。 部分页面用 webview 实现的原因是:那些业务用原生实现的话人力成本极大。 远高于 1:2 。 至于你说的同时懂原生和 js 的人是不多,但这并不是大问题。 两个团队的人分别负责维护原生和 js 就行了。
    karott7
        31
    karott7  
    OP
       13 天前   ❤️ 1
    今天工作忙,所以只能晚上来回复了;也感谢楼上几位朋友的分享,我也有一些新的收获。

    --------------
    先说下我为什么不太能接受离线包方案
    1. 正常情况下 SPA 应用多数页面都是要和服务端交互的,你有页面不能走通接口有啥意义
    2. 离线包我公司要前端 /后端 /客户端三人合作,前端还有控制版本,人力成本高;至于离线包增量更新,这不是重新实现 http 缓存方案吗?放着现有的 http 缓存机制不用,自己花时间去做,不浪费时间?站在开发者角度,我觉得不够有效率
    3. 站在用户角度,你能接受你手机的 app 下载在 H5 更新后就直接下载一个可能几兆的离线包,甚至很多页面你都不会访问。

    我如何解决部分白屏时间的 (我用 react ):
    1. H5 加载流程是这样:webview 启动( A )-> 请求 index.html 并解析( B ) -> 请求主要模块和首页资源( C ) -> 进入首页( D );
    可能造成页面白屏的阶段是 A 、B 、C ,A 阶段到 B 阶段( dom 和 css 解析完成之前)需要客户端处理(加个 loading ?),B 阶段 ( dom 和 css 解析完成之后)就会展示 <div id="root"></div>,这里开始到 C 阶段又是一段比较久的白屏,因为主要模块一般比较大(几十到两三百 kb );
    - A 阶段我就不说了,不是前端能处理的
    - B 阶段到 D 阶段之前是可以处理的,其实 <div id="root"></div> 可以改为 <div id="root"><span class="loading" /></div> 的,dom 和 css 解析完到 react 模块加载完成是就会展示这个 loading 样式(用 style 会比 svg 动画好);然后主要模块体积也要小,引入外部模块也要考虑模块体积。打包后的首屏体积一般 500kb 以下,控制到 100kb 也不是没可能
    - D 阶段进入首页之后就用 import 方法预加载其他页面或者模块资源。( service worker 我没研究,就不说了

    2. 说说 http 缓存
    - 服务器会默认给 html 、css 、js 、img 等资源设置一个 3600s 的缓存有效期。假设你什么都不做,你更新内容发布上线,会有页面没更新的情况,因为 html 这个文件是有缓存期的;需要对这个入口文件设置 no-cache (感谢 @lisongeee 提醒协商缓存) 或者 no-store 缓存响应头,就能保证每次进来都是最新的;其他资源都设置一年缓存过期时间,这样对用户体验就很好了,只要用户访问过该资源,设备就会缓存资源到本地,二次进入就是秒开。

    我觉得对于 SPA 应用,即使是弱网环境,客户端在获取 html 之前设置 loading 展示 + 前端加载主要模块设置 loading + http 缓存

    ------
    @kop1989smurf 你说的首屏广告这都属于特殊情况,还有类似笔记应用等多数页面不用和服务端交互的我觉得用离线包没问题,但是用户上面的解决白屏的方法+控制首屏资源体积+http 缓存真的适用于大多数场景。

    或许 app 加载后可以在后台开个 webview 预加载 H5 首页,之后再访问 H5 主要模块不就走缓存了么?
    karott7
        32
    karott7  
    OP
       13 天前
    @kamilic 感谢提供方案,等我有空也研究下 service worker
    karott7
        33
    karott7  
    OP
       13 天前
    @woxihejinghao 离线包加载完成后肯定行啊,重要的是人力成本;你说的更新这些,http 缓存机制都有,除去 cache-control 相关的相应头还有 Last-Modified ,etag
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2907 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 12:06 · PVG 20:06 · LAX 04:06 · JFK 07:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.