从 H5 到 uni-app:一篇写给前端小白的"翻译指南"

24 阅读12分钟

📌 本文适合谁? 你会写 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 请求通信

页面加载的流程变成了:

1. 前端先渲染一个"空壳子"页面
2. 页面加载后,JS 发起网络请求(uni.request)
3. 后端返回 JSON 数据
4. 前端拿到数据,填充到页面上

📖 什么是 JSON?
JSON(JavaScript Object Notation)是一种轻量级的数据格式,长这样:{"name": "张三", "age": 25}。它是目前前后端数据交换的"通用语言"。

📖 什么是 HTTP 请求?
就是浏览器/客户端向服务器"要数据"的动作。你在浏览器地址栏输入网址回车,本质就是发了一次 HTTP 请求。在代码中,我们用 uni.request() 来手动发起请求。

🍔 打个比方:
以前是餐厅把菜直接端到你桌上(服务端渲染好再给你);现在是餐厅给你一份空盘子和菜单,你自己去吧台取菜(前端主动请求数据,再自己填到页面上)。

2.3 代码层面怎么用?

// 在 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 了

对比项传统 H5uni-app
文件后缀.html.vue
开发时写的HTML 标签Vue 模板语法
运行时实际是什么HTML 直接运行经过编译器转换成各平台代码

3.2 什么是"编译器"和"运行时"?

这是理解 uni-app 的核心概念:

📖 什么是编译器?
把你写的 .vue 源代码,"翻译"成各个平台能识别的代码。比如编译到微信小程序,就生成 wxml + wxss + js;编译到 H5,就生成 html + css + js。你写一次,它翻译多次。

📖 什么是运行时(Runtime)?
编译后的代码在设备上实际执行时,需要一套"运行环境"来支撑。比如 Vue 的响应式系统、组件生命周期管理等,都属于运行时的范畴。

🍔 类比: 编译器像"翻译官",把你的中文稿翻译成各国语言;运行时像"同声传译设备",确保翻译出来的内容能被正确"播放"。


四、文件内部结构变了:从"一栋楼"到"三个房间"

4.1 以前的 HTML 文件

<!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>

<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 几个关键规则

  1. <template> 内必须有一个根元素,且只能有一个。通常用 <view> 包裹。
  2. <script> 中必须 export default {} ,这是 ES6 模块语法,表示"导出这个组件"。
  3. <style> 中建议加 scoped,防止样式泄漏到其他组件。

📖 什么是 ES6 模块和 export default
ES6(ECMAScript 2015)是 JavaScript 的一次重大升级,引入了模块化语法。export default 的意思是"把这个对象作为默认导出",别的文件通过 import 引入后就能使用。你可以理解为"这个文件对外提供的产品"。


五、外部文件的引用方式变了

5.1 引入 JS 文件

以前:

<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>

现在:

// 方式一: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"。这样写路径,无论文件被嵌套多深,都不会出错。

📖 requireimport 的区别?
两者都是"引入外部模块"的语法。require 是 CommonJS 规范(Node.js 风格),import 是 ES6 模块规范。在 uni-app 中推荐使用 import,因为编译器对 ES6 模块的支持更好,且支持 Tree Shaking(自动去除未使用的代码,减小包体积)。

5.2 引入 CSS 文件

以前:

<link href="css/bootstrap.css" rel="stylesheet" />

现在:

<style>
@import "@/common/uni.css";
</style>

⚠️ 注意: @import 语句必须写在 <style> 标签内的最顶部,后面要加分号 ;

5.3 全局样式写在哪?

项目根目录有一个 App.vue 文件,它是整个应用的"入口组件"。

<!-- 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 + 注册):

<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)风格。

正确示例:

components/my-button/my-button.vue     →  模板中用 <my-button />
components/user-card/user-card.vue     →  模板中用 <user-card />
components/uni-list/uni-list.vue       →  模板中用 <uni-list />

错误示例(不会生效):

