📌 本文适合谁? 你会写 HTML/CSS/JS,但第一次接触 uni-app,对 Vue、小程序、跨端开发一无所知。本文基于 uni-app 官方文档「白话 uni-app」 深度梳理,用大白话帮你完成从"传统网页开发者"到"跨端开发者"的认知升级。
📌 阅读收获: 搞清楚 uni-app 和传统 H5 到底哪里不一样、为什么不一样、怎么写才对。所有知识点均严格依据官方文档整理,确保权威、无坑。
一、先搞清楚:uni-app 到底是什么?
一句话:uni-app 是一个让你"写一次代码,跑遍所有平台"的跨端开发框架。
你写一套代码,它能帮你编译出:
- ✅ iOS App
- ✅ Android App
- ✅ 鸿蒙 App
- ✅ H5 网页
- ✅ 微信/支付宝/百度/抖音/QQ/快手/飞书/钉钉等各家小程序
- ✅ 快应用
💡 类比理解: 你可以把 uni-app 想象成一个"万能翻译官"。你用"中文"(Vue 语法)写好一篇稿子,它帮你翻译成英文、日文、法文......每个平台都能读懂。
但注意------翻译官不是万能的 。你写的"中文"必须符合它的语法规则,否则翻译出来的东西就会出错。所以,如果你之前写的是传统网页(H5),你会发现 uni-app 的写法和以前有很大不同。
这篇文章,就是帮你把"旧知识"平滑迁移到"新世界"。
二、网络模型变了:从"服务端渲染"到"前后端分离"
2.1 传统 H5 是怎么工作的?
早期的网页开发,后端(服务器)把数据直接塞进 HTML 里,然后整体返回给浏览器。用户看到的就是一个"已经填好内容的网页"。
这种模式叫做 B/S 模式(Browser/Server,浏览器/服务器模式)。
📖 什么是 B/S 模式?
B/S 即 Browser/Server(浏览器/服务器)架构。用户只需要一个浏览器,所有业务逻辑和数据都在服务器端处理,浏览器只负责"展示"。与之对应的是 C/S 模式(Client/Server,客户端/服务器),比如你电脑上的 QQ、微信桌面版,需要安装专门的客户端软件。
简单说:B/S = 打开浏览器就能用;C/S = 必须装个软件才能用。
在传统 B/S 模式下,后端使用 JSP、PHP、ASP.NET 等技术,把数据"渲染"到 HTML 中再返回。前端和后端代码是混在一起的。
2.2 现在 uni-app 是怎么工作的?
uni-app 采用的是前后端分离架构:
- 前端(你写的代码): 负责画界面、处理交互
- 后端(服务器): 只提供数据接口(通常返回 JSON 格式的数据)
- 两者通过 HTTP 请求通信
页面加载的流程变成了:
markdown
1. 前端先渲染一个"空壳子"页面
2. 页面加载后,JS 发起网络请求(uni.request)
3. 后端返回 JSON 数据
4. 前端拿到数据,填充到页面上
📖 什么是 JSON?
JSON(JavaScript Object Notation)是一种轻量级的数据格式,长这样:
{"name": "张三", "age": 25}。它是目前前后端数据交换的"通用语言"。
📖 什么是 HTTP 请求?就是浏览器/客户端向服务器"要数据"的动作。你在浏览器地址栏输入网址回车,本质就是发了一次 HTTP 请求。在代码中,我们用
uni.request()来手动发起请求。
🍔 打个比方:以前是餐厅把菜直接端到你桌上(服务端渲染好再给你);现在是餐厅给你一份空盘子和菜单,你自己去吧台取菜(前端主动请求数据,再自己填到页面上)。
2.3 代码层面怎么用?
javascript
// 在 uni-app 中发起网络请求
uni.request({
url: 'https://api.example.com/user/list', // 后端接口地址
method: 'GET',
success: (res) => {
// res.data 就是后端返回的 JSON 数据
this.userList = res.data;
}
});
⚠️ 注意: 这里用的是
uni.request(),不是以前你熟悉的$.ajax()(jQuery)或fetch()。在 uni-app 中,所有 API 都以uni.开头,这是为了跨平台兼容。后面会详细讲。
三、文件类型变了:从 .html 到 .vue
3.1 你写的文件不再是 HTML 了
| 对比项 | 传统 H5 | uni-app |
|---|---|---|
| 文件后缀 | .html |
.vue |
| 开发时写的 | HTML 标签 | Vue 模板语法 |
| 运行时实际是什么 | HTML 直接运行 | 经过编译器转换成各平台代码 |
3.2 什么是"编译器"和"运行时"?
这是理解 uni-app 的核心概念:
📖 什么是编译器?
把你写的
.vue源代码,"翻译"成各个平台能识别的代码。比如编译到微信小程序,就生成wxml + wxss + js;编译到 H5,就生成html + css + js。你写一次,它翻译多次。
📖 什么是运行时(Runtime)?编译后的代码在设备上实际执行时,需要一套"运行环境"来支撑。比如 Vue 的响应式系统、组件生命周期管理等,都属于运行时的范畴。
🍔 类比: 编译器像"翻译官",把你的中文稿翻译成各国语言;运行时像"同声传译设备",确保翻译出来的内容能被正确"播放"。
四、文件内部结构变了:从"一栋楼"到"三个房间"
4.1 以前的 HTML 文件
xml
<!DOCTYPE html>
<html>
<head>
<script src="js/jquery.js"></script>
<style>
body { background: #fff; }
</style>
</head>
<body>
<div id="app">页面内容</div>
<script>
// JS 逻辑
</script>
</body>
</html>
所有东西------结构、样式、逻辑------都塞在一个 <html> 大壳子里,混在一起。
4.2 现在的 Vue 单文件组件(SFC)
📖 什么是 SFC?
SFC 是 Single File Component 的缩写,即"单文件组件"。它是 Vue 的核心概念:一个
.vue文件 = 一个组件 = 一个功能模块 。每个.vue文件包含三个顶级代码块:<template>、<script>、<style>。
xml
<template>
<!-- ⚠️ 必须有一个根元素(如 view),且只能有一个! -->
<view class="container">
<text>{{ message }}</text>
<button @click="changeText">点我修改文字</button>
</view>
</template>
<script>
export default {
data() {
return {
message: '你好,uni-app!'
};
},
methods: {
changeText() {
this.message = '文字被修改了!';
}
}
}
</script>
<style scoped>
/* scoped 表示样式只作用于当前组件,不会污染其他页面 */
.container {
padding: 20rpx;
}
</style>
🏠 类比:
以前是"大开间"------客厅、卧室、厨房全在一个空间里,互相干扰。
现在是"三室一厅"------
<template>是客厅(放界面结构)、<script>是书房(放业务逻辑)、<style>是衣帽间(放样式),各司其职,互不干扰。
4.3 三个块的职责(必须记牢)
| 代码块 | 作用 | 类比 |
|---|---|---|
<template> |
定义页面长什么样(结构) | 房子的户型图 |
<script> |
定义页面怎么动(逻辑) | 房子里的电路系统 |
<style> |
定义页面好不好看(样式) | 房子的装修 |
4.4 几个关键规则
<template>内必须有一个根元素 ,且只能有一个。通常用<view>包裹。<script>中必须export default {},这是 ES6 模块语法,表示"导出这个组件"。<style>中建议加scoped,防止样式泄漏到其他组件。
📖 什么是 ES6 模块和
export default?ES6(ECMAScript 2015)是 JavaScript 的一次重大升级,引入了模块化语法。
export default的意思是"把这个对象作为默认导出",别的文件通过import引入后就能使用。你可以理解为"这个文件对外提供的产品"。
五、外部文件的引用方式变了
5.1 引入 JS 文件
以前:
xml
<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>
现在:
javascript
// 方式一:CommonJS 规范(require)
const util = require('@/common/util.js');
const result = util.formatTime(new Date());
// 方式二:ES6 模块规范(import)------ 推荐
import { formatTime } from '@/common/util.js';
const result = formatTime(new Date());
📖
@符号是什么?在 uni-app 中,
@代表项目的根目录 。@/common/util.js就等于"项目根目录下的 common 文件夹里的 util.js"。这样写路径,无论文件被嵌套多深,都不会出错。
📖require和import的区别?两者都是"引入外部模块"的语法。
require是 CommonJS 规范(Node.js 风格),import是 ES6 模块规范。在 uni-app 中推荐使用import,因为编译器对 ES6 模块的支持更好,且支持 Tree Shaking(自动去除未使用的代码,减小包体积)。
5.2 引入 CSS 文件
以前:
ini
<link href="css/bootstrap.css" rel="stylesheet" />
现在:
xml
<style>
@import "@/common/uni.css";
</style>
⚠️ 注意:
@import语句必须写在<style>标签内的最顶部 ,后面要加分号;。
5.3 全局样式写在哪?
项目根目录有一个 App.vue 文件,它是整个应用的"入口组件"。
xml
<!-- App.vue -->
<script>
export default {
onLaunch() {
console.log('App 启动了');
}
}
</script>
<style>
/* 这里写的样式是全局的,所有页面都会生效 */
page {
background-color: #f5f5f5;
}
</style>
⚠️ 重要:
App.vue中没有<template>!它不定义任何界面,只负责全局逻辑和全局样式。页面的 UI 由pages/目录下的各个.vue文件定义。
5.4 组件引入(重点!)------easycom 机制详解
📖 什么是"组件"?
组件就是把一段"界面 + 逻辑 + 样式"打包成一个可复用的模块。比如你做了一个"评分星星"的 UI,封装成组件后,任何页面都能直接使用,不用重复写代码。
uni-app 提供了 easycom 机制,极大简化了组件引入。
传统方式(需要手动 import + 注册):
xml
<script>
import MyComponent from '@/components/my-component.vue';
export default {
components: {
MyComponent // 注册组件
}
}
</script>
<template>
<my-component />
</template>
easycom 方式(零配置,直接用):
📖 什么是 easycom?
easycom 是 uni-app 提供的组件自动导入机制 。它让你无需手动 import,无需手动注册,只要在模板中写组件标签名,编译器就会自动找到并加载组件。翻译过来就是"轻松使用组件"。
核心路径规则(必须严格遵守):
components/组件名称/组件名称.vue
也就是说:文件夹名和文件名必须完全一致,且都是小写加连字符(kebab-case)风格。
✅ 正确示例:
bash
components/my-button/my-button.vue → 模板中用 <my-button />
components/user-card/user-card.vue → 模板中用 <user-card />
components/uni-list/uni-list.vue → 模板中用 <uni-list />
❌ 错误示例(不会生效):
css
components/MyButton.vue ← 缺少同名文件夹,❌
components/my-button/index.vue ← 文件名不是 my-button.vue,❌
components/my-button/MyButton.vue ← 大小写不匹配,❌
📖 补充规则: 如果组件安装在
uni_modules目录下,easycom 同样支持自动识别,路径格式为:
bashuni_modules/插件id/components/组件名称/组件名称.vue例如你通过插件市场安装了
uni-ui,它会自动注册uni-badge、uni-card等组件,你直接在模板中写标签名就能用。
为什么传统 Vue 开发者会困惑?
如果你之前用 Vue 开发过 Web 项目,你一定习惯了 import + components 注册。第一次用 uni-app 时,看到别人模板里直接写 <uni-list> 而没有任何 import,会本能地觉得"这不可能生效"。
答案就是 easycom。 编译器在编译阶段会扫描 components/ 和 uni_modules/ 目录,自动完成 import 和注册。你只需要保证路径符合规范即可。
💡 记忆口诀: "同名文件夹 + 同名文件 = 直接用"。
六、标签大换血:HTML 标签 → uni-app 组件
这是最直观、最影响日常编码的变化。你熟悉的 HTML 标签,在 uni-app 中要换成对应的组件。
6.1 为什么不能直接用 HTML 标签?
因为 uni-app 要跨平台。<div>、<span> 这些标签只有浏览器认识,微信小程序不认识、原生 App 也不认识。所以 uni-app 定义了一套跨平台通用的组件标签,由编译器负责翻译成各平台的原生写法。
📖 标签 vs 组件,有什么区别?
- 标签(Tag): 浏览器"出厂自带"的,数量有限且固定,如
<div>、<p>、<img>。- 组件(Component): 开发者可以自由封装和扩展的 UI 模块,就像函数一样,把一段 UI + 逻辑打包起来反复使用。组件的标签名是自定义的,如
<my-button>、<user-card>。
6.2 核心标签对照表(必背)
| 传统 HTML 标签 | uni-app 组件 | 说明 |
|---|---|---|
<div> |
<view> |
最基础的容器组件,相当于一个"盒子" |
<span> / <p> / <font> |
<text> |
文本展示 |
<a href="..."> |
<navigator url="..."> |
页面跳转 |
<img src="..."> |
<image src="..."> |
图片展示 |
<select> |
<picker> |
下拉选择器 |
<iframe> |
<web-view> |
嵌入外部网页 |
<ul> / <li> / <ol> |
❌ 不存在 | 用 <view> 嵌套实现列表 |
<input type="radio"> |
<radio> / <radio-group> |
单选 |
<input type="checkbox"> |
<checkbox> / <checkbox-group> |
多选 |
<input type="date"> |
<picker mode="date"> |
日期选择 |
<audio> |
不推荐用标签 | 改用 uni.createInnerAudioContext() API |
6.3 uni-app 独有的"移动端"组件
这些是传统网页没有的,专为移动端场景设计:
| 组件 | 用途 | 使用场景 |
|---|---|---|
<scroll-view> |
可滚动区域 | 列表、长内容区域 |
<swiper> + <swiper-item> |
轮播图 | 首页 Banner |
<switch> |
开关 | 设置页"开启通知" |
<slider> |
滑块 | 音量调节、亮度调节 |
<progress> |
进度条 | 下载进度、加载进度 |
<camera> |
相机 | 拍照、扫码 |
<map> |
地图 | 位置展示、导航 |
<video> |
视频播放器 | 视频播放 |
<cover-view> / <cover-image> |
覆盖层 | 覆盖在 <video>、<map> 等原生组件上方 |
⚠️ 关于
<cover-view>: 在小程序端,<video>、<map>、<camera>是原生组件 ,层级最高,普通的<view>盖不住它们。如果你需要在视频上方显示按钮或文字,必须用<cover-view>和<cover-image>。
6.4 一个完整的对比例子
以前写一个图片列表(HTML):
ini
<div class="list">
<img src="pic1.jpg" alt="图片1" />
<img src="pic2.jpg" alt="图片2" />
</div>
现在在 uni-app 中写:
xml
<template>
<view class="list">
<image src="/static/pic1.jpg" mode="widthFix" />
<image src="/static/pic2.jpg" mode="widthFix" />
</view>
</template>
<style>
.list {
display: flex;
flex-direction: column;
padding: 20rpx;
}
image {
width: 100%;
margin-bottom: 20rpx;
}
</style>
💡 注意
mode="widthFix": 这是<image>组件的属性,表示"宽度固定,高度按比例自适应"。传统<img>标签没有这个属性,需要写 CSS 实现。
七、JS 的三大变化(核心重点!)
7.1 运行环境变了------浏览器专属 API 不能用了
| 对比项 | 传统 H5(浏览器中) | uni-app(App/小程序端) |
|---|---|---|
| JS 运行在哪 | 浏览器的 JS 引擎 | V8 引擎(App端)/ 各小程序引擎 |
window 对象 |
✅ 可用 | ❌ 不可用 |
document 对象 |
✅ 可用 | ❌ 不可用 |
navigator 对象 |
✅ 可用 | ❌ 不可用 |
location 对象 |
✅ 可用 | ❌ 不可用 |
localStorage |
✅ 可用 | ❌ 不可用(用 uni.setStorage) |
| Cookie | ✅ 可用 | ❌ 不可用 |
| jQuery / DOM 操作库 | ✅ 可用 | ❌ 不可用 |
📖 什么是
window、document?
window:浏览器提供的"全局对象",代表当前浏览器窗口。alert()、setTimeout()、location都挂在它上面。document:代表当前网页的 DOM 树(文档对象模型),通过它可以查找和操作页面上的任何元素。在 App 和小程序端,没有浏览器,自然就没有
window和document。
⚠️ 唯一例外: 当你把 uni-app 编译为 H5 时,运行环境就是浏览器,此时window、document是可用的。但为了跨端兼容,建议永远不要直接使用它们。
7.2 不再操作 DOM,改用"数据绑定"(MVVM)
这是最核心的思维转变,请务必理解透。
📖 什么是 DOM?
DOM(Document Object Model,文档对象模型)是浏览器把 HTML 解析后生成的一棵"树"。每个 HTML 标签都是树上的一个节点。通过
document.getElementById()、document.querySelector()等方法可以"找到"某个节点,然后修改它的内容、样式、属性。
📖 什么是 MVVM?MVVM 是 Model-View-ViewModel 的缩写:
- Model(模型): 数据(如
message: "你好")- View(视图): 用户看到的界面
- ViewModel(视图模型): 连接数据和界面的"桥梁"(在 Vue 中就是
data()和模板的绑定关系)核心思想:你只需要修改数据,界面会自动更新。你不需要手动去"找到某个元素然后改它"。
❌ 以前的写法(操作 DOM):
xml
<span id="myText">123</span>
<button onclick="changeText()">修改</button>
<script>
function changeText() {
// 第一步:通过 id 找到元素
// 第二步:修改元素的文本内容
document.getElementById("myText").innerText = "789";
}
</script>
你需要手动找到元素 → 手动修改它。
✅ 现在的写法(数据绑定):
xml
<template>
<view>
<text>{{ message }}</text>
<button @click="changeText">修改</button>
</view>
</template>
<script>
export default {
data() {
return {
message: '123' // ← 这就是"数据"
};
},
methods: {
changeText() {
this.message = '789'; // ← 只改数据,界面自动变!
}
}
}
</script>
你只需要修改 data 中的变量 ,界面上所有引用了这个变量的地方会自动更新。不需要找元素,不需要操作 DOM。
🍔 类比:
以前你是"手动挡"------每次换挡都要自己踩离合、拨挡杆(找元素、改属性)。
现在你是"自动挡"------你只管踩油门(改数据),变速箱自动帮你换挡(更新界面)。
关键规则:
| 规则 | 说明 |
|---|---|
需要绑定到界面的数据,必须 写在 data() 的 return {} 中 |
否则模板中无法识别 |
修改数据直接赋值:this.xxx = 新值 |
不需要微信小程序的 setData() |
事件绑定用 @事件名="方法名" |
如 @click、@input、@longpress |
方法定义在 methods: {} 中 |
通过 this.方法名() 调用 |
📖 什么是
this?在 Vue 组件中,
this指向当前组件实例。通过this你可以访问data中的数据、调用methods中的方法。比如this.message就是访问 data 里的 message 变量。
7.3 API 全换了
以前你用的浏览器 API,在 uni-app 中全部替换为 uni.xxx() 形式:
| 你想做的事 | 传统 H5 写法 | uni-app 写法 |
|---|---|---|
| 弹窗提示 | alert('提示') |
uni.showToast({ title: '提示' }) |
| 确认弹窗 | confirm('确定吗?') |
uni.showModal({ title: '提示', content: '确定吗?' }) |
| 网络请求 | $.ajax() 或 fetch() |
uni.request() |
| 本地存储(存) | localStorage.setItem(k, v) |
uni.setStorageSync(k, v) |
| 本地存储(取) | localStorage.getItem(k) |
uni.getStorageSync(k) |
| 页面跳转 | location.href = 'xxx' |
uni.navigateTo({ url: 'xxx' }) |
| 返回上一页 | history.back() |
uni.navigateBack() |
| 获取屏幕宽度 | window.innerWidth |
uni.getSystemInfoSync().windowWidth |
📖 命名规律: uni-app 的 API 基本是把微信小程序的
wx.xxx改成了uni.xxx。如果你看过微信小程序文档,会发现非常亲切。
实际代码示例:
javascript
// 发起网络请求
uni.request({
url: 'https://api.example.com/data',
method: 'GET',
success: (res) => {
console.log('请求成功:', res.data);
},
fail: (err) => {
console.log('请求失败:', err);
}
});
// 本地存储
uni.setStorageSync('username', '张三');
const name = uni.getStorageSync('username'); // '张三'
// 弹窗
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success: (res) => {
if (res.confirm) {
// 用户点了"确定"
}
}
});
八、页面跳转详解:五种 API 各有分工(重要!)
在 uni-app 中,页面跳转不再是简单的 location.href,而是有 5 种专用 API,各有不同的使用场景。搞混了就会出现"页面回不去"、"跳转无反应"等问题。
8.1 五种跳转方式对比表
| API | 作用 | 页面栈变化 | 能否传参 | 适用场景 |
|---|---|---|---|---|
uni.navigateTo |
保留当前页,打开新页面 | 栈 +1 层 | ✅ | 从列表进详情页、从首页进设置页 |
uni.redirectTo |
关闭当前页,打开新页面 | 栈不变(替换) | ✅ | 登录成功后跳到首页(不想让用户返回登录页) |
uni.switchTab |
关闭所有非 Tab 页,切到 Tab 页 | 清空非 Tab 栈 | ❌ | 底部导航栏切换(首页/我的/消息) |
uni.reLaunch |
关闭所有页面,打开指定页面 | 清空所有栈 | ✅ | 退出登录后回到登录页、重置整个应用状态 |
uni.navigateBack |
返回上一页(或上 N 页) | 栈 -1(或 -N)层 | ❌ | 用户点返回、提交后返回 |
📖 什么是"页面栈"?
页面栈是一个"后进先出"的结构。每打开一个新页面,就压入栈顶;每返回一次,就弹出栈顶。就像一摞盘子------你只能从最上面拿。
uni.navigateTo就是往这摞盘子上"加一个",uni.navigateBack就是"拿走最上面那个"。⚠️ 注意: 微信小程序端页面栈最多 10 层,超过就无法再 navigateTo 了。
8.2 逐个详解
① uni.navigateTo------最常用,"前进"
php
// 从首页跳转到详情页(保留首页,用户可以返回)
uni.navigateTo({
url: '/pages/detail/detail?id=123&title=hello'
});
规则:
- ✅ 可以传参(URL 拼接
?key=value) - ❌ 不能跳转到 tabBar 页面(会报错或无反应)
- 新页面被压入页面栈,用户点返回可回到原页面
② uni.redirectTo------"替换",不保留当前页
php
// 登录成功后跳到首页(用户不能返回登录页)
uni.redirectTo({
url: '/pages/home/home'
});
规则:
- ✅ 可以传参
- ❌ 不能跳转到 tabBar 页面
- 当前页面被关闭销毁,无法返回
🍔 类比:
navigateTo像"打开新标签页"(旧页面还在);redirectTo像"在当前标签页输入新网址"(旧页面没了)。
③ uni.switchTab------专门切 Tab 页
php
// 切换到底部导航栏的"我的"页面
uni.switchTab({
url: '/pages/user/user'
});
规则:
- ❌ 不能传参! 这是官方限制。如果需要传数据,用
uni.setStorageSync或全局变量中转 - 只能跳转到
pages.json中tabBar.list里声明过的页面 - 会关闭所有非 tabBar 页面
⚠️ 常见坑: 如果你用
uni.navigateTo去跳一个 tabBar 页面,会直接报错page is not found。必须用uni.switchTab。
④ uni.reLaunch------"核弹级"重置
php
// 退出登录,回到登录页(关闭所有页面)
uni.reLaunch({
url: '/pages/login/login'
});
规则:
- ✅ 可以传参
- ✅ 可以跳转到任何页面(包括 tabBar 页面)
- 关闭所有已打开的页面,从零开始
⑤ uni.navigateBack------"后退"
ini
// 返回上一页(默认 delta=1)
uni.navigateBack();
// 返回上两层(如果栈里够两层的话)
uni.navigateBack({ delta: 2 });
规则:
- 如果页面栈只剩一个页面,调用无效(不会关闭最后一个页面)
delta参数表示回退几层,默认 1
8.3 实战场景决策树
markdown
我要跳转到一个新页面?
│
├── 目标页面是 tabBar 页面吗?
│ ├── 是 → 用 uni.switchTab(不能传参)
│ └── 否 ↓
│
├── 用户需要返回当前页面吗?
│ ├── 需要 → 用 uni.navigateTo
│ └── 不需要(当前页可以关掉)→ 用 uni.redirectTo
│
├── 需要关闭所有页面重新开始?
│ └── 是 → 用 uni.reLaunch
│
└── 用户点了返回按钮?
└── 用 uni.navigateBack
💡 一句话记忆: 日常开发 90% 的场景用
navigateTo,切底部 Tab 用switchTab,登录/退出场景用redirectTo或reLaunch。
九、CSS 的变化
好消息:标准 CSS 语法基本都能用。 但有几个重要差异:
9.1 选择器限制
| 选择器 | 是否支持 | 说明 |
|---|---|---|
.class-name |
✅ | 类选择器,最常用 |
#id |
✅ | ID 选择器 |
tag |
✅ | 标签选择器,如 view {} |
*(通配符) |
❌ | 不支持 |
body |
❌ | 改为 page |
后代选择器 .a .b |
✅ | 支持 |
子选择器 .a > .b |
⚠️ | 部分端支持 |
⚠️ 重点: 以前你写
body { margin: 0; },现在要写成page { margin: 0; }。因为在非 H5 端没有body这个概念,page代表整个页面。
9.2 单位:用 rpx 替代 px
📖 什么是 rpx?
rpx(responsive pixel,响应式像素)是 uni-app 引入的自适应单位。
换算规则: 无论什么屏幕,都规定屏幕宽度 = 750rpx。
- 如果设计稿是 750px 宽,那 1px = 1rpx,直接换算。
- 如果设计稿是 375px 宽(iPhone 6/7/8),那 1px = 2rpx。
好处: 你不需要写任何媒体查询(
@media),rpx 会自动适配所有屏幕宽度。
css
/* 以前的写法 */
.box {
width: 100px;
font-size: 14px;
padding: 10px 20px;
}
/* 现在推荐写法(假设设计稿 750px 宽) */
.box {
width: 200rpx;
font-size: 28rpx;
padding: 20rpx 40rpx;
}
💡 实际使用建议: 如果你的 UI 设计稿是 750px 宽,直接把 px 换成 rpx 即可。如果是 375px 宽,把数值 ×2 再写 rpx。
9.3 布局:强烈推荐 Flex
📖 什么是 Flex 布局?
Flex(Flexible Box,弹性盒子)是 CSS3 引入的一维布局方式。通过给父元素设置
display: flex,子元素会自动排列,支持对齐、分布、换行等。它是目前移动端最主流、兼容性最好的布局方案。
css
/* 水平排列,两端对齐,垂直居中 */
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
/* 垂直排列 */
.column {
display: flex;
flex-direction: column;
}
✅ Flex 布局在 uni-app 的所有端(H5、App、各家小程序)都完美支持,放心使用。
9.4 其他注意事项
| 注意点 | 说明 |
|---|---|
| 背景图 / 字体文件 | 建议不超过 40KB,否则影响编译性能。大图建议用网络地址或 base64 |
position: fixed |
在部分小程序端表现有差异,谨慎使用 |
| 百分比高度 | 需要父元素有明确高度才生效 |
十、工程结构和页面管理
10.1 项目目录结构(必须了解)
sql
├── pages/ ← 所有页面放这里
│ ├── index/
│ │ └── index.vue ← 首页
│ └── user/
│ └── user.vue ← 用户页
├── components/ ← 自定义组件(easycom 扫描目录)
├── static/ ← 静态资源(图片、字体等,不会被编译)
├── uni_modules/ ← 插件/组件库(easycom 也扫描这里)
├── App.vue ← 应用入口(全局逻辑+全局样式)
├── main.js ← 应用初始化文件
├── pages.json ← 页面路由和窗口配置(重要!)
├── manifest.json ← 应用配置(appid、版本、权限等)
└── uni.scss ← 全局 SCSS 变量
10.2 pages.json------你的"页面调度中心"
📖 在传统网页中,页面之间的跳转靠
<a href="xxx.html">链接。在 uni-app 中,每个页面都必须在pages.json中注册,否则无法访问。
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "个人中心"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "我的应用",
"navigationBarBackgroundColor": "#ffffff"
},
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/home.png",
"selectedIconPath": "static/home-active.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "static/user.png",
"selectedIconPath": "static/user-active.png"
}
]
}
}
关键规则:
| 规则 | 说明 |
|---|---|
pages 数组的第一项就是首页 |
不需要额外配置 |
| 新建页面必须在这里注册 | 否则路由找不到 |
navigationBarTitleText |
页面顶部导航栏标题 |
tabBar |
底部选项卡(类似微信底部的"首页/通讯录/发现/我") |
tabBar.list 中的页面只能用 switchTab 跳转 |
用 navigateTo 会报错 |
10.3 与小程序的对应关系
如果你看过微信小程序文档,这个对应关系能帮你快速理解:
| 微信小程序 | uni-app | 说明 |
|---|---|---|
app.json(页面配置部分) |
pages.json |
管页面路由和窗口样式 |
app.json(应用配置部分) |
manifest.json |
管 appid、版本号、权限 |
app.js + app.wxss |
App.vue |
全局逻辑 + 全局样式 |
.wxml |
.vue 中的 <template> |
页面结构 |
.wxss |
.vue 中的 <style> |
页面样式 |
.js |
.vue 中的 <script> |
页面逻辑 |
十一、生命周期------页面从生到死的过程
📖 什么是生命周期?
每个页面/组件从"创建 → 显示 → 隐藏 → 销毁"会经历一系列阶段,每个阶段 uni-app 都会自动调用对应的函数(钩子),你可以在这些函数里写特定逻辑。
页面生命周期(写在 .vue 的 <script> 中):
| 钩子函数 | 触发时机 | 典型用途 |
|---|---|---|
onLoad(options) |
页面加载时(只执行一次) | 接收参数、发起初始请求 |
onShow() |
页面每次显示时 | 刷新数据(从其他页面返回时) |
onReady() |
页面初次渲染完成 | 操作 canvas、初始化地图 |
onHide() |
页面隐藏时(跳到其他页面) | 暂停定时器、保存临时数据 |
onUnload() |
页面卸载时(彻底关闭) | 清理定时器、释放资源 |
xml
<script>
export default {
onLoad(options) {
// options 中是前一个页面传过来的参数
// 如:uni.navigateTo({ url: '/pages/detail/detail?id=123' })
// 这里 options.id === '123'
console.log('页面加载了,参数:', options);
this.loadData();
},
onShow() {
console.log('页面显示了');
},
methods: {
loadData() {
// 发起请求获取数据
}
}
}
</script>
⚠️ 注意: 这些生命周期钩子(
onLoad、onShow等)是 uni-app 扩展的,直接写在export default {}里面 ,和data、methods平级,不是写在methods里面!
十二、一张总结表:从 H5 到 uni-app 的完整迁移清单
| 序号 | 传统 H5 | uni-app | 备注 |
|---|---|---|---|
| 1 | .html 文件 |
.vue 文件 |
单文件组件 |
| 2 | <div> |
<view> |
容器 |
| 3 | <span> / <p> |
<text> |
文本 |
| 4 | <img> |
<image> |
图片 |
| 5 | <a> |
<navigator> |
跳转 |
| 6 | document.getElementById() |
数据绑定(MVVM) | 不再操作 DOM |
| 7 | onclick="fn()" |
@click="fn" |
事件绑定 |
| 8 | alert() / confirm() |
uni.showToast() / uni.showModal() |
弹窗 |
| 9 | $.ajax() / fetch() |
uni.request() |
网络请求 |
| 10 | localStorage |
uni.setStorageSync() / uni.getStorageSync() |
本地存储 |
| 11 | location.href |
uni.navigateTo() |
页面跳转 |
| 12 | history.back() |
uni.navigateBack() |
返回 |
| 13 | px / rem |
rpx |
尺寸单位 |
| 14 | <script src="..."> |
import / require |
引入 JS |
| 15 | <link rel="stylesheet"> |
@import |
引入 CSS |
| 16 | body 选择器 |
page 选择器 |
全局样式 |
| 17 | URL 路由 / 链接 | pages.json 配置 |
页面管理 |
| 18 | 手动 import + 注册组件 | easycom 自动识别 | 组件引入 |
十三、给小白的学习建议
如果你是一个熟悉 H5 但没接触过 Vue 和小程序的新手,记住三句话就够了:
- 别操作 DOM,改数据就行。 这是最核心的思维转变,一旦理解,后面一切豁然开朗。
- 标签换一套,API 换一套。 但编程思想不变,只是"语法"不同。就像你从写中文作文改写成英文作文,思路一样,单词和语法不同。
- 一切页面配置看
pages.json。 它就是你的"页面调度中心",新建页面、配置导航栏、设置 TabBar 都在这里。
推荐学习路线:
arduino
第 1 步:跑通一个 uni-app 项目(用 HBuilderX 创建)
↓
第 2 步:理解 .vue 文件的三段式结构(template / script / style)
↓
第 3 步:掌握数据绑定(data → template 的 {{}} 语法)
↓
第 4 步:学会常用组件(view、text、image、button、input)
↓
第 5 步:学会页面跳转和传参(navigateTo + onLoad)
↓
第 6 步:学会网络请求(uni.request)
↓
第 7 步:学会列表渲染(v-for)和条件渲染(v-if)
↓
第 8 步:上手一个完整小项目(如 Todo List、资讯列表)
写在最后
uni-app 的学习曲线并不陡峭,尤其对于有 HTML/CSS/JS 基础的同学。它本质上就是 Vue 语法 + 一套跨平台组件和 API。把 Vue 的数据绑定、组件化思想搞明白,剩下的就是熟悉各平台的差异和 uni-app 特有的 API 而已。
不要怕"不一样",要拥抱"不一样"。 正是这些差异,让 uni-app 能做到一套代码跑遍所有平台,极大提升开发效率。
祝你在跨端开发的路上,一路畅通!🚀
📚 参考资料:
- uni-app 官方文档 - 白话 uni-app
- uni-app 官方文档 - 组件使用入门
- uni-app 官方文档 - API 概述
- uni-app 官方文档 - 页面路由
- uni-app 官方文档 - easycom 组件
- uni-app 官方文档 - 工程简介
📝 本文为原创技术总结,基于官方文档深度梳理和个人理解重新组织。所有技术点均严格依据 uni-app 官方文档,确保准确权威。转载请注明出处。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有问题欢迎评论区交流~ 👋