本节导读:跨端的难度不在"多写几套代码",而在差异藏在宿主、渲染、包管理、审核四个不同层面。本节把平台版图铺开,为整册的"罗盘"提供需要指路的全部航段;读完后再看 1.2 的条件编译,你会明白它每一处设计对应的是哪一层差异。
假设你负责一个会员积分商城:产品经理要求微信里能打开、浏览器里能打开、应用商店里能下载。你会立刻撞上三个问题。第一,微信里最顺手的形态是小程序,而小程序跑在微信提供的 JavaScript 运行环境里,没有完整的浏览器对象;第二,浏览器里是标准 Web 页面,DOM、BOM 样样都有,却没有原生导航栏和推送;第三,应用商店要求原生 App,iOS 与 Android 的包格式、签名、上架流程完全不同。同样的业务逻辑,三种宿主给出了三套约束。跨端框架要解决的,就是把业务逻辑写一次、把这三套约束收敛成一套可管理的差异。
uni-app 的目标平台可以归成三大类。小程序类包括微信、支付宝、字节跳动、百度、QQ、快手、京东等,特点是代码跑在超级 App 提供的运行时里,逻辑层与渲染层分离,包体积有硬性上限,发布要经过平台审核。H5 类即普通浏览器环境,拥有完整 DOM 能力,部署自由(发个静态资源就能上线),但没有原生壳。App 类分两种渲染路线:vue 页面走内置 WebView 渲染(配合原生导航与窗口机制),nvue 页面走原生排版引擎;App 端还拥有 plus 运行时,可以调用绝大多数系统能力。下面的矩阵把各端关键属性摊开对比。

把矩阵读薄,差异集中在四层。宿主层:小程序没有 window、document,H5 都有;App 端多出 plus 运行时。渲染层:小程序的组件最终由原生或 WebView 混合渲染,部分 css 特性(如部分选择器)不生效;H5 css 能力最完整;App 的 nvue 页面只支持有限的 css 子集且默认 flex 布局。包管理层:微信小程序主包上限两兆、总体积也有上限,逼你做分包;H5 与 App 没有这种硬限制,但要关心首屏与安装包大小。发布层:小程序审核、应用商店审核、H5 自由部署,节奏与回滚能力完全不同。
一个直观的对照实验:同一段"获取屏幕宽度"的逻辑,三端写法各不相同。
// H5:直接读浏览器对象 const w1 = window.innerWidth; // 小程序:没有 window,走 uni 系统 API uni.getSystemInfo({ success(res) { const w2 = res.windowWidth; console.log('可用窗口宽度:', w2); } }); // App 端(vue 页面):可以走 uni API,也可以走 plus const w3 = plus.display ? plus.display.applicationWidth : uni.upx2px(750);
若把这些差异直接写进业务代码,项目很快会变成"if 平台"的沼泽。uni-app 的处理方式分两层:一层是把高频能力统一收敛到 uni. 前缀的 API 体系(第 6 章展开);另一层就是条件编译,负责收敛不了的、平台专属的那部分。
把版图讲给团队听时,最容易收到三种反驳,每一种都值得当场拆穿。误判一:"H5 端有 window,写点 window 没关系。" 麻烦在于这段代码在编译期不会报错——条件编译不处理的部分会被原样带进每端产物,等微信用户点到那个页面,控制台才告诉你 window is not defined。正确姿势是把浏览器专属逻辑圈进条件编译,让它在别的端编译期就消失:
function pageWidth() { // #ifdef H5 return window.innerWidth; // #endif // #ifndef H5 return uni.getSystemInfoSync().windowWidth; // #endif }
误判二:"小程序反正是同一种东西,一套写法通吃。" 各家小程序的 API 命名、授权流程、包体积上限都不一致,微信的登录与字节的登录是两条流程;把它们当同一种宿主,测试阶段就会在各家工具里轮流翻车。误判三:"App 端就是个套壳浏览器。" vue 页面确实跑在 WebView 里,但 plus 运行时给它的能力远超网页——推送、文件系统、原生插件都是 H5 拿不到的;反过来 nvue 页面的样式子集也比网页苛刻得多。把 App 当 H5 写,多半在推送与离线场景还账。
三种误判的共同根源,是拿某一端的直觉代表所有端。版图铺开的意义就在这里:先承认差异分布在四层,再让条件编译逐层认领,而不是指望某一段"聪明的代码"自动适配一切。
诚实地列出条件编译也无法消除的差异,比记住能消除的更重要。小程序的包体积上限不会消失,业务再小也要规划分包;iOS 上热更新受审核政策约束,动态化方案要留后路;H5 的跨域由后端与代理解决,客户端无能为力;各家小程序的授权弹窗文案、登录流程由平台方定义,只能逐家适配。判断一个需求能否跨端统一,标准不是"代码能不能写",而是"各端宿主给不给这个能力"——这句话值得贴在工位上。
版图落到日常,最有用的场景是需求评审。产品说"分享出去领优惠券",你按四层过一遍:宿主层,微信端分享靠小程序卡片、H5 端靠复制链接、App 端走原生分享面板;渲染层与此无关;发布层,H5 的活动页随时能上,小程序的分享配置变更却要重新审核。评审时把这几个答案摆出来,排期与方案的分歧自然消解。反过来,若等到开发中期才发现某端能力缺位,返工的就不只是一个函数,而是交互方案本身。把"这个需求在四层各是什么表现"写成评审模板的固定一栏,是团队从这套版图里能拿走的最实在的东西。
uni. API 收敛高频能力,条件编译处理平台专属部分,二者配合而非互替;下一节我们进入罗盘本体:条件编译到底怎么写、编译器拿到标记后做了什么。