uni-app 条件编译终极指南:一套代码精准控制小程序/H5/App的差异化实现

2026-08-24 0 533

上周把项目从微信小程序扩展到H5和安卓App,原以为要维护三套代码,结果发现uni-app的条件编译彻底解决了这个问题。不过群里还是有很多人搞不懂 #ifdef#ifndef,甚至有人把条件编译写进css里结果没生效。今天我就把条件编译这块从头到尾捋一遍,全是实战经验,没有废话。

先说明:条件编译是uni-app独有的特性,它不是在运行时判断,而是在编译期就干掉不需要的代码。也就是说,你编译成微信小程序时,H5的代码块根本不会被打包进去,这样做不仅能减小包体积,还能避免运行时报错。这是不同于JS里写if判断的核心点。

基础语法:记住这几个指令就够日常用了

在注释中写条件编译,主要用 #ifdef 表示“如果存在平台”,#ifndef 表示“如果不存在平台”。可以写在js、css、template以及组件json里。写法有点差异,先看最常用的js。

// #ifdef H5
console.log('这是H5专属逻辑');
// #endif

// #ifndef MP-WEIXIN
console.log('除了微信小程序之外的平台都会执行');
// #endif

其中H5MP-WEIXIN是官方预定义的平台标识。注意不要加引号,直接写就行。多平台用 || 连接,比如 // #ifdef H5 || MP-WEIXIN 表示H5或微信小程序。

在模板template里,条件编译写在注释中,格式如下:

<!-- #ifdef H5 -->
<view>只有在H5端才显示</view>
<!-- #endif -->

<!-- #ifndef MP-WEIXIN -->
<view>非微信小程序端显示</view>
<!-- #endif -->

在css里,需要用注释包裹样式:

/* #ifdef H5 */
.h5-only {
  display: flex;
}
/* #endif */

/* #ifdef MP-WEIXIN */
.mp-only {
  padding: 10px;
}
/* #endif */

有一点要特别注意:在css里进行条件编译时,必须把选择器完整包裹在注释里。如果你只包了一行声明,很可能编译后留下半吊子样式,我当时就被坑过。

文件级条件编译:不同平台用不同的文件

光靠代码块里的条件编译在复杂项目里依然难受。比如H5需要调微信JS-SDK,而小程序要调uni.login,逻辑完全不一样。这时候我更喜欢把不同代码放到不同文件里,利用uni-app的文件命名规则来区分。

官方支持在文件名后加平台后缀,例如:

  • index.h5.vue —— 仅H5会加载
  • index.mp-weixin.vue —— 仅微信小程序会加载
  • index.app.vue —— 仅App会加载

以前我总把这个规则忘记,后来项目多了才体会到它的威力。像一些使用了第三方H5 SDK的api,直接建一个sdk.h5.jssdk.mp-weixin.js,然后在主代码里统一import sdk from '@/utils/sdk',uni-app会根据当前编译平台自动选择文件。这样做比在代码里到处写#ifdef清晰得多。

这里有个坑:如果你同时存在 index.vueindex.h5.vue,那么H5编译时会优先选择 index.h5.vue,而不是 index.vue。所以公共的逻辑仍然放回默认文件里,只有差异部分单独写后缀文件。

实战案例:封装一个平台差异的存储模块

最近在做跨端小项目,需要用到本地存储。H5可以用localStorage,小程序用uni.setStorageSync,老项目里可能还有App的plus.storage。用条件编译把它一次性封装好,以后直接调用同一套接口。

// src/utils/storage.js

export function setItem(key, value) {
    // #ifdef H5
    localStorage.setItem(key, JSON.stringify(value));
    // #endif

    // #ifndef H5
    uni.setStorageSync(key, value);
    // #endif
}

export function getItem(key) {
    // #ifdef H5
    const data = localStorage.getItem(key);
    return data ? JSON.parse(data) : null;
    // #endif

    // #ifndef H5
    return uni.getStorageSync(key);
    // #endif
}

export function removeItem(key) {
    // #ifdef H5
    localStorage.removeItem(key);
    // #endif

    // #ifndef H5
    uni.removeStorageSync(key);
    // #endif
}

因为H5的localStorage只能存字符串,所以额外做了一次JSON序列化。而uni的storage处理非字符串会自动转换,所以小程序和App直接用原值就行。这种封装方式在项目里特别常见,避免了到处写分支。

页面json的条件编译:小程序独有的配置

小程序页面json里可以配置下拉刷新、导航栏样式等,有些属性在H5端没有意义。如果还想在同一份代码里维护,可以在json文件中使用条件编译注释。比如pages/index/index.json:

{
  "navigationBarTitleText": "首页",
  // #ifdef MP-WEIXIN
  "enablePullDownRefresh": true,
  "backgroundTextStyle": "dark"
  // #endif
}

