1.2 条件编译机制全解


1.2 条件编译:罗盘的刻度与读法

本节导读:全册的主线概念在此正式登场。条件编译以 #ifdef#ifndef 与平台标识构成一套"刻度",告诉编译器每段代码属于哪个航段。本节覆盖它在五种文件位置上的写法、编译期的真实行为、与运行时判断的分界线,以及最容易踩的失效姿势。

「#ifdef」这个记号值得单独一节,因为它是 uni-app 里唯一贯穿 js、模板、样式、页面配置与应用配置所有层面的语法。上一节铺开了平台版图,本节回答"代码如何按平台分叉";再往后的每一章,遇到平台差异时都会回到这里规定的书写方式。先看全貌,再抠细节。

先看它长什么样

条件编译的核心是三个记号:#ifdef 平台标识(只在指定平台保留)、#ifndef 平台标识(在指定平台删除)、#endif(结束标记)。标记本身用各文件类型已有的注释语法包裹,因此不破坏源文件在开发工具里的合法性。

js 与 ts 文件、script 块内,用双斜线注释包裹:

// #ifdef MP-WEIXIN // 只有微信小程序会保留这段 wx.reportAnalytics('cart_click', { from: 'detail' }); // #endif // #ifndef H5 // 除 H5 外的所有端都保留:检查更新是 App 与小程序的需求 checkAppUpdate(); // #endif

template 内,用 HTML 注释包裹:

<template> <view class="pay-bar"> <!-- #ifdef MP-WEIXIN --> <button open-type="contact">联系客服</button> <!-- #endif --> <!-- #ifdef H5 --> <a href="javascript:void(0)" @click="openService">联系客服</a> <!-- #endif --> <!-- #ifndef MP --> <button @click="shareApp">分享</button> <!-- #endif --> </view> </template>

style 内,用块注释包裹:

/* #ifdef H5 */ .pay-bar { max-width: 720px; margin: 0 auto; } /* #endif */ /* #ifdef MP-WEIXIN */ .pay-bar { padding-bottom: env(safe-area-inset-bottom); } /* #endif */

pages.json 与 manifest.json 里同样用注释包裹,这是"某端独享页面"与"某端专属配置"的标准写法:

{ "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "会员商城" } } // #ifdef H5 ,{ "path": "pages/agreement/h5-privacy", "style": { "navigationBarTitleText": "隐私政策" } } // #endif ] }

平台标识是罗盘的刻度,常用的如下表。

标识 指向的平台
H5 浏览器网页端
MP 全部小程序(含各家)
MP-WEIXIN / MP-ALIPAY / MP-BAIDU 微信 / 支付宝 / 百度小程序
MP-TOUTIAO / MP-QQ / MP-KUAISOU 字节 / QQ / 快手小程序
APP-PLUS App 端(vue 与 nvue 页面)
APP-NVUE 仅 nvue 页面
APP-ANDROID / APP-IOS App 端按系统细分

图 1-2 条件编译罗盘刻度盘

图 1-2 条件编译罗盘刻度盘

编译期到底发生了什么

条件编译是构建时静态删除,不是运行时分支。编译某个平台时,编译器按标记把不属于该端的代码整块剔除,产物里根本不存在那些字符。这一点和运行时 if 有本质区别:

// 方式一:条件编译(编译期) // #ifdef MP-WEIXIN import wechatPay from '@/utils/wechat-pay.js'; // #endif // 微信构建产物里 wechatPay 存在;H5 构建产物里没有这行,也不会报错 // 方式二:运行时判断 import wechatPay from '@/utils/wechat-pay.js'; // H5 构建也会把它打包进去 const isWeixin = typeof wx !== 'undefined' && wx.requestPayment; if (isWeixin) { wechatPay.pay(); // 但非微信端运行到这里的路径根本不该存在 }

方式二的问题在于:import 无条件执行,H5 构建会把微信支付实现一并打进包里,若该模块引用了小程序专属全局对象,甚至直接白屏。方式一在编译期就斩断了这条依赖链。记住判据:凡是"这段代码在另一端不存在或不可用",用条件编译;凡是"逻辑在各端都存在、只是参数或表现不同",优先用运行时判断或配置表。后者保持代码结构统一,前者保证产物干净。

与运行时判断的分界线

举例说明分界线怎么划。主题色、文案这类"各端都展示、内容略不同"的差异,用一张平台配置表比到处写 #ifdef 干净:

// config/platform.js:把小差异收敛成配置 export const platformConfig = { // #ifdef MP-WEIXIN channel: 'weixin', shareTitle: '微信专享价', // #endif // #ifdef MP-ALIPAY channel: 'alipay', shareTitle: '支付宝专享价', // #endif // #ifdef H5 channel: 'h5', shareTitle: '新人立减', // #endif };

而"能力是否存在"这类硬差异,比如只有 App 才有的指纹登录、只有微信才有的开放标签,必须是条件编译——因为另一端连符号都解析不了。混用的代价也要心里有数:在条件编译里写运行时判断,两套条件叠加会让"哪个端执行哪句"变得极难推断,排查问题时建议先用编译产物(各端构建后的代码)确认,再怀疑逻辑。

常见的坑

⚠️ 标记注释的格式必须精确。<!-- #ifdef MP-WEIXIN --> 的井号后不能有空格差异随意,#endif 漏写会让后面所有代码被当成条件块,报错位置却可能在很远的地方。格式写错多数时候不报语法错误,而是整块代码神秘消失。

⚠️ 平台标识拼写错误不会在当前端报错。把 MP-WEIXIN 写成 MP-WECHAT,微信构建时这块代码被静默删除——功能丢了但构建成功。上线前用"各端构建一遍 + 核心路径冒烟"兜底。

嵌套条件编译在新版本里受支持,但嵌套超过一层就应视为结构坏味道,优先拆函数或抽配置。另外,条件编译作用于"文件内的块",不能跨越文件边界包裹 import 之外的语句块,也不要试图用它包裹半个语法单元(比如只包住函数的左括号),编译器按完整语句处理,半截代码会破坏解析。

收束:带走这几点

  • 三记号加一张刻度表:#ifdef 保留、#ifndef 排除、#endif 收束,五类文件各有对应的注释外壳;
  • 编译期静态删除意味着产物干净、依赖链被斩断,这是它优于运行时判断的场景;
  • 内容差异用配置表收敛,能力差异用条件编译分叉,两者划清边界代码才不烂;
  • 标记格式错误与平台名拼错都是静默失败,多端构建加冒烟验证是最后的保险。

罗盘的用法已经齐备。下一节回头补一段背景:这套机制所在的框架,是怎么一步步演化到今天的。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U