components/MyButton.vue                 ← 缺少同名文件夹,❌
components/my-button/index.vue          ← 文件名不是 my-button.vue,❌
components/my-button/MyButton.vue       ← 大小写不匹配,❌

📖 补充规则: 如果组件安装在 uni_modules 目录下,easycom 同样支持自动识别,路径格式为:

uni_modules/插件id/components/组件名称/组件名称.vue

例如你通过插件市场安装了 uni-ui,它会自动注册 uni-badgeuni-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):

<div class="list">
  <img src="pic1.jpg" alt="图片1" />
  <img src="pic2.jpg" alt="图片2" />
</div>

现在在 uni-app 中写:

<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 操作库✅ 可用❌ 不可用

📖 什么是 windowdocument

  • window:浏览器提供的"全局对象",代表当前浏览器窗口。alert()setTimeout()location 都挂在它上面。
  • document:代表当前网页的 DOM 树(文档对象模型),通过它可以查找和操作页面上的任何元素。

在 App 和小程序端,没有浏览器,自然就没有 windowdocument

⚠️ 唯一例外: 当你把 uni-app 编译为 H5 时,运行环境就是浏览器,此时 windowdocument 是可用的。但为了跨端兼容,建议永远不要直接使用它们

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):

<span id="myText">123</span>
<button onclick="changeText()">修改</button>

<script>
function changeText() {
  // 第一步:通过 id 找到元素
  // 第二步:修改元素的文本内容
  document.getElementById("myText").innerText = "789";
}
</script>

你需要手动找到元素 → 手动修改它

✅ 现在的写法(数据绑定):

<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.innerWidthuni.getSystemInfoSync().windowWidth

📖 命名规律: uni-app 的 API 基本是把微信小程序的 wx.xxx 改成了 uni.xxx。如果你看过微信小程序文档,会发现非常亲切。

实际代码示例:

// 发起网络请求
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——最常用,"前进"

// 从首页跳转到详情页(保留首页,用户可以返回)
uni.navigateTo({
  url: '/pages/detail/detail?id=123&title=hello'
});

