主题
项目介绍与 uni-app 基础(二)
本页对应 项目介绍与 uni-app 基础,共 7 段视频,约 1 小时 0 分。
学习目标
- 能够按步骤排查 HBuilder 真机运行失败问题:检查手机授权、撤销授权并重插数据线、切换 USB 调试、重启手机,并说明 iOS 无证书时使用云手机或 H5 调试的替代方案。
- 能够注册/登录 DCloud 账号,通过 HBuilder 插件安装入口或插件市场搜索并安装 Prettier,并在已安装插件列表中确认安装结果。
- 能够配置 Prettier 生效范围(如
*.vue、*.js),在项目根目录创建 Prettier 配置文件,并设置缩进、分号、引号、Vue 缩进等常用格式化规则。 - 能够完成 HBuilder 基本配置:调整字号、将制表符设为 2、勾选空格代替制表符、开启保存时自动格式化、开启折叠最后一行,并按“先配置 Prettier、后开启自动保存”的顺序操作。
- 能够使用 HBuilder 将 uni-app 运行到真机或云手机,完成上传 APK、进入云手机、安装并启动应用的基本测试流程,并验证页面在多端表现一致。
- 能够在
uni.scss中定义全局 SCSS 变量并在页面样式中引用验证生效;能够在App.vue的script中编写全局生命周期、在style中编写全局样式,并说明App.vue不能写template。
知识点
2.1 全局文件:先建立“哪些东西全局生效”的意识
为什么需要:uni-app 组合了 Vue 和微信小程序两套知识。小程序里有 app.js、app.json、app.wxss 这类全局文件;uni-app 也提供对应物。先认清全局文件,才能知道样式、配置、生命周期该写在哪里。
怎么想:把全局文件理解成“项目级入口”:uni.scss 管全局 SASS 变量,App.vue 管应用生命周期和全局样式,pages.json 管页面路径、窗口和 tabBar 等配置。
怎么做:在 uni.scss 中定义变量,运行时它会自动注入页面样式:
css
/* uni.scss */
/* 在原有 sass 变量基础上添加新的变量 */
$uni-bg-color: #f4f4f4;随后在页面样式中使用该变量。转写稿里的调试过程是:定义变量后给 page 设置背景色,第一次发现颜色没生效,重新启动/刷新服务后才看到变化;这说明变量本身可用,问题多在编译服务未及时更新。第一次使用 SASS 时,HBuilderX 会提示安装 dart-sass 插件。

易错点:uni.scss 是变量和全局 SASS 能力,不是普通 CSS 文件;页面使用 SASS 变量时样式块要支持 SCSS。
2.1.2 App.vue:不是根组件,而是应用入口
为什么需要:小程序的 app.js 负责应用生命周期,app.wxss 负责全局样式。uni-app 的 App.vue 承接这两件事。
怎么想:script 对应 app.js,style 对应 app.wxss,template 必须省略。
怎么做:
html
<script>
// 相当于小程序的 app.js
export default {
onLaunch: function () {
console.log('App Launch')
},
onShow: function () {
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
},
}
</script>
<!-- 组件模板需要被省略 -->
<template></template>
<!-- 组件模板需要被省略 -->
<style>
/* 相当于 app.wxss */
page {
background-color: $uni-bg-color;
}
</style>转写稿中通过给 App.vue 的 style 写全局样式,再在首页放一个元素验证文字颜色变化,确认全局样式生效。
易错点:不要把 App.vue 当普通根组件写 template;应用级生命周期写在这里。
2.1.3 pages.json:页面配置与全局配置平级写
为什么需要:小程序 app.json 同时管页面路径、窗口样式、tabBar、分包等。uni-app 用 pages.json 统一管理。
怎么想:pages 是页面配置,globalStyle 对应全局 window;其它全局配置如 tabBar、subPackages 与 globalStyle 平级。
怎么做:
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "学习uni-app"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
}tabBar 配置示例:
json
{
"pages": [],
"globalStyle": {},
"tabBar": {
"color": "",
"selectedColor": "",
"borderStyle": "",
"position": "",
"list": [
{
"text": "",
"pagePath": "",
"iconPath": "",
"selectedIconPath": ""
}
]
},
"uniIdRouter": {}
}subPackages 配置示例:
json
{
"pages": [],
"globalStyle": {},
"tabBar": {},
"subPackages": [
{
"root": "",
"pages": [
{
"path": "",
"style": {
}
}
],
}
],
"uniIdRouter": {}
}易错点:tabBar 图片必须放 static 目录,否则小程序端可能无法运行;不要把这些全局项误写进 globalStyle 内部。
2.2 组件:内置组件照小程序,扩展组件按需下载
为什么需要:跨端要尽量表现一致。uni-app 重新实现了微信小程序内置组件,所以会小程序组件就能迁移。
怎么做:
xml
<view class="message">hello uni-app!</view>
<image src="" mode="" />
<swiper autoplay>
<swiper-item>
<image src="" />
</swiper-item>
<swiper-item>
<image src="" />
</swiper-item>
<swiper>扩展组件(uni ui)偏业务交互,如倒计时、日历、文件上传,需要下载到项目中再按文档使用。