但是注意,在json里写注释是不符合标准json规范的,好在uni-app的编译器会先剥离注释再解析。目前我在微信小程序上用了没问题,H5端因为没写这个属性也不会报错。不过还是建议大多数情况用pages.json里的条件编译,而不是页面内json文件。

pages.json里的条件编译:全局配置差异化

pages.json是uni-app的全局配置文件,里面也支持条件编译。比如我们小程序端需要设置全局导航栏颜色,H5端不需要,可以这样:

// pages.json 不允许直接写注释,但官方支持特殊写法
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页"
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarBackgroundColor": "#ffffff"
  },
  // #ifdef MP-WEIXIN
  "networkTimeout": {
    "request": 10000
  },
  // #endif
}

这个我没有实际用过,但看官方文档支持。唯一要注意的就是必须严格遵守JSON带注释的格式,不能把注释乱放。如果编译报错,优先检查注释是否位于键值对之间,而不是数组中间。

注意:条件编译只能用于uni-app体系内

有些同学在组件内部直接写requireocess.env.NODE_ENV去判断,其实这不算条件编译。由于uni-app在编译时已经确定了平台,你完全可以写死。但也别把原生条件编译和webpack的resolve.alias混为一谈,不然容易晕。我建议项目根目录的vue.config.js里仍然可以用webpack属性,但里面的逻辑会作用于所有端。跨端差异还是交给#ifdef吧。

还有一点,HBuilderX 3.x版本以后,条件编译支持在<script>里对import语句做注释包裹,但有一点需要注意——如果你使用vite构建(比如用CLI创建的项目),条件编译在编译过程中生效,所以import的代码也会被裁剪。如果你用npm包导入某个第三方库,在H5端用了一个版本,小程序端又用了另一个版本,最好的方式还是用文件级条件编译。

别踩这些坑:条件编译排错心得

《一》注释标记必须成对出现。少了#endif,编译阶段会报错,而且报错位置经常是文件末尾,很迷惑。

《二》html标签里的条件编译,只能包住<view>等uniapp组件,不能包住<text>的一部分。如果你试图在同一个标签中间切一刀,编译后会变得很奇怪。

《三》#ifdef#ifndef后面必须跟空格再写平台。写成#ifdefH5是无效的,而且不容易发现。

《四》如果你同时在模板和script中使用了条件编译,两个区域的编译是独立处理的。比如你在script里定义了某个变量,但在H5端又被裁剪了,模板里H5分支却用了该变量,那么H5编译时就找不到变量。这就是所谓的“跨区域引用错误”,做的时候要把模板和逻辑一起考虑,建议把平台差异封装成同一个变量,比如:

const isH5 = !!(typeof window !== 'undefined'); // 不用条件编译也可以判断,但只在H5为true

但要记住,这只是一个普通的JS判断,不会裁掉代码。若你想让H5打包时不包含小程序SDK,还是要用条件编译或文件级隔离。

《五》在App端,由于App使用的可能是vue页面或nvue页面,nvue里条件编译同样可以用,但注意nvue的样式有限,不能用某些CSS功能。如果你自适应用了条件编译,最好分别测试。

组合使用:条件编译+环境变量

有时候单纯靠平台标识不够,比如还在区分测试环境和生产环境。这时候可以把process.env.NODE_ENV和条件编译一起用。比如测试环境的H5需要开启mock数据,生产环境H5使用线上API。

// #ifdef H5
const API_BASE = process.env.NODE_ENV === 'development' ? 'https://dev-api.com' : 'https://api.com';
// #endif

// #ifdef MP-WEIXIN
const API_BASE = 'https://api.com';
// #endif

注意小程序端不能使用process.env.NODE_ENV吗?其实在uni-app里通过webpack或vite注入也能用,但为了避免混淆,小程序端我直接写死。这样编译出来,小程序端只有一行URL,不会有环境分支。

最后送上一个我在实战中总结的“口诀”

“模板注释包标签,脚本注释包代码,样式注释包选择器,文件后缀最省心。”

只要记住了这几句,基本可以应付90%的跨端需求。我对条件编译的定位就是:它是解决“跨端差异”的最终武器,但也不要滥用。如果一个地方差异很小,比如只是某个样式值不同,完全可以用普通class加平台判断去搞定,过度的条件编译会让代码阅读困难。在团队协作中,最好约定好条件注释的用途和范围,避免每个人乱插。

现在这个项目已经跑了两周,H5端和小程序端共用一份代码仓库,改个逻辑两边同时生效,再也不用ctrl+c/v两遍了。条件编译真的是uni-app最值得花时间掌握的功能之一。

uni-app 条件编译终极指南:一套代码精准控制小程序/H5/App的差异化实现
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 uniapp uni-app 条件编译终极指南:一套代码精准控制小程序/H5/App的差异化实现 https://www.taomawang.com/web/uniapp/2596.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务