规则:

  • ✅ 可以传参(URL 拼接 ?key=value
  • 不能跳转到 tabBar 页面(会报错或无反应)
  • 新页面被压入页面栈,用户点返回可回到原页面

uni.redirectTo——"替换",不保留当前页

// 登录成功后跳到首页(用户不能返回登录页)
uni.redirectTo({
  url: '/pages/home/home'
});

规则:

  • ✅ 可以传参
  • 不能跳转到 tabBar 页面
  • 当前页面被关闭销毁,无法返回

🍔 类比: navigateTo 像"打开新标签页"(旧页面还在);redirectTo 像"在当前标签页输入新网址"(旧页面没了)。

uni.switchTab——专门切 Tab 页

// 切换到底部导航栏的"我的"页面
uni.switchTab({
  url: '/pages/user/user'
});

规则:

  • 不能传参! 这是官方限制。如果需要传数据,用 uni.setStorageSync 或全局变量中转
  • 只能跳转到 pages.jsontabBar.list 里声明过的页面
  • 会关闭所有非 tabBar 页面

⚠️ 常见坑: 如果你用 uni.navigateTo 去跳一个 tabBar 页面,会直接报错 page is not found。必须用 uni.switchTab

uni.reLaunch——"核弹级"重置

// 退出登录,回到登录页(关闭所有页面)
uni.reLaunch({
  url: '/pages/login/login'
});

规则:

  • ✅ 可以传参
  • ✅ 可以跳转到任何页面(包括 tabBar 页面)
  • 关闭所有已打开的页面,从零开始

uni.navigateBack——"后退"

// 返回上一页(默认 delta=1)
uni.navigateBack();

// 返回上两层(如果栈里够两层的话)
uni.navigateBack({ delta: 2 });

规则:

  • 如果页面栈只剩一个页面,调用无效(不会关闭最后一个页面)
  • delta 参数表示回退几层,默认 1

8.3 实战场景决策树

我要跳转到一个新页面?
│
├── 目标页面是 tabBar 页面吗?
│   ├── 是 → 用 uni.switchTab(不能传参)
│   └── 否 ↓
│
├── 用户需要返回当前页面吗?
│   ├── 需要 → 用 uni.navigateTo
│   └── 不需要(当前页可以关掉)→ 用 uni.redirectTo
│
├── 需要关闭所有页面重新开始?
│   └── 是 → 用 uni.reLaunch
│
└── 用户点了返回按钮?
    └── 用 uni.navigateBack

💡 一句话记忆: 日常开发 90% 的场景用 navigateTo,切底部 Tab 用 switchTab,登录/退出场景用 redirectToreLaunch


九、CSS 的变化

好消息:标准 CSS 语法基本都能用。 但有几个重要差异:

9.1 选择器限制

选择器是否支持说明
.class-name类选择器,最常用
#idID 选择器
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 会自动适配所有屏幕宽度。

/* 以前的写法 */
.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,子元素会自动排列,支持对齐、分布、换行等。它是目前移动端最主流、兼容性最好的布局方案。

/* 水平排列,两端对齐,垂直居中 */
.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 项目目录结构(必须了解)

├── 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 中注册,否则无法访问。

{
  "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.wxssApp.vue全局逻辑 + 全局样式
.wxml.vue 中的 <template>页面结构
.wxss.vue 中的 <style>页面样式
.js.vue 中的 <script>页面逻辑

十一、生命周期——页面从生到死的过程

📖 什么是生命周期?
每个页面/组件从"创建 → 显示 → 隐藏 → 销毁"会经历一系列阶段,每个阶段 uni-app 都会自动调用对应的函数(钩子),你可以在这些函数里写特定逻辑。

页面生命周期(写在 .vue<script> 中):

钩子函数触发时机典型用途
onLoad(options)页面加载时(只执行一次)接收参数、发起初始请求
onShow()页面每次显示时刷新数据(从其他页面返回时)
onReady()页面初次渲染完成操作 canvas、初始化地图
onHide()页面隐藏时(跳到其他页面)暂停定时器、保存临时数据
onUnload()页面卸载时(彻底关闭)清理定时器、释放资源
<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>

⚠️ 注意: 这些生命周期钩子(onLoadonShow 等)是 uni-app 扩展的,直接写在 export default {} 里面,和 datamethods 平级,不是写在 methods 里面!


十二、一张总结表:从 H5 到 uni-app 的完整迁移清单

序号传统 H5uni-app备注
1.html 文件.vue 文件单文件组件
2<div><view>容器
3<span> / <p><text>文本
4<img><image>图片
5<a><navigator>跳转
6document.getElementById()数据绑定(MVVM)不再操作 DOM
7onclick="fn()"@click="fn"事件绑定
8alert() / confirm()uni.showToast() / uni.showModal()弹窗
9$.ajax() / fetch()uni.request()网络请求
10localStorageuni.setStorageSync() / uni.getStorageSync()本地存储
11location.hrefuni.navigateTo()页面跳转
12history.back()uni.navigateBack()返回
13px / remrpx尺寸单位
14<script src="...">import / require引入 JS
15<link rel="stylesheet">@import引入 CSS
16body 选择器page 选择器全局样式
17URL 路由 / 链接pages.json 配置页面管理
18手动 import + 注册组件easycom 自动识别组件引入

十三、给小白的学习建议

如果你是一个熟悉 H5 但没接触过 Vue 和小程序的新手,记住三句话就够了:

  1. 别操作 DOM,改数据就行。 这是最核心的思维转变,一旦理解,后面一切豁然开朗。
  2. 标签换一套,API 换一套。 但编程思想不变,只是"语法"不同。就像你从写中文作文改写成英文作文,思路一样,单词和语法不同。
  3. 一切页面配置看 pages.json 它就是你的"页面调度中心",新建页面、配置导航栏、设置 TabBar 都在这里。

推荐学习路线:

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 官方文档,确保准确权威。转载请注明出处。


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有问题欢迎评论区交流~ 👋