AIGC 应用、数据分析等企业 10+ 热门专题课,就在极客时间企业版>>> 了解详情
写点什么

Safari 版本更新?开发者的噩梦之旅!

作者:Ashley

  • 2023-04-24
    北京
  • 本文字数:7608 字

    阅读完需:约 25 分钟

Safari 版本更新?开发者的噩梦之旅!

AI 大模型超全落地场景&金融应用实践,8 月 16 - 19 日 FCon x AICon 大会联诀来袭、干货翻倍!

近期 Safari 推出了 16.4 版本,新版本给我们带来的不是便利、而是又一场恐怖的噩梦。我们是基于浏览器的游戏开发应用 Construct 的软件商,Safari 16.4 这个早期版本则在项目打开、项目预览和使用现有项目内容等各个方面都对 Construct 造成了毁灭式的打击。这里我想分享一点个人经验,让各位客户、开发者、监管机构乃至苹果自己感受到我们在 Safari 的这次例行发布中受到了怎样的折磨。

 

大多数浏览器都会提供预发行版以供早期测试。Chrome Canary 和 Firefox Nightly 就会每天更新,但其实际开发和测试版的发布频率则相对较低。苹果虽然也提供 Safari 技术预览版(STP),但却仅适用于 macOS,而且不会公开发布任何更新时间表——大概的频率就是每两周一次。浏览器的预发行版往往很不稳定,其中的明显问题也能快速得到解决。而一旦进入测试版,专业用户得仔细观察、认真体验了。所以当 Safari 16.4 beta 1 于 2 月 16 号发布时(同样没有任何相应的公开时间表),我们马上开始研究,并很快发现了一大堆问题。

 

无法正常打开项目

 

Construct 项目基于 zip 文件,我们使用流行的 zip.js 库来读取这些文件,在支持方面则反过来使用 Compression Streams API。Safari 16.4 添加了对 Compression Streams API 的支持,但却与 zip.js 存在一定的兼容性问题,因此在 Construct 中打开项目经常会触发失败。可以想见,Web 上其他依赖于 zip.js 的项目应该也受到了类似的影响。

 

我在 2 月 17 号上报了这个问题。在对这个问题是否等同于另一个问题抱有疑惑之后(实际并不相同),苹果工程师进行了调查并确定上报属实,表示在 2 月 27 日之前成功将其解决。最终效果不错,苹果方面的工作也值得赞赏。

 

之后就是 3 月 8 号发布的 Safari 技术预览版 165,我发现这个 bug 仍然存在。这时候 Safari 16.4 的正式版似乎很快就要来了,但我们也不确定,毕竟苹果根本就不提供公开时间表。这时候我们该怎么办?Bug 的存在是因为苹果方面已经做了修复,但问题没得到解决?还是说他们根本就没在这个版本里做修复?可是一周多之前已经修复过了呀,怎么会出这样的纰漏呢?Safari 的正式版会不会也有问题,之后再发布紧急补丁来解决?苹果不至于要先惹毛所有 zip.js 开发者用户,才猛然发觉需要补救吧?如果真是这样,那这个问题要持续多久?身为用户,我们是不是该啥都别做,单纯指望 Safari 16.4 能附带有效的修复程序?可万一正式发布的版本不行,那 Construct 必然会受到灾难性的影响。这简直是个恐怖的困境。

 


我询问了 Safari 16.4 当中是否会包含修复程序,一位工程师虽然做出了回应,但却只表示技术预览版是修复过了的。这明显没有回答我们的问题,对方也没有明确保证技术预览版中的修复一定会被纳入 Safari 16.4。

 

最终,我们决定看看 Safari 16.4 到底是个什么情况。而且就在这段时间,我们的 Construct 已经无法正常打开大部分项目。当时我们唯一的选择就是不断手动测试各个 Safari 版本,浪费大量时间来验证苹果之前就已经收到过报告的问题。经过几个星期的痛苦等待,技术预览版 166 于 3 月 23 日星期四发布,Safari 16.4 则于 3 月 27 日星期一推出。没错,这两个日期之间就只夹着一个完整的工作日,而且我还记得那天我干了啥——啥也没干。我跟公司请了个假,从那个周五开始连休接下来的整个礼拜。因为没有公开的发布时间表,所以我不知道 Safari 新版本到底何时才会到来。于是乎,我直到 4 月 3 号才真正能够验证对新版本做验证,这时候距离 Safari 16.4 的全球发布已经过去了整整一周。在这段时间里,我根本不知道自己的软件能不能在 Safari 上正常运行。但幸运的是,没出什么毛病。Safari 16.4 跟技术预览版 166 拥有相同的修复程序,可整件事真的太悬了。

 