易错点:扩展组件不是内置即用;插件市场第三方库要注意 Vue3 兼容性,例如 uView 不支持 Vue3。
2.3 生命周期:应用/页面用小程序,组件用 Vue
为什么需要:uni-app 同时存在小程序生命周期和 Vue 生命周期,混用会写错位置。
怎么想:记原则——应用生命周期、页面生命周期采用小程序;自定义组件生命周期使用 Vue。Vue3 setup 中,应用/页面生命周期要从 @dcloudio/uni-app 引入。
html
<script setup>
// Vue 组件生命周期
import { onMounted } from 'vue'
// 应用/页面生命周期(小程序生命周期)
import { onLaunch, onLoad } from '@dcloudio/uni-app'
// ...
</script>易错点:onLoad、onShow 等页面生命周期不要从 vue 引入;应用级 onLaunch 应放在 App.vue 逻辑中。
2.4 API 调用:命名空间换成 uni,回调与 Promise 二选一
为什么需要:小程序 API 常以 wx. 调用,uni-app 为跨端统一改为 uni.。
怎么做:
html
<script setup>
import { onLoad } from '@dcloudio/uni-app'
// 页面加载后获取接口数据
onLoad(() => {
// 原来的 wx.request 换成 uni.request
uni.request({
url: '',
success(result) {
console.log(result)
}
})
})
</script>Promise 规则:传入 success、fail、complete 任一回调,就是 callback 方式,不返回 Promise;不传回调则返回 Promise。
javascript
// 回调方式,不返回 Promise
uni.request({
url: 'https://your.api.com/xxx',
success: (result) => {
console.log(result)
}
})
// 如果在调用 API 时传入了 success fail complete 那么不会返回 Promisejavascript
// Promise 方式
const result = uni.request({
url: 'https://your.api.com/xxx'
})
// Promise 对象
console.log(result)易错点:不要一边传 success 一边又 await 返回值;Promise 方式便于配合 async/await。
2.5 条件编译:一套代码里给不同平台留口子
为什么需要:跨端有差异,例如 uni.login 在 H5 不支持,keep-alive 只用于 H5。条件编译让特定代码只在指定平台参与编译。
怎么做:用特殊注释标记:
#ifdef 平台名称 || 平台名称
特定平台要执行的代码
#endif
#ifndef 平台名称
除了特定平台之外其它平台要执行的代码
#endif示例:
html
<script setup>
// #ifdef H5
console.log('这段代码只在 H5 端才会运行')
// #endif
</script>
<template>
<!-- #ifdef H5 -->
<view class="iconfont icon-search"></view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="iconfont icon-scan"></view>
<!-- #endif -->
</template>
<style lang="scss" scoped>
.iconfont {
color: #666;
font-size: 30rpx;
/* #ifdef H5 */
font-size: 28rpx;
/* #endif */
}
</style>常用平台名有 VUE3、APP-PLUS、APP-ANDROID、APP-IOS、H5、MP、MP-WEIXIN、MP-ALIPAY。
易错点:条件编译靠注释识别,注释符号和 #ifdef/#endif 不能写错;它是在编译阶段生效,不是运行时判断。
作业目标:把真机运行环境和 HBuilder 配置跑通
本节作业不是写业务页面,而是把后续开发要用的环境与工具配置顺一遍:真机运行能连上、HBuilder 能装插件、Prettier 能格式化、没有安卓手机时知道用云手机验证。下面按课堂演示顺序整理。
一、真机运行失败时先排查环境
为什么需要: 真机运行要在手机上安装 App,手机会弹出授权提示;不同手机型号界面有差异,连接也可能“上午好用、下午不好用”。
怎么想: 先判断是步骤问题还是环境问题。步骤上,看到授权提示必须点“允许”;环境上,按“撤销授权→拔插数据线→关开 USB 调试→重启手机”的顺序排查。
怎么做: 连接成功后,在手机弹窗中勾选允许,让电脑向手机安装 App。若之前授权过但失效,先撤销授权,拔掉数据线重新插上;再次弹出授权就点允许。还不行,把 USB 调试关一下再打开;仍不行就重启手机。苹果手机真机需要证书,课堂不要求购买;没有安卓手机也不影响学习,后面可用云手机验证。
易错点: 不要点拒绝;不要一失败就以为代码错,可能是网络、数据线、手机型号或硬件环境;模拟器配置比真机更复杂,不推荐作为首选。
二、HBuilder 安装插件:以 Prettier 为例
为什么需要: 课堂从 VS Code 转到 HBuilder,很多地方不如 VS Code 顺手,需要装插件提升开发体验;后面云打包也要注册账号。
怎么想: 插件安装方法比某个插件本身更重要。先注册/登录账号,再通过“工具→插件安装”进入插件市场搜索安装。
怎么做: 打开 HBuilder,注册并登录账号;点“工具→插件安装”,可查看已安装插件;需要新插件时点“前往插件市场安装”,搜索 Prettier,登录后点安装,HBuilder 会自动打开并确认安装;安装后在“插件安装”里能看到。
易错点: 不登录账号安装会麻烦;不要只记住 Prettier,要掌握“搜索—安装—查看”的通用步骤;以后装 ESLint 等插件也是同样方式。
三、配置 Prettier 与 HBuilder 基本设置
为什么需要: 团队代码风格要统一:缩进两个还是四个、要不要分号、单引号还是双引号,都应由项目配置决定。
怎么想: 先配 Prettier,再开“保存时自动格式化”。课堂特别提醒:如果一上来就开自动保存/自动格式化,代码写到一半有错时可能立刻报错,容易误判。
怎么做: 打开“工具→设置→插件配置”,给 Prettier 补上生效范围,例如 **/*.js 这类匹配项。然后在项目根目录新建 .prettierrc,写入常用配置:
json
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"vueIndentScriptAndStyle": true
}再到 HBuilder 常用设置里把制表符改为 2,勾选“使用空格代替制表符”,勾选“保存时自动格式化”和“折叠最后一行”。需要手动格式化时右键选择“重排”。
易错点: .prettierrc 必须放在项目根目录;第一次重排若弹窗,要选自己安装的 Prettier,不要选内置;字体项需要本机已安装字体,没装就用默认。
四、云手机与全局文件验证
为什么需要: 没有安卓手机或苹果证书时,仍要看看 App 在手机上的效果;企业测试多机型也不可能每种都买。云手机就是在线连接真手机做测试,免费时长通常有限。
怎么想: 云手机不是模拟器,而是平台提供的真手机;上传打包好的 APK 后进入云手机安装测试。全局文件方面,uni.scss 定义的是全局 Sass 变量,App.vue 的 script 相当于小程序 app.js,style 相当于 app.wxss。
怎么做: 注册云手机账号,上传 APK,进入云手机安装并打开验证。uni.scss 末尾可定义变量:
scss
/* uni.scss 末尾:自定义样式变量 */
$bg-color: pink;页面样式要加 lang="scss" 后才能使用变量:
vue
<style lang="scss">
page {
background-color: $bg-color;
}
</style>App.vue 可写全局样式验证:
vue
<style>
.ts {
color: red;
}
</style>易错点: App.vue 不要写 template;首次用 Sass,HBuilder 会提示安装 sass 插件;服务卡住时可重启服务或重新运行;云手机免费时间用完需付费。
代码示例
示例 1:uni.scss + App.vue(全局变量与全局入口)
css
/* uni.scss —— 全局 SASS 变量,运行时会自动注入到每个页面的样式里 */
/* 在原有 sass 变量基础上添加新的变量 */
$uni-bg-color: #f4f4f4;html
<!-- App.vue -->
<script>
// 相当于小程序的 app.js
export default {
onLaunch: function () {
console.log('App Launch')
},
onShow: function () {
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
},
}
</script>
<!-- 组件模板需要被省略 -->
<template></template>
<!-- 组件模板需要被省略 -->
<style lang="scss">
/* 相当于 app.wxss */
page {
background-color: $uni-bg-color;
}
</style>关键行与易错点
$uni-bg-color定义在uni.scss中即可全局使用,无需在页面里@import,这是 uni-app 运行时自动注入的效果。App.vue里的template必须省略(或写成空标签),它不是根组件,只承担两件事:script对应小程序的app.js(应用级生命周期),style对应小程序的app.wxss(全局样式)。- 使用
$uni-bg-color时,<style>必须加lang="scss",否则变量不生效;首次加该属性 HBuilderX 会提示自动安装dart-sass插件,装完需重新运行。 - 改动
uni.scss后如果颜色没变化,先停掉再重新运行服务(转写稿中演示时正是因为旧服务卡住导致样式未生效)。
示例 2:pages.json(页面配置 + 全局配置)
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "学习uni-app"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "",
"selectedColor": "",
"borderStyle": "",
"position": "",
"list": [
{
"text": "",
"pagePath": "",
"iconPath": "",
"selectedIconPath": ""
}
]
},
"subPackages": [
{
"root": "",
"pages": [
{
"path": "",
"style": {}
}
]
}
],
"uniIdRouter": {}
}关键行与易错点
pages是页面配置:path指定页面路径,style是这一页自己的配置(导航栏标题、背景色等)。globalStyle是全局配置,对应小程序app.json里的window。- 其余全局配置(
tabBar、subPackages等)与globalStyle平级书写,不要嵌套进globalStyle里面——这是最容易写错的地方。 - 重点:
tabBar的图标图片必须放在static目录下,否则小程序端无法运行。 pages数组的第一项就是启动页,新增页面必须先在pages中注册才能访问。
示例 3:页面中的生命周期 + API 调用 + 条件编译
html
<script setup>
// Vue 组件生命周期
import { onMounted } from 'vue'
// 应用/页面生命周期(小程序生命周期)
import { onLoad } from '@dcloudio/uni-app'
// 页面加载后获取接口数据
onLoad(() => {
// 原来的 wx.request 换成 uni.request
uni.request({
url: 'https://your.api.com/xxx',
success: (result) => {
console.log(result)
}
})
})
onMounted(() => {
console.log('组件 mounted')
})
// #ifdef H5
console.log('这段代码只在 H5 端才会运行')
// #endif
</script>
<template>
<!-- #ifdef H5 -->
<view class="iconfont icon-search"></view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="iconfont icon-scan"></view>
<!-- #endif -->
</template>
<style lang="scss" scoped>
.iconfont {
color: #666;
font-size: 30rpx;
/* #ifdef H5 */
font-size: 28rpx;
/* #endif */
}
</style>关键行与易错点
- 生命周期分工原则:应用级(
onLaunch/onShow/onHide)和页面级(onLoad/onShow/onHide/onReady)用小程序的生命周期,组件级用 Vue 生命周期。setup 语法下页面/应用生命周期要从@dcloudio/uni-app导入,不能从vue导入。 - API 命名空间统一换成
uni:wx.request→uni.request,其余用法一致。 - Promise 规则:调用时传了
success/fail/complete任一回调就是 callback 方式、不返回 Promise;一个都不传才返回 Promise,可配合async/await。 - 条件编译是特殊注释,
#ifdef 平台名表示只在某平台编译,#ifndef表示排除某平台;template、script、style三处写法不同(HTML 用<!-- -->,JS/CSS 用/* */)。 - 常见平台名:
H5、MP-WEIXIN、MP-ALIPAY、APP-PLUS、APP-ANDROID、APP-IOS、VUE3;多个平台用||连接。
常见错误
- 现象:
tabBar配置后小程序端不显示或直接运行失败 → 原因:iconPath、selectedIconPath图片未放在static目录 → 修法:把 tabBar 图标统一移到static下,并修正pages.json中的路径。 - 现象:
App.vue编译报错或页面结构异常 → 原因:在App.vue中写了<template>→ 修法:删除template,只保留script和style。 - 现象:
pages.json中tabBar、subPackages不生效 → 原因:误把这些全局配置写进globalStyle内部 → 修法:将tabBar、subPackages与globalStyle平级配置。 - 现象:页面里写
onLaunch不执行,或组件里写onLoad不执行 → 原因:应用/页面生命周期与 Vue 组件生命周期混用 → 修法:应用生命周期放App.vue,页面生命周期放页面,组件用 Vue 生命周期;setup中页面生命周期从@dcloudio/uni-app导入。 - 现象:调用
uni.request后await拿不到预期结果 → 原因:同时传了success/fail/complete又按 Promise 方式使用 → 修法:二选一;要await就不要传回调,要回调就不要按 Promise 取返回值。 - 现象:条件编译代码在错误平台执行或完全不生效 → 原因:平台名写错、注释格式不规范、
#ifdef/#ifndef未闭合 → 修法:核对平台名称,按// #ifdef H5、// #endif规范书写,并检查成对闭合。 - 现象:
uni.scss中变量在页面样式里不可用 → 原因:style未声明lang="scss",或 Sass 插件未安装完成 → 修法:给对应style加lang="scss",按提示安装dart-sass后重启服务验证。 - 现象:HBuilderX 中右键“重排”后格式化结果不符合预期 → 原因:首次弹窗选择了内置格式化器,或 Prettier 未按项目配置生效 → 修法:弹窗选择已安装的 Prettier;确认项目根目录存在 Prettier 配置文件,并检查插件配置中的生效范围。
练习
- 在
uni.scss中定义一个全局颜色变量,并在首页样式中通过lang="scss"使用它,验证变量是否全局生效。 - 配置一个包含 2 个页面的
tabBar,图标放到static目录,分别验证微信小程序端和 H5 端是否正常显示。 - 在
App.vue中监听应用生命周期,在首页监听页面生命周期,在自定义组件中监听 Vue 生命周期,并说明各自执行时机。 - 使用
uni.request分别写出 callback 写法和 Promise/async-await写法,并验证“传回调不返回 Promise、不传回调返回 Promise”的差异。 - 使用条件编译,让同一段模板在 H5 显示搜索图标、非 H5 显示扫码图标,并检查编译结果。
课堂画面






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 真机环境问题总结 | 07:38 |
| 2 | HBuilder安装插件 | 08:24 |
| 3 | HBuilder插件配置 | 10:11 |
| 4 | HBuilder基本配置 | 06:42 |
| 5 | 答疑和HBuilder动手机测试 | 09:57 |
| 6 | uni-app全局文件-uni.scss | 10:09 |
| 7 | uni-app全局文件-App.vue | 07:40 |
小结
uni.scss、App.vue、pages.json是全局能力入口,但职责边界要分清,不能混写。App.vue不是普通根组件,template必须省略;应用生命周期和全局样式都从这里入手。pages.json中只有globalStyle是全局窗口配置,其他全局配置要与它平级。- 生命周期先按“应用/页面用小程序、组件用 Vue”判断,再考虑
setup中的导入来源。 uni.request的 callback 与 Promise 是互斥使用方式,混用会导致返回值判断错误。- 条件编译要严格依赖平台名称和注释语法,平台差异代码应尽量局部化。
- HBuilderX 的插件安装、Prettier 配置、保存自动格式化有顺序要求,先配格式化再开自动保存可减少误报。