第4章 4.1 条件编译:#ifdef MP-WEIXIN
🎯 开场 3 分钟:为什么要学这个?
上一章我们做了仿掘金首页,跟着抄了一遍代码,感觉自己牛得不行了。但是慢着——你有没有想过一个让人头秃的问题:
同一套代码,怎么同时跑在微信小程序、H5、App 端?
举个例子,你在小程序里想调微信支付,H5 端想调支付宝,App 端想调苹果支付。这三个平台 API 完全不一样,总不能写三套代码维护吧?
痛点来了:
- ❌ 写三份代码 = 改一个 bug 要改三遍
- ❌ 用
if判断= 打补丁越打越烂 - ❌ 维护困难 = 头发掉光
学完这章,你能:
用一个叫「条件编译」的东西,一份代码,哪里不同打哪里补丁,微信小程序走微信的逻辑,H5 走 H5 的逻辑,App 走 App 的逻辑。改一处不影响另外两处,舒服!
🧱 基础 25 分钟:核心概念(小白视角)
4.1.1 条件编译是什么?
生活类比:快递公司的分区派送
想象你寄快递:
- 寄到北京 → 走\n\n
\n\n
\n\n京津冀专线
- 寄到上海 → 走长三角专线
- 寄到广州 → 走珠三角专线
快递公司不会为每个城市造一条流水线,而是在分叉口做判断,走不同的路线。
条件编译就是这个思想:在代码编译时,通过 #ifdef 判断当前编译目标平台,只保留对应平台的代码,其他平台的就当不存在。
4.1.2 条件编译语法一览
uniapp 的条件编译用 /* #ifdef */ 和 /* #endif */ 包裹,语法如下:
/* #ifdef MP-WEIXIN */
这里是微信小程序才有的代码
/* #endif */
/* #ifndef H5 */
这里是「除了 H5 以外」才有的代码
/* #endif */
/* #ifdef MP-WEIXIN || H5 */
这里是微信小程序或 H5 才有的代码(多平台组合)
/* #endif */
解释一下:
- #ifdef = "如果有定义"(if defined)
- #ifndef = "如果没有定义"(if not defined)
- #endif = 结束判断(end if)
- MP-WEIXIN = 微信小程序的标识,其他还有 H5、APP-PLUS、MP-ALIPAY 等
4.1.3 支持的平台标识
| 标识 | 平台 |
|---|---|
MP-WEIXIN |
微信小程序 |
MP-ALIPAY |
支付宝小程序 |
MP-BAIDU |
百度小程序 |
MP-TOUTIAO |
抖音小程序 |
H5 |
H5 网页 |
APP-PLUS |
App 端 |
APP-NVUE |
App 端的 nvue |
4.1.4 在 JavaScript 中使用
export default {
data() {
return {
platformName: ''
}
},
onLoad() {
// 小程序端获取用户信息
/* #ifdef MP-WEIXIN */
this.platformName = '微信小程序'
wx.getUserProfile({
desc: '获取用户信息',
success: (res) => {
console.log('获取到用户信息', res)
}
})
/* #endif */
// H5 端用另外的方式
/* #ifdef H5 */
this.platformName = 'H5 网页'
console.log('H5 端直接用 localStorage')
localStorage.setItem('user', 'admin')
/* #endif */
// App 端
/* #ifdef APP-PLUS */
this.platformName = 'App 端'
plus.push.getClientInfo()
/* #endif */
// 通用代码(所有平台都走这里)
console.log('所有平台都会执行这句')
}
}
解释: 代码块里的内容,只有编译目标匹配时才会被包含进去。onLoad 之外那句 console.log,三个平台都会打印。
4.1.5 在模板中使用
<template>
<view class="container">
<text>欢迎使用</text>
<!-- #ifdef MP-WEIXIN -->
<button type="primary" open-type="getPhoneNumber">
微信一键登录
</button>
<!-- #endif -->
<!-- #ifdef H5 -->
<button type="primary" @click="h5Login">
手机号登录
</button>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<button type="primary" @click="appLogin">
一键登录
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<text>非微信小程序用户专属提示</text>
<!-- #endif -->
</view>
</template>
解释: 模板里用 <!-- #ifdef xxx --> 注释语法,效果和 JS 里一样。小程序看不到 H5 和 App 的按钮,H5 也看不到小程序专属的微信登录按钮。
4.1.6 在 CSS 样式中使用
.container {
padding: 20rpx;
}
/* #ifdef MP-WEIXIN */
.container {
background-color: #07c160; /* 微信小程序用绿色 */
}
/* #endif */
/* #ifdef H5 */
.container {
background-color: #1890ff; /* H5 用蓝色 */
}
/* #endif */
/* #ifdef APP-PLUS */
.container {
background-color: #f5222d; /* App 用红色 */
}
/* #endif */
/* 通用样式所有平台都生效 */
text {
color: #333;
}
解释: 编译后每个平台只会包含自己平台的那段样式,代码量最少,加载最快。
🔥 实战 35 分钟:3 个递进的小项目
📦 项目 1:平台检测小工具(5 分钟)
目标: 写一个能识别当前运行平台的简单工具。
完整代码:
// pages/index/index.js
export default {
data() {
return {
currentPlatform: '',
platformList: []
}
},
onLoad() {
this.detectPlatform()
},
methods: {
detectPlatform() {
// 通过条件编译获取平台标识
/* #ifdef MP-WEIXIN */
this.currentPlatform = 'MP-WEIXIN'
/* #endif */
/* #ifdef H5 */
this.currentPlatform = 'H5'
/* #endif */
/* #ifdef APP-PLUS */
this.currentPlatform = 'APP-PLUS'
/* #endif */
/* #ifdef MP-ALIPAY */
this.currentPlatform = 'MP-ALIPAY'
/* #endif */
this.platformList = [
{ name: '微信小程序', active: this.currentPlatform === 'MP-WEIXIN' },
{ name: 'H5 网页', active: this.currentPlatform === 'H5' },
{ name: 'App 端', active: this.currentPlatform === 'APP-PLUS' },
{ name: '支付宝小程序', active: this.currentPlatform === 'MP-ALIPAY' }
]
console.log('当前平台:', this.currentPlatform)
}
}
}
<!-- pages/index/index.vue -->
<template>
<view class="container">
<view class="title">当前运行平台</view>
<view class="platform-name">{{ currentPlatform }}</view>
<view class="title">平台列表</view>
<view
v-for="item in platformList"
:key="item.name"
:class="['platform-item', { active: item.active }]"
>
{{ item.name }} {{ item.active ? '✅' : '' }}
</view>
</view>
</template>
<style>
.container {
padding: 40rpx;
}
.title {
font-size: 32rpx;
font-weight: bold;
margin-top: 40rpx;
margin-bottom: 20rpx;
}
.platform-name {
font-size: 48rpx;
color: #1890ff;
font-weight: bold;
}
.platform-item {
padding: 20rpx;
background: #f5f5f5;
margin-bottom: 10rpx;
border-radius: 8rpx;
}
.platform-item.active {
background: #e6f7ff;
color: #1890ff;
}
</style>
预期输出:
- 微信开发者工具:显示 MP-WEIXIN,微信小程序那行有 ✅
- H5 浏览器:显示 H5,H5 那行有 ✅
一句话解释: 通过条件编译在运行时获取当前平台标识,然后展示出来。
📦 项目 2:多端差异化 API 调用(15 分钟)
目标: 根据不同平台调用不同的 API 获取用户信息。
场景: 你做一个应用,要获取用户头像和昵称。小程序用 wx.getUserProfile,H5 用输入框让用户填写,App 用第三方 SDK。
完整代码:
// utils/user.js - 用户相关工具函数
// 获取用户信息(根据平台差异化处理)
export function getUserInfo() {
return new Promise((resolve, reject) => {
/* #ifdef MP-WEIXIN */
// 微信小程序:调用微信 API
wx.getUserProfile({
desc: '用于完善用户资料',
success: (res) => {
resolve({
platform: 'MP-WEIXIN',
nickname: res.userInfo.nickName,
avatar: res.userInfo.avatarUrl,
gender: res.userInfo.gender
})
},
fail: (err) => {
reject(err)
}
})
/* #endif */
/* #ifdef H5 */
// H5:模拟获取(实际项目可能调自己后端)
setTimeout(() => {
resolve({
platform: 'H5',
nickname: 'H5用户_' + Math.floor(Math.random() * 1000),
avatar: 'https://via.placeholder.com/100',
gender: 1
})
}, 500)
/* #endif */
/* #ifdef APP-PLUS */
// App:使用 uni-app 的 api
uni.getUserInfo({
success: (res) => {
resolve({
platform: 'APP-PLUS',
nickname: res.userInfo.nickname,
avatar: res.userInfo.avatarUrl,
gender: res.userInfo.gender
})
},
fail: (err) => {
reject(err)
}
})
/* #endif */
})
}
// 显示用户信息(根据平台用不同方式)
export function showUserInfo(userInfo) {
/* #ifdef MP-WEIXIN */
wx.showModal({
title: '用户信息',
content: `平台:${userInfo.platform}\n昵称:${userInfo.nickname}`,
showCancel: false
})
/* #endif */
/* #ifdef H5 */
alert(`平台:${userInfo.platform}\n昵称:${userInfo.nickname}`)
/* #endif */
/* #ifdef APP-PLUS */
plus.nativeUI.alert(
`平台:${userInfo.platform}\n昵称:${userInfo.nickname}`
)
/* #endif */
}
<!-- pages/demo/user-api.vue -->
<template>
<view class="container">
<button type="primary" @click="handleGetUserInfo">
获取用户信息
</button>
<view v-if="userInfo" class="user-card">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill" />
<view class="info">
<text class="nickname">{{ userInfo.nickname }}</text>
<text class="platform">平台:{{ userInfo.platform }}</text>
</view>
</view>
</view>
</template>
<script>
import { getUserInfo, showUserInfo } from '@/utils/user.js'
export default {
data() {
return {
userInfo: null
}
},
methods: {
async handleGetUserInfo() {
try {
// 调用工具函数获取用户信息
const userInfo = await getUserInfo()
this.userInfo = userInfo
// 弹窗显示(不同平台效果不同)
showUserInfo(userInfo)
} catch (err) {
console.error('获取用户信息失败', err)
uni.showToast({
title: '获取失败',
icon: 'none'
})
}
}
}
}
</script>
<style>
.container {
padding: 40rpx;
}
.user-card {
display: flex;
align-items: center;
margin-top: 40rpx;
padding: 30rpx;
background: #f9f9f9;
border-radius: 16rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
}
.info {
margin-left: 30rpx;
}
.nickname {
display: block;
font-size: 36rpx;
font-weight: bold;
}
.platform {
display: block;
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>
预期输出: 点击按钮后,不同平台弹出对应风格的对话框,并展示用户信息卡片。
一句话解释: 把平台差异化的逻辑抽到 utils/user.js 里,页面只负责调用,代码干净多了。
📦 项目 3:多端适配的配置中心(15 分钟)
目标: 做一个配置中心,根据不同平台加载不同配置,并显示对应的功能特性。
场景: 你的 App 要在不同平台展示不同的功能入口和支付方式。
完整代码:
// config/platform-config.js
// 多端配置中心
const config = {
// 通用的基础配置
appName: '我的应用',
version: '1.0.0',
// 微信小程序配置
/* #ifdef MP-WEIXIN */
payment: '微信支付',
loginType: '微信授权登录',
shareTitle: '邀请你一起用这个好应用',
adUnitId: 'adunit-xxx', // 小程序广告 ID
features: ['微信支付', '微信分享', '模板消息'],
/* #endif */
// H5 配置
/* #ifdef H5 */
payment: '支付宝/微信支付',
loginType: '手机号登录',
shareTitle: '我发现了一个超好用的应用',
features: ['支付宝支付', '微信支付', '短信登录'],
/* #endif */
// App 配置
/* #ifdef APP-PLUS */
payment: '支付宝/微信/苹果支付',
loginType: '一键登录',
shareTitle: '推荐这个超好用的 App',
features: ['支付宝支付', '微信支付', '苹果内购', '推送通知', '扫码'],
/* #endif */
// 通用功能(所有平台都有)
commonFeatures: ['浏览商品', '搜索', '收藏', '历史记录']
}
export default config
<!-- pages/demo/config-center.vue -->
<template>
<view class="container">
<view class="header">
<text class="app-name">{{ config.appName }}</text>
<text class="version">v{{ config.version }}</text>
</view>
<view class="section">
<view class="section-title">平台信息</view>
<view class="info-item">
<text class="label">当前平台:</text>
<text class="value platform">{{ currentPlatformName }}</text>
</view>
<view class="info-item">
<text class="label">登录方式:</text>
<text class="value">{{ config.loginType }}</text>
</view>
<view class="info-item">
<text class="label">支付方式:</text>
<text class="value">{{ config.payment }}</text>
</view>
</view>
<view class="section">
<view class="section-title">平台专属功能</view>
<view
v-for="(feature, index) in config.features"
:key="index"
class="feature-tag"
>
{{ feature }}
</view>
</view>
<view class="section">
<view class="section-title">通用功能(所有平台都有)</view>
<view
v-for="(feature, index) in config.commonFeatures"
:key="index"
class="feature-tag common"
>
{{ feature }}
</view>
</view>
<view class="section">
<view class="section-title">平台对比</view>
<view class="compare-table">
<view class="tr header">
<text class="th">功能</text>
<text class="th">小程序</text>
<text class="th">H5</text>
<text class="th">App</text>
</view>
<view class="tr">
<text class="td">微信支付</text>
<text class="td">✅</text>
<text class="td">✅</text>
<text class="td">✅</text>
</view>
<view class="tr">
<text class="td">苹果内购</text>
<text class="td">❌</text>
<text class="td">❌</text>
<text class="td">✅</text>
</view>
<view class="tr">
<text class="td">推送通知</text>
<text class="td">✅</text>
<text class="td">❌</text>
<text class="td">✅</text>
</view>
<view class="tr">
<text class="td">扫码</text>
<text class="td">✅</text>
<text class="td">❌</text>
<text class="td">✅</text>
</view>
</view>
</view>
</view>
</template>
<script>
import config from '@/config/platform-config.js'
export default {
data() {
return {
config: config,
currentPlatformName: ''
}
},
onLoad() {
this.setPlatformName()
},
methods: {
setPlatformName() {
/* #ifdef MP-WEIXIN */
this.currentPlatformName = '微信小程序'
/* #endif */
/* #ifdef H5 */
this.currentPlatformName = 'H5 网页'
/* #endif */
/* #ifdef APP-PLUS */
this.currentPlatformName = 'App 端'
/* #endif */
}
}
}
</script>
<style>
.container {
padding: 30rpx;
}
.header {
display: flex;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16rpx;
margin-bottom: 30rpx;
}
.app-name {
font-size: 40rpx;
font-weight: bold;
color: #fff;
}
.version {
font-size: 24rpx;
color: rgba(255,255,255,0.8);
margin-left: 20rpx;
}
.section {
margin-bottom: 40rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
padding-left: 10rpx;
border-left: 6rpx solid #667eea;
}
.info-item {
display: flex;
padding: 20rpx 0;
border-bottom: 1rpx solid #eee;
}
.label {
color: #666;
width: 160rpx;
}
.value {
flex: 1;
}
.value.platform {
color: #667eea;
font-weight: bold;
}
.feature-tag {
display: inline-block;
padding: 10rpx 20rpx;
background: #e6f7ff;
color: #1890ff;
border-radius: 30rpx;
margin-right: 15rpx;
margin-bottom: 15rpx;
font-size: 24rpx;
}
.feature-tag.common {
background: #f5f5f5;
color: #666;
}
.compare-table {
border: 1rpx solid #eee;
border-radius: 8rpx;
overflow: hidden;
}
.tr {
display: flex;
border-bottom: 1rpx solid #eee;
}
.tr:last-child {
border-bottom: none;
}
.tr.header {
background: #f9f9f9;
}
.th, .td {
flex: 1;
padding: 20rpx;
text-align: center;
font-size: 24rpx;
}
.th {
font-weight: bold;
color: #333;
}
</style>
预期输出: 页面上展示当前平台的配置信息,包括登录方式、支付方式、平台专属功能和通用功能,还有一个功能对比表。
一句话解释: 把所有平台的配置写在一个文件里,用条件编译按需加载,维护起来超级方便。
💪 进阶 20 分钟:常见坑 + 性能小贴士
❌ 坑 1:条件编译写在方法外面不生效
// ❌ 错误写法:条件编译在 data() 外面
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test' })
/* #endif */
export default {
data() {
return {}
}
}
// ✅ 正确写法:放在方法或生命周期里
export default {
onLoad() {
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test' })
/* #endif */
}
}
解释: 条件编译是在「编译时」处理的,如果写在 export default 外面,uniapp 不知道这段代码该往哪个平台塞。
❌ 坑 2:混用 if 判断和条件编译
// ❌ 错误写法:同时用运行时判断和条件编译
if (platform === 'mp-weixin') {
/* #ifdef MP-WEIXIN */
wx.showToast({ title: '微信' })
/* #endif */
}
// ✅ 正确写法:二选一
/* #ifdef MP-WEIXIN */
wx.showToast({ title: '微信' })
/* #endif */
// 或者
if (platform === 'mp-weixin') {
wx.showToast({ title: '微信' })
}
解释: 条件编译是编译时处理,if 是运行时处理。混着用容易搞混,而且条件编译的分支在编译时就被删掉了,运行时 if 判断是多余的。
❌ 坑 3:条件编译块跨越组件根节点
<!-- ❌ 错误写法:条件编译块不能跨越根元素 -->
<template>
<!-- #ifdef MP-WEIXIN -->
<view class="mp-only">小程序专属</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="h5-only">H5专属</view>
<!-- #endif -->
</template>
<!-- ✅ 正确写法:每个根元素内部独立使用条件编译 -->
<template>
<view>
<!-- #ifdef MP-WEIXIN -->
<view class="mp-only">小程序专属</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="h5-only">H5专属</view>
<!-- #endif -->
</view>
</template>
解释: 模板的根节点只能有一个,条件编译块不能把它拆开。
❌ 坑 4:忘记 #endif
// ❌ 错误写法:少了 #endif,后面的代码全遭殃
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test1' })
wx.showToast({ title: 'test2' })
// 这里忘了写 #endif
// H5 的代码也会被编译进小程序!
wx.navigateTo({ url: '/pages/h5/h5' }) // 小程序可没有这个 API!
✅ 正确写法:成对出现
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test1' })
wx.showToast({ title: 'test2' })
/* #endif */
/* #ifdef H5 */
wx.navigateTo({ url: '/pages/h5/h5' }) // 这是 H5 的代码,安全
/* #endif */
❌ 坑 5:多平台标识拼写错误
// ❌ 错误写法:写错了平台标识,条件编译失效
/* #ifdef MP_WEIXIN */ // 注意是 MP-WEIXIN,不是 MP_WEIXIN
wx.showToast({ title: 'test' })
/* #endif */
// ✅ 正确写法:使用正确的标识符
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test' })
/* #endif */
提示: 写完条件编译后,可以尝试在不同端编译一下,如果报 API 不存在的错误,先检查标识符拼写对不对。
💡 性能小贴士:条件编译减少包体积
技巧: 把平台差异化代码用条件编译包裹,编译到某个平台时,其他平台的代码会被完全移除,不会增加包体积。
比如你的小程序不需要苹果支付的 SDK,用条件编译排除掉:
/* #ifndef APP-PLUS */
// 这个 SDK 不会被编译进 App 以外的端
import { applePay } from '@/utils/apple-pay-sdk'
/* #endif */
🔧 调试技巧:编译目标切换
在 manifest.json 里切换编译目标:
// manifest.json
{
"mp-weixin": {
"appid": "wx1234567890"
}
}
运行时控制台会显示当前编译目标:
[HBuilder] 正在编译: MP-WEIXIN
如果发现编译目标不对,检查 manifest.json 的配置,或者点击 HBuilderX 顶部的「运行」菜单,手动选择目标平台。
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):平台判断
- 输入:在微信小程序编译,运行项目 1
- 预期输出:显示 MP-WEIXIN
- 提示:onLoad 里用了条件编译,小程序编译后只会进入 #ifdef MP-WEIXIN 分支
练习 2(2 分钟):加一个平台判断
- 输入:在项目 1 基础上,加一个 APP-VUE 的判断
- 预期输出:App 端显示 APP-VUE
- 提示:复制 #ifdef APP-PLUS 那段,改成 APP-VUE
练习 3(3 分钟):多平台登录
- 输入:用项目 2 的方法,给支付宝小程序加一个登录方式
- 预期输出:支付宝小程序显示「支付宝授权登录」
- 提示:参考 #ifdef MP-ALIPAY,在 getUserInfo 函数里加分支
练习 4(3 分钟):配置不同标题
- 输入:在项目 3 基础上,给每个平台设置不同的 appName 后缀
- 预期输出:小程序显示「我的应用-小程序版」,H5 显示「我的应用-H5版」
- 提示:用条件编译在 config 对象里给 appName 赋值
练习 5(挑战题,5 分钟):修复报错
- 输入:以下代码报错 "xxx is not defined",找出原因
export default {
onLoad() {
/* #ifdef MP-WEIXIN */
wx.showToast({ title: 'test' })
/* #endif */
/* #ifdef H5 */
alert('test')
/* #endif */
// 报错:alert is not defined
alert('这句是通用的')
}
}
- 预期输出:理解为什么 alert 在小程序里报错
- 提示:小程序没有浏览器的
alert函数,条件编译只处理了/* #ifdef H5 */的部分,没条件编译的普通代码所有平台都会执行
作业题(30 分钟 - 2 小时)
作业:做一个「多端欢迎页」
- 需求描述: 做一个欢迎页面,根据不同平台展示不同的欢迎语、背景色和功能提示
- 功能点:
1. 检测当前平台,显示平台名称和图标
2. 根据平台显示不同的欢迎语(小程序「欢迎使用小程序」、H5「欢迎访问网页版」、App「欢迎使用 App」)
3. 每个平台有不同的主题色
4. 展示该平台支持的 3 个核心功能 - 加分项:
1. 加上平台特有的引导操作(如小程序的「转发」、H5 的「保存到桌面」、App 的「推送开关」)
2. 使用平滑的动画过渡 - 验收标准: 能跑起来 + 不同平台显示不同内容 + 代码有注释
- 提交方式: 评论区贴代码或 GitHub 链接
📚 总结 + 资源
本文学了 3 个核心点:
- ✅ 条件编译用
/* #ifdef 平台标识 */和/* #endif */包裹,在编译时按平台选择性包含代码 - ✅ JS、模板、样式三处都可以用条件编译,实现多端差异化
- ✅ 把平台差异化逻辑抽到独立的
utils文件里,保持页面代码干净
延伸学习资源:
- uniapp 官方条件编译文档 - 官方出品,必看
- uniapp 多端适配经验分享 - 官方实战教程
- 视频:uniapp 跨平台开发避坑指南 - B 站搜索 uniapp 条件编译有很多教程
互动钩子:
你是做小程序为主,还是 H5 为主,还是 App 为主?跨端开发时你最头疼的问题是什么?评论区聊聊,老粉优先回复!
下章预告:
这一章我们用条件编译解决了「多端代码差异化」的问题。但是!每个页面都写一堆
#ifdef是不是有点烦?下一章我们要学习 自定义组件,把常用的 UI 拆成独立组件,一行代码复用,到时候条件编译配合组件,那叫一个爽!

评论(0)