如果苹果能像其他“正常”的网络浏览器开发商一样在修复完成的版本里标记出问题,那以上所有惊心动魄的猜测和焦虑本来都可以避免。哪怕项目的透明度再好上一点点,我们都不至于搞得这么被动。

 

无法正常预览项目

 

我们发现的下一个问题,就是 Construct 在预览项目时只能显示一个空白屏幕。这对我们来说也是大麻烦,于是很快完成了问题上报。苹果工程师再次协助调查,同样做得很好。由于过程非常复杂而且跟本文主题无关,这里允许我忽略具体细节。总之,Service Workers 对于 Construct 中的项目预览功能非常重要,而我们不小心依赖了一个 Chrome bug,导致我们的 Service Worker 在 Safari 16.4 上会崩溃。在这种情况下,问题其实出在谷歌那边(麻烦快点修复,谢谢),但现在担子又落到了我们身上。然后,跟无法打开项目类似的问题又再次出现。


还是类似的可怕困境,但影响更糟糕:Safari 好像马上就要正式发布了。要是只有我们自己来解决,那大概得花多长时间?这个问题非常重要,毕竟我们的 Construct 可是有着明确的发布时间表,包括用于测试的 beta 版本,而且每隔几个月就会向所有用户推出稳定版本。如果掌握了 Safari 的发布日期,我们就能核对双方时间表,估算出能拿多长时间来调查、确保修复程序能在 Safari 更新之前就按计划发放给我们的客户。但如果说 Safari 第二天就突然更新了,那我们可就完了:Construct 无法正常预览项目,而我们必须尽快修复以防止客户受到干扰!总之大家懂的,就是例行修复和紧急状况之间的区别。

 

苹果那边则总是遮遮掩掩,部分员工暗示他们不能透露更新时间表,唯一能说的就是新版本会“很快”到来。所以我们只能把问题当作紧急状况来处理,立刻采取行动。更糟糕的是,Service Workers 开发难度很大,涉及各种复杂性因素,编码工作着实令人头痛。所以我们被迫经历了服务中断、放弃其他工作,争分夺秒先把迫在眉睫的难题搞定,努力把修复程序立即发布给所有客户。有经验的朋友肯定看得出来,这里头颇有风险——一旦搞出问题并破坏了其他组件,后果简直不堪设想。

 

如果苹果能像其他“正常”的网络浏览器开发商一样提供明确的版本更新时间表,那以上所有惊心动魄的猜测和焦虑本来都可以避免。哪怕项目的透明度再好上一点点,我们都不至于搞得这么被动。幸运的是我们的修复工作进展顺利,其他东西没有受到破坏。期间最让人烦躁的,就是 Safari 对原有 Service Worker 脚本的缓存方式似乎跟其他浏览器都不一样。我一直不理解苹果为什么要搞这个特殊,这样真的很容易让情况变得更糟。

 

最终,Safari 16.4 隔了快一个月才推出。我们本来可以不那么拼命的,紧急响应引发了一系列不必要的服务中断和时间浪费,但当时的我们别无选择。

 

Construct 中发布的所有内容都出了问题

 

除了打不开项目、无法正常预览项目之外,最严重的问题还没出场呢。在 Safari 16.4 中,Construct 近年来发布的所有 Web 游戏全都出了问题。

 

Safari 16.4 添加了对OffscreenCanvas的支持,但却只支持“2d”上下文——换言之,不支持 WebGL。Construct 需要用 WebGL 进行渲染,于是在发现 OffscreenCanvas 受到支持之后,它就会创建一个 worker 和 OffscreenCanvas,之后获取 WebGL 上下文。可这时能获取到的只有 null,于是触发故障,用户面前只剩一个空白屏幕。而这才是本次浏览器版本更新的最大问题。众多原有 Web 内容因此受到影响,我们为此专门发布了另一个紧急补丁进行修复(期间服务再次中断)。但由于我们的客户多年来已经在网络上发布了各种各样的 Web 内容,所以对全部内容做更新基本上没有可能。受到影响的包括 itch.io、Newgrounds、Poki 和我们自己网站上的几千款游戏;企业使用的海量培训材料;教师使用的教材;博物馆中的交互式信息亭等等……如果放任不理,这将是一场彻头彻尾的灾难。

 

