主题
组件库、生命周期与 pinia(一)
本页对应 组件库、生命周期与 pinia,共 7 段视频,约 1 小时 21 分。
学习目标
- 能在 HBuilderX 中创建 Vue3 版 uni-app 项目,完成 uViewPlus 的安装与四项核心配置,并通过按钮、日历组件验证组件库生效。
- 能区分 uni-app 中应用、页面、组件三类生命周期,并按“应用/页面用小程序生命周期、组件用 Vue 生命周期”的原则正确选用。
- 能在 Vue3 组合式 API 中从
@dcloudio/uni-app引入onLoad、onShow等页面生命周期,并运行验证其执行结果。 - 能使用
uni.request等 uni-app API,说明命名空间替换规则,并验证传入success/fail回调时不返回 Promise、不传回调时返回 Promise 的行为差异。 - 能使用条件编译特殊注释,在 JS、CSS、模板中按 H5、小程序、App 等平台包含或排除代码,并解释其解决跨端兼容问题的作用。
- 能规范克隆并导入神领物流项目,在 HBuilderX 中运行 H5 端,说明 task、user、message 三个分包及任务、消息、我的模块的主要业务流转。
知识点
本节目标与边界
本节的任务是把“神领物流”司机端的静态页面拿到本地,导入 HBuilderX 并跑通 H5,同时认识项目结构和业务主流程。转写稿中重点展开的是拉取代码、导入启动、分包与业务介绍;网络请求封装和公共方法封装在本节视频里没有展开,本文不虚构,后续随项目开发再补。
为什么需要:先有可运行基线,后面写页面、调接口才有对照。怎么想:先跑通,再读结构,最后改代码。怎么做:固定目录克隆、导入、运行 H5、课下再验小程序和 App。易错点:项目名和目录乱放,后面自己都不知道运行的是哪一份。
拉取代码:目录要规整
为什么需要:从今天起连续几天都做同一个“神领物流”,如果桌面、下载目录到处克隆同名项目,排错时很难判断当前运行的是哪份代码。怎么想:把项目当成接下来几天的唯一工作副本,放在固定、整齐的目录里。怎么做:在目标目录打开终端,粘贴讲师提供的仓库地址克隆:
bash
# 在固定目录打开终端,粘贴讲师提供的仓库地址并克隆易错点:不要今天克隆一份、明天再克隆一份;不要放桌面或随手目录。转写稿特别强调“名字都叫神领物流”时最容易混乱。
导入 HBuilderX 与启动
为什么需要:代码拉下来只是文件,必须导入开发工具才能运行和调试。怎么想:拖拽和“导入本地”都可以,关键是路径清楚。怎么做:打开 HBuilderX,可把项目目录拖进去,也可点“导入→本地”选择目录。课堂先跑 H5,并切到手机模式;课下再跑小程序端和 App 端验证。易错点:从列表移除不是删除文件,别误点删除;控制台里小图标 404 可先忽略,转写稿说明它不影响主流程。
项目结构:uni-ui、三分包与入口文件
为什么需要:先认路,后面才知道页面、样式、分包往哪放。怎么想:这个项目已经选用 uni-ui,不再重复整合 uView Plus。转写稿演示 uView Plus 时是“改四行代码、装两个依赖”,但神领物流不要照搬,否则容易整合乱。怎么做:展开目录,重点看 App.vue、main.js、uni.scss、static 字体图标,以及 task、user、message 三个分包,分别对应任务、我的、消息模块。易错点:不要把演示项目的组件库配置硬塞进本项目;main.js 里有 Vue3 条件编译判断,读代码时要能看懂。
业务主流程:任务、消息、我的
为什么需要:不理解业务,页面状态变化就看不懂。怎么想:司机端核心是“接任务→提货→运输→交付”。怎么做:任务详情里可延迟提货或提货;延迟要填原因,提货要填凭证、上传照片;运输中可能异常上报,最后交付,任务进入已完成。消息模块含任务通知和公告;我的模块展示个人信息、车牌、里程等,系统设置、换手机号、换密码、清缓存等页面暂时没有接口,是摆设。易错点:不要纠结无接口页面,先跑通主流程。
条件编译:跨端兼容要会读
为什么需要:uni-app 号称一套代码多端运行,但 H5 和小程序能力不同,比如扫码在 H5 基本做不到,小程序和 App 可以。怎么想:需要按平台保留或屏蔽代码时,用条件编译。怎么做:JS、CSS、模板里都用特殊注释:#ifdef H5 到 #endif 表示仅 H5 生效;#ifndef H5 表示非 H5 生效。项目里任务样式有 H5 高度判断,main.js 有 Vue3 判断。易错点:CSS 注释是 /* */,不是 //;开始和结束必须配对;条件表达式很多,不必背全,会查文档即可。
读代码前的基础结论
为什么需要:项目里页面和组件混用生命周期与 API。怎么想:应用、页面优先用小程序生命周期;自定义组件用 Vue 生命周期。API 统一从微信的 wx.xxx 换成 uni.xxx。怎么做:页面里写 onLoad、onShow;组件里写 created、mounted。调 uni.request 想要 Promise 时,不要传 success、fail、complete。易错点:在 Vue 组合式 API 里直接用小程序生命周期要按 @dcloudio/uni-app 引入;传了回调函数就不会按 Promise 返回。
收尾验证
为什么需要:没跑起来,下午写代码会掉队。怎么想:先 H5 验证,再课下补小程序和 App。怎么做:运行到浏览器,切手机模式,点进任务详情看页面跳转;改一处条件编译样式或文案,确认热更新生效。易错点:只导入不运行、只跑一个端就认为全通,都是本节常见问题。
登录状态是神领物流接口的前置条件
为什么需要:本节笔记开宗明义:神领物流所有接口的调用都必须是登录状态才可以。也就是说,登录不是孤立页面,而是后续任务、消息、我的等模块的前置条件。没有登录状态,后续请求没有统一凭证,业务无法展开。
怎么想:登录成功后第一件事不是只做页面跳转,而是把登录凭证 token 记录下来,并让所有页面、所有请求都能拿到它。token 属于跨页面共享状态,不能只放在某个页面的局部变量里。
怎么做:登录成功后把 token 写入统一的状态管理;请求封装时统一读取 token;未登录或 token 失效时回到登录流程。转写稿中项目叫“神领物流”,不是“申领物流/森林物流”,统一纠正。
易错点:只在当前页面存 token,刷新或切页丢失;多个页面各自存一份导致不一致;把 token 当用户信息;只做跳转不记录登录态。
为什么选 Pinia:Vue3 下的状态管理选型
为什么需要:笔记指出,用户登录后需要记录登录状态信息;Vue2 项目用 Vuex3,Vue3 项目需要用 Vuex4 或 Pinia。选型不解决,token 就没有稳定落点。
怎么想:Pinia 是 Vue 专属状态管理库,是 Vuex 的替代品。它的特点包括:更简单的 API(去掉 mutation)、组合式风格 API、去掉 modules 概念且每个 store 独立模块、配合 TypeScript 更友好并提供可靠类型推断。对登录 token 这种跨页面共享状态,独立 user/auth store 更清晰。
怎么做:Vue3 项目优先按笔记选 Pinia;按业务拆 store,登录相关只放 token 和必要用户标识;组件按需引入,不把所有业务塞进一个 store。
易错点:仍按 Vuex 的 mutation 思路写;把 Pinia 当普通全局变量乱放;store 里塞不可序列化大对象;Vuex4 与 Pinia 混用造成维护混乱。
Pinia 特点逐条落地
为什么需要:理解特点是为了避免设计错。笔记列出的四条不是背诵项,而是设计约束。
怎么想:API 更简单,意味着少样板代码;组合式风格,意味着和 setup 写法一致;无 modules,意味着按文件拆独立 store;TS 友好,意味着 token 类型、用户标识类型应明确。
怎么做:登录 store 只暴露 token、设置登录态、清除登录态等方法;页面只调用方法,不直接改内部状态;请求层从 store 取 token。
易错点:以为“无 modules”就不用拆分;在 store 里直接发登录请求导致状态与请求耦合;退出登录忘记清除 token;类型写成 any。
安装 Pinia:保留笔记代码块,不臆补命令
为什么需要:使用 Pinia 前要安装。但本节笔记在“2.1.1 安装 Pinia”后给出的代码块是空的,没有具体安装命令,不能擅自编造。
怎么想:先看项目 package.json 和锁文件是否已含 Pinia;若没有,再按团队使用的包管理器安装;安装后按项目脚手架实际方式注册。笔记未给注册代码,本节不展开臆写。
怎么做:保留笔记原代码块如下:
bash
易错点:盲目复制网上命令导致版本不一致;装完不重启开发服务;只验证 H5 不验证小程序/App;把空代码块当成可执行命令。
结合项目:登录模块在司机端的落点与调试
为什么需要:转写稿介绍神领物流司机端有三个分包:task、user、message,对应任务、我的、消息模块。任务列表、消息通知、个人信息等接口都要登录后才能拉取。
怎么想:先跑通 H5 验证项目,再确认三个分包;登录后 token 供三个分包共用;H5 与小程序/App 存在差异,例如转写稿中扫码在 H5 做不到,需要条件编译。
怎么做:把代码克隆到规整目录,用 HBuilderX 导入并跑 H5;登录后请求任务列表观察是否携带 token;若用 uni.request 并 await,不要同时传 success 回调——转写稿验证过,传入 success 后不会返回 Promise。
易错点:代码克隆多份找不到当前项目;只跑 H5 不验证小程序/App;await 与 success 混用;H5 强行做扫码;token 未带导致接口无数据。
代码示例
示例一:页面生命周期 + 条件编译(index.vue)
vue
<template>
<view class="content">
<text class="title">{{ title }}</text>
<!-- #ifdef H5 -->
<button>只有 H5 端才会渲染这个按钮</button>
<!-- #endif -->
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
const title = ref('首页')
// 页面级生命周期:用小程序的生命周期,由 uni-app 提供
onLoad(() => {
console.log('页面加载了...')
})
onShow(() => {
console.log('页面显示了...')
})
// #ifdef H5
console.log('当前是 H5 环境')
// #endif
// #ifndef H5
console.log('当前不是 H5 环境')
// #endif
</script>
<style lang="scss">
/* #ifdef H5 */
.content {
height: 100vh;
background-color: red;
}
/* #endif */
/* #ifndef H5 */
.content {
background-color: #fff;
}
/* #endif */
</style>要点解释 / 易错点
- 页面里不要写
onMounted代替onLoad:onLoad / onShow / onReady是小程序页面生命周期,vue里根本没有,必须从@dcloudio/uni-app引入;直接从vue引入onLoad会拿不到、也不会执行。 - 选哪个生命周期,看你在写什么:
App.vue是应用级 →onLaunch / onShow / onHide;页面 →onLoad / onShow / onReady;自定义组件 → 用 vue 的onMounted / onCreated等。 - 条件编译是“特殊的注释”:必须
#ifdef开头、#endif结尾,成对出现,少写一个#endif后面的样式会整片失效(转写稿里演示时就踩了这个坑)。 - 注释符号跟着语言走:JS 用
//,CSS 用/* */(不是//),模板用<!-- -->;#ifndef是“不是”,别和#ifdef混。 title用ref定义,否则模板里使用未定义变量控制台会报警告。
示例二:uni.request 返回 Promise 的写法
js
// 页面中发起网络请求
uni.request({
url: 'http://your-host/api/task/list' // 神领物流接口需登录态才返回数据
}).then(res => {
console.log(res)
})
// 错误示范:传了 success 回调,就没有 Promise 了
uni.request({
url: 'http://your-host/api/task/list',
success: (res) => {
console.log(123)
}
}).then(res => {
console.log(res) // 这里不会执行,且会报错
})要点解释 / 易错点
- 命名空间换成
uni:原来是wx.request / wx.showLoading,在 uni-app 里统一换成uni.request / uni.showLoading,用法完全一致。 - 只有“不传回调”才返回 Promise:uni-app 里所有 API 都能返回 Promise,但一旦传入了
success/fail/complete,就不会返回 Promise,此时再.then就会报错——这是最常踩的坑。 - 原生小程序里并不是所有 API 都返回 Promise,uni-app 做了统一,但“传回调即失去 Promise”这一点仍然成立。
- 神领物流的接口必须登录后才能拿到数据,未登录时
.then里能执行但返回的是空数据,别误判成请求失败。
示例三:uview-plus 组件库整合(main.js / uni.scss / App.vue)
js
// main.js
import App from './App'
import { createSSRApp } from 'vue'
import uviewPlus from '@/uni_modules/uview-plus'
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
return { app }
}scss
/* uni.scss:把 uview-plus 的变量文件引入,全局生效 */
@import '@/uni_modules/uview-plus/theme.scss';vue
<!-- App.vue -->
<script>
export default {
onLaunch: function () {},
onShow: function () {},
onHide: function () {}
}
</script>
<style lang="scss">
/* App.vue 的 style 相当于全局样式,这里引入组件库样式 */
@import '@/uni_modules/uview-plus/index.scss';
</style>bash
# 组件库依赖的库(转写稿:倒计时/时间用到 dayjs,另一个按文档一并安装)
npm install dayjs要点解释 / 易错点
- 总共只加了 4 行代码:
main.js两行(import uviewPlus+app.use(uviewPlus)),uni.scss一行变量文件,App.vue一行全局样式——其余都是项目模板自带的。 createSSRApp不是createApp:uni-app 的main.js模板本身就是createSSRApp,别按网页端写法改掉。App.vue的<style>相当于App.wxss(全局样式),<script>相当于App.js;它里面不能写 template,所以必须加lang="scss"才能让 scss 生效。- SCSS 已装可跳过:用 HBuilderX 创建的项目已内置 sass 编译,不必重复安装;装完依赖记得重启 HBuilderX。
- 使用方式:
<u-button type="success" :loading="true" loading-text="加载中">按钮</u-button>、<u-calendar :show="true"></u-calendar>,type取值primary / success / info / warning / error。
常见错误
1. uView Plus 装完,组件能渲染但样式全丢 / 控制台报 Sass 变量未定义 现象:<u-button> 有 DOM 没样式,或编译报 Undefined variable: $u-primary。 原因:只装了 npm 包,四步接入没做全——main.js 里 import uviewPlus 后没 app.use(uviewPlus);uni.scss 没 @import 'uview-plus/theme.scss';App.vue 没引入 uview-plus/index.scss 或引入时漏了 lang="scss"。 修法:把四行代码逐条对回去,App.vue 的 <style> 必须带 lang="scss";改完重启 HBuilderX(不是刷新浏览器),否则新依赖不参与编译。
2. 组合式 API 里写 onLoad 直接报 onLoad is not defined 现象:<script setup> 中写 onLoad(() => {}),编译或运行时报未定义。 原因:onLoad / onShow / onReady 是小程序页面生命周期,Vue 不提供,import { onLoad } from 'vue' 必然拿不到。 修法:从 @dcloudio/uni-app 引入。顺带一条判据:凡是从 vue 能引到的(onMounted、onUnmounted)属于组件级;页面级、应用级的都必须走 uni-app 的包。
3. 生命周期写对了语法,但就是不执行 现象:App.vue 里写 onLoad 没反应;页面里写 onLaunch 没反应;自定义组件里写 onShow 没反应。 原因:应用级、页面级、组件级三类生命周期串了位置。文件是"什么身份",就只能用对应那一类。 修法:App.vue 只放 onLaunch / onShow / onHide;pages 下的页面放 onLoad / onShow / onReady / onHide;components 下的组件一律用 Vue 的 created / mounted / unmounted。写之前先问自己一句"这文件是应用、页面还是组件"。
4. uni.request(...).then() 拿到 undefined,或 success 里代码不执行 现象:加了 .then 但回调不触发;或者反过来,写了 .then 又填了 success,两边只活一个。 原因:传了 success / fail / complete 任一回调,就不再返回 Promise;两者是互斥的。 修法:二选一。要 await / then 就一个回调都别传;要回调风格就别接 .then。注意 uni.request 原生不支持 Promise,这个坑在网络请求里出现频率最高。
5. 条件编译写了等于没写,或整段代码凭空消失 现象:// #ifdef H5 里的代码在 H5 下没生效;或某段模板/样式编译后不见了。 原因:格式错——漏了 #、用了 // 注释却写在 CSS 里(CSS 必须 /* #ifdef H5 */)、平台标识小写 h5(应为 H5)、漏写 #endif、模板中把条件编译写进了标签属性内部而不是包在标签外层。 修法:JS 用 //,CSS 用 /* */,模板用 <!-- -->,三者前缀都是 #ifdef / #ifndef,收尾必须 #endif;平台标识大小写严格。养成"先写 #ifdef 紧接着写 #endif,再往中间填内容"的习惯,能避免九成的丢代码问题。
6. 明明改了代码,页面跑的还是老逻辑 现象:改完保存,行为不变;或打开的是上一个版本的项目。 原因:一是 HBuilderX 缓存 / 依赖未重装(尤其装完 uView Plus、dayjs 之后);二是本地克隆了多份同名项目,运行的压根不是当前这份。 修法:先重启 HBuilderX 再判断;项目目录固定一处、命名带版本意识,从列表清理旧项目时用"移除"而不是"删除"。
练习
接入自检:新建空白 uni-app(Vue3)项目,完整接入 uView Plus,在首页放一个
u-button(type="success"+loading)和一个u-calendar(show为 true),改一次按钮主题色验证uni.scss变量确实全局生效。全程记录你改动了几处文件、几行代码。生命周期归位:在
App.vue、pages/index/index.vue、components/demo.vue三个文件里各打三条console.log,分别用应用级、页面级、组件级生命周期,运行后对照控制台顺序,画出"打开首页 → 切后台 → 回前台"的完整执行序列。同一功能两种写法:把练习 2 中首页的
onLoad、onShow改写为<script setup>组合式 API 版本,对比选项式与组合式在引入来源、调用方式上的差异,写三行结论。Promise 二选一:调用同一个接口写两版——
async/await版(不传任何回调)和success回调版,各自打印返回值,验证"传回调则无 Promise"这条结论;再故意两个都写,观察报错信息。条件编译三处落地:在 JS、CSS、模板三处各写一组
#ifdef H5/#ifndef H5,H5 与微信小程序各跑一次,比对三处输出;再模拟"扫码按钮仅非 H5 显示"这一真实场景,用条件编译实现。
课堂画面






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | uni-app中使用uViewPlus | 15:26 |
| 2 | uni-app中生命周期的用法-1 | 11:52 |
| 3 | uni-app中生命周期的用法-2 | 10:05 |
| 4 | uni-app中API的调用 | 09:33 |
| 5 | uni-app中的条件编译 | 15:24 |
| 6 | 神领物流-拉取代码 | 07:29 |
| 7 | 神领物流-业务及代码情况介绍 | 11:34 |
小结
- uView Plus 的接入成本不在"会不会用组件",而在四行代码 + 两个依赖是否齐全;漏任何一处,症状都是"样式没了"而不是"报错",排查要按清单走,不要凭感觉。
- 装完依赖后重启 HBuilderX 是必要动作,不重启就别怀疑代码写错。
- 生命周期只有一条判断链:先认文件身份(应用 / 页面 / 组件),再选生命周期家族(小程序 / 小程序 / Vue),最后才选写法(选项式 / 组合式)。顺序颠倒必错。
- 组合式 API 下,Vue 能引的归 Vue,页面级与应用级的归
@dcloudio/uni-app——这条边界比记具体函数名更重要。 uni.request的 Promise 化是"有回调就没 Promise"的二选一,不是"都能用";这条同样适用于绝大多数 uni API。- 条件编译本质是编译期剪枝,不是运行时判断——写错了不会报错,只会静默丢代码,因此格式(注释符、
#、大小写、#endif配对)必须一次到位。 - 跨端从来不是"一套代码零成本",
getUserInfo、扫码这类能力本身就端端不同;条件编译就是官方给的兼容出口,遇到平台差异第一反应应该是它。 - 项目目录规整不是洁癖:多份同名克隆导致的"改了不生效",排查成本远高于当初多花十秒选目录。