这真的让我们惊掉了下巴。我们通过查看 OffscreenCanvas 是否已被定义(即 typeof OffscreenCanvas !== "undefined")来做检测,却没想到 Safari 浏览器居然只定义某些上下文、却漏掉了其他上下文。难道上下文不该跟标准的 <canvas> 元素拥有同等地位吗?为什么不这样呢?MDN 文档压根没提过上下文可用性不一致的问题。Chrome 在 2018 年就发布了支持所有上下文的 OffscreenCanvas,Firefox 在 2022 年完成了同样的全面支持。而 Safari 呢,时至今日还是没有做到。

 

但苹果有自己的说辞,指出只支持部分上下文完全符合规范要求,开发者应该做好相应的错误功能检测工作。我没那个闲工夫去查什么相关规范,哪怕真的符合要求,我也理解不了苹果为什么要这么干。难道作为浏览器开发商,苹果认真研究规范条文就是为了暗地里给 Web 开发者挖坑?

 

首先,我认为规范的存在意义就是保持良好的 Web 兼容性——也就是让 HTML 设计原则中强调的“支持现有内容”真正落地。例如,在发现新的 Array flatten 方法名称会破坏网站时,规范就会将其重新命名为 flat。是的,规范条款要以实际需求为准,而非实际应用以条款为准。所以我认为最合理的解决方案应该是更新规范,声明 HTML Canvas 和 OffscreenCanvas 应当支持相同的上下文。这不仅能避免我们(也可能包括其他人)面临的网络兼容性问题,也能让开发思路变得更趋统一。之后,Safari 应该延迟发布 OffscreenCanvas,直到确保其支持 WebGL,这样所有受到影响的 Web 内容都能正常运行。

 

其次,即使把规范奉为圭臬而且作者确实不打算修改,那难道苹果也不关心 Web 兼容性吗?无论如何,为什么不推迟 OffscreenCanvas 的发布?毕竟这才是尊重 Web 兼容性的务实选择。苹果完全可以放慢脚步,花点时间添加对 WebGL 的支持。我相信大多数有经验的软件开发者在职业生涯中都做过类似的判断:在开发后期发现新功能会引发问题,因此暂时关闭该功能,推迟到下一个预定版本再行发布,利用这段时间做好修复。而在 Chrome 全面支持 OffscreenCanvas 的 4 年零 6 个月之后,Safari 才迟缓地发布 OffscreenCanvas,而且还不能支持所有上下文类型。反正已经晚成这个样子了,为什么不能再等 3 个月,用完整的实施来维护 Web 兼容性?为什么一定得匆忙上线、破坏原有 Web 开发成果?我已经尽力想要说服苹果,但对方给出的回应非常模糊,基本没给项目延后留任何空间。

 

在我看来,苹果的立场反而是想尽一切办法别影响已经定好的发布时间表。我们用户这边如大难临头,苹果那边的最佳选项是推迟这项调整登陆 Safari 16.4 的时间。但最后,他们居然添加了一项特殊的浏览器功能,用来检测我们的引擎并禁用 OffscreenCanvas。这确实避免了兼容性问题,但却只适用于我们 Construct——其他受到同类问题影响的朋友,不好意思了,这个办法对你的引擎无效。

 

一点个人吐槽

 

其实我不是个喜欢抱怨的人,但面对这次的大麻烦,我真心感觉应该强调一下问题的严重性。我个人最近几周过得很累、压力巨大,甚至因为焦虑而恶心反胃。对,不光是在工作中,回到家里也是一样。问题的根源就是苹果更新带来的不确定性:也许我们即将大祸临头,也许一切都能安然度过,但我不知道该信哪个、也不知道什么时候才能揭晓答案。连着好几个礼拜,我都在胆战心惊中生活,这种心情如同等待死刑。接下来的一天要么普普通通、要么炸响惊雷,而我只能坐等一切发生。我甚至不知道最终击倒 Construct 的到底会是哪个问题。另外提醒大家,虽然以往的情况没这么极端,但 Safari 之前的版本更新也曾经引发过类似的问题。

 

我们都是人,是人就会犯错,这没什么大不了。但我想提醒大家的是,Construct 是一家已经成立十几年的公司,从一人一台笔记本发展成了如今拥有 25 万月活用户的成功企业。在此期间,我也曾看到种种灾难、争议、愤怒的客户、意外的服务器故障等等。这些都很艰难,但我们专业人士总能坚持下去。但说实在的,Safari 的这个版本是我经历过的最糟糕、压力最大的变数。最令人难以接受的是,这个问题本来很容易避免,苹果只要点点头就能把我们从苦难中拯救出来。但多年以来,他们一直坚定地拒绝改变、拒绝展现哪怕一点点仁慈之心。

 

我想再次强调,我说的这些绝对不是针对任何一位特定的苹果员工。这个错不是苹果中的具体哪个人导致的——事实上,我在前文中也提到,很多苹果员工都把工作做得很好。苹果也绝对不乏聪明和勤奋的头脑。问题在于,苹果制定的版本管理政策太过僵化,毫无透明度的制度必然引发巨大的不确定性,这才是我们这些 Web 开发人员承受不必要压力的核心与根源。我之所以反复强调这一点,唯一的目的就是让苹果意识到 Safari 的管理政策在生态系统中造成了多大的苦难,希望苹果方面能够调整自己的政策设计思路。

 

苹果的“斑斑劣迹”

 

实事求是地讲,过去的 Safari 版本一般不会闹这么大问题,但类似的情况也确实出现过,给开发者造成了很大的压力甚至服务中断。从这次的事态看,苹果的一切都没有改变,或者说正在变得更糟。下面我向大家简要汇报一下之前遇到过的 Safari 相关问题。

 

  • iOS 11.2.2破坏了WebAssembly,导致维基百科的部分内容、所有已发布的 Construct 内容等 Web 成果受到影响。许多问题本来可以通过关闭 WebAssembly 来避免,待做好准备再行开启。但苹果却决定默认启用,直到几个月后发布 iOS 11.3 时才完成修复。与此同时,苹果没有提供任何有意义的帮助或支持。期间我们只得到了一位乐于助人的维基百科工程师的支持,他分享了他们发现的解决办法。

  • Safari 11.1 破坏了MessageChannels并导致 Construct 无法正常运行,我们虽然找到了临时解决办法,但被迫在随后的好几年里都自行维持这套方案。

  • Safari 14 破坏了replaceChildren()方法,导致 Construct 发生故障。

  • Safari 14 破坏了localStorageIndexedDB

  • 即使问题已经解决,压力和不确定性也仍然存在。Safari 15 中的音频问题可能会破坏所有 Construct 内容中音频素材的正常播放。虽然这个问题在 Safari 15 正式版发布之前就已经得到了修复,但苹果事前根本没做任何通知,所以我承受了跟这次相似的巨大心理压力。

  • Safari 15.0 至 15.4 版本中存在一个WebGL bug,会导致某些 Construct 内容显示空白屏幕。问题最终在 Safari 15.5 中得到了修复,但苹果未给出任何通知,我们在手动检查各个 Safari 版本时才自行发现。

  • 多年以来,我们一直希望拥有一种能在所有浏览器中播放的统一开放音频文件格式。WebM Opus 距离成功就只差一步了——所有浏览器均可支持,包括 macOS 上的 Safari,但就偏偏是iOS和iPadOS上的Safari不行。这是我们在全部浏览器上统一使用该格式的唯一障碍,若能消除将显著降低 Web 环境下音频支持的复杂度。然而,目前还不清楚苹果怎么考虑这个问题。而且在大概一年前提交申请以来,苹果从来就没给过我任何明确的答复。

  • Safari 16 存在一个问题,在某些情况下会破坏 Construct 中的音频播放。苹果似乎没有对此做出任何有意义的回应,而问题早在半年前我们就已经提交过去了。我们仍在维护自己的解决办法,但这同时也会导致其他并发问题。

  • 太多问题如石沉大海。我曾在约一年前向苹果提出过问题,对方没有做出任何有意义的回应。与之对应,Firefox的处理态度要积极得多。

 

我们实际遇到的问题还远不止这些,聊起这个我能说个不停。而且各个问题都有类似的过程:不清楚当前发生了什么,从苹果那边得不到关于发布时间表的确切消息,苹果也拒绝向开发者透露必要的细节。

 

没错,只有苹果和 Safari 毛病最多。我们在任何其他浏览器开发商那边都很少遇到类似的问题。而且即使出了问题,对方的解决方案也是完全透明的,可供我们做出相应的规划。

 

如何解决

 

解决问题的办法非常简单——学学其他浏览器开发商。没有任何一家开发商像苹果这样给我们惹出无数麻烦,这主要是因为他们会为 Web 开发者提供更友好的开发流程,具体包括:


  • 更加透明:告知开发者即将发布 bug 修复的版本,并提供发布时间表。仅此一项,就足以消除大量不确定性。

  • 让 Safari 独立于操作系统更新之外:Safari 是业界最后一款跟操作系统完全绑定的浏览器了,这也成为浏览器更新的一个巨大障碍。即使是小小但却重要的更新,也需要等待下次整个系统更新时才能完成。这无疑延长了解决严重问题的时间周期,把本可以几周甚至几天解决的问题拖上好几个月。如果 Safari 团队能自主控制发布周期并科学管理,相信很多问题根本就不会闹到这个地步。

  • 提供更多预发布测试选项:类似于 Chrome Canary 和 Firefox Nightly,每天更新且独立于操作系统之外,这将有助于快速迭代问题并验证是否成功修复。苹果的技术预览版还应覆盖 iOS 和 iPadOS,因为目前测试预发布 iOS 版 Safari 的唯一方法就是更新整个测试版系统。这样速度很慢、极不方便。

  • 沟通:错误时有发生,如果中断是由 Safari 中的某些问题所引发,苹果理应对外通报事件情况、目前正如何处理、预计何时能够解决,以及各开发商在此期间应如何应对。但苹果以往基本保持沉默,给人一种不关心开发生态的强烈印象。我怀疑苹果的员工其实是在乎的,只是从外部视角来看,实际感受真的很差。

 

根据我的经验,其他所有浏览器开发商在这几个问题上都做得很好,只有苹果全方位表现稀烂。为 Safari 注入新功能当然是好事,苹果似乎也非常关注 Safari 16.4,但却没有采取任何措施解决这些问题。

 

总结

一切都已经过去,也确实没发生太大的问题。所以,我真有必要这么纠结吗?对,我觉得有必要。我希望有更多朋友能意识到在 Safari 上正常运行有多么费劲,而且每一次版本更新会给生态系统中的合作伙伴造成怎样的“精神创伤”。如果继续这么搞,终归会有人受到影响,我们也将反复面临灾难……每每想到这个,我恶心反胃的感觉就会翻涌而来。

 

我其实很想对 Safari 说“爱你”,它的技术积累很棒,新版本也提供了不少令人兴奋的酷炫功能。很明显,苹果并不缺能迅速解决技术问题的优秀员工,而且我对苹果中的任何个人都没有意见。但遗憾的是,Safari 的更新已经成了我们开发者的噩梦,而苹果显然有能力、也应该做得更好。新版本发布引发的严重中断已经存在多年,而苹果往往只需一点小操作就能很大程度上回避这些问题。可他们还是在坚持原本的旧办法,也从未表现出做改变的意愿和兴趣。于是像我这样的开发者就在噩梦中经受无尽折磨:正常工作被意外干扰,浪费时间检测那些苹果已经知道、却不愿主动告诉我们的问题,并在迫在眉睫的灾难压力和不确定性面前手足无措。在我看来,这些行为相当于是在忽视甚至迫害 Web 开发群体。

 

我非常希望苹果能尽快做出改变。我希望 Safari 能成长为出色的浏览器。我希望开发出能在 Safari 中顺畅运行的精彩内容。我也希望它能作为一股重要的力量,为 Web 世界的健康发展做出贡献。但坦率地讲,我更希望苹果能重视我们开发者的心理健康。如果他们还不改变,那开发商们唯一的选择就是劝说用户改用 Chrome 或者 Firefox,并通过监管机构强制扭转苹果的现有政策。虽然之前已经有监管力量的介入,但 Safari 16.4 版本的问题似乎证明苹果的问题反而日益恶化。与此同时,相较于解决问题,苹果好像更关注如何回避监管。如果我们最终只能选择 Chromium 作为唯一的答案,那不仅对 Web 世界不利,也会给我们自身带来新问题。而且哪怕最终事态发展到 Chromium 一家独大的地步,结合目前 Safari 的糟糕表现,我也只能略带惋惜地评价一句“活该”……

 

原文链接:

https://www.construct.net/en/blogs/ashleys-blog-2/safari-releases-development-1616


相关阅读:

Chrome、Edge 合力"围剿",Safari 夹缝求生?

为什么 Safari 浏览器这么不受 Web 开发者待见?

好用的油猴 Safari 浏览器插件:Tampermonkey 中文版

苹果:你甚至可以在 Safari 中使用 Chrome 的插件

2023-04-24 10:085348

评论

发布
暂无评论
发现更多内容

怎么购买GPT4o?GPT4o买不了怎么办?GPT4o订阅银行卡教程

蓉蓉

openai gpt4o

闲鱼商品详情API接口:获取与应用实战指南

Noah

如何使用华为NEXT模拟器进行应用开发

彭康佳

android 华为 鸿蒙

青椒云如何通过云桌面实现移动图形工作站

青椒云云电脑

图形工作站 移动图形工作站

从打点平台谈打点治理

百度Geek说

数据质量 企业号 6 月 PK 榜 打点平台 打点治理

实现全国算力互联互通,我们是认真的!

天翼云开发者社区

云计算 算力

性能分析: 快速定位SQL问题

EquatorCoco

数据库 sql 性能优化

从“数据孤岛”、Data Fabric(数据编织)谈逻辑数据平台

Aloudata

数据孤岛 数据虚拟化 Data Fabric 数据编织

《ERC-875:开启区块链资产交易新征程》

dappweb

defi 元宇宙开发 区块链开发

五连冠!天翼云稳居中国专属云服务市场榜首!

天翼云开发者社区

云计算 云服务 IDC

成就数智企业!用友BIP构建AI+全场景智能服务

用友BIP

AI+财务丨以共享智能化为牵引,缔造全链路的无人值守能力

用友BIP

鸿蒙系统开发如何实现跨平台功能?

FN0

鸿蒙 跨平台 HarmonyOS 鸿蒙卡片

直播预约丨《指标体系建设实战》第三期:指标平台功能架构及落地实践

袋鼠云数栈

指标体系 数据指标 指标 指标平台 指标建设

selenium滑块解锁实现的研究

霍格沃兹测试开发学社

市值风云APP装机数百万,借助NineData实现数据高效流通

NineData

数据迁移 数据管理 迁移工具 NineData 市值风云

云消息队列 ApsaraMQ 成本治理实践(文末附好礼)

阿里巴巴云原生

阿里云 云原生 ApsaraMQ

五月答谢 - 清凉礼包大派送,先到先得

Laval小助手

IM跨平台技术学习(十一):环信基于Electron打包Web IM桌面端的技术实践

JackJiang

网络编程 即时通讯 IM

数字先锋| SaaS服务“拎包入住”?央企数字化转型体验感拉满!

天翼云开发者社区

云计算 数字化转型 SaaS平台

移动图形工作站有哪些价格实惠的推荐?

青椒云云电脑

图形工作站 移动图形工作站

大厂扎堆入驻鸿蒙,中小应用厂商怎么跟?

ToB行业头条

首期“软件企业 AI 开发提效实战营”成功举办

阿里云云效

人工智能 阿里云 云原生

以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的

bin的技术小屋

ZGC JVM GC

用移动图形工作站做设计是种什么样的体验?

青椒云云电脑

图形工作站 移动图形工作站

开源数据库生态遇新变数,天翼云TeleDB提供企业数据管理更优解!

天翼云开发者社区

数据库 云计算 存储

首期“软件企业 AI 开发提效实战营”成功举办

阿里巴巴云原生

人工智能 阿里云 云原生

WiFi 7 Technology: Market Growth, Opportunities, and Challenges

wallyslilly

ipq9574 IPQ5332

浅析Spring中Async注解底层异步线程池原理

得物技术

Java spring 企业号2024年6月PK榜

Safari 版本更新?开发者的噩梦之旅!_大前端_InfoQ精选文章