uni-app 运行与编译原理梳理
传统 uniapp 模板 Vs Vue3 + Vite + Ts + uniapp 模板 Vs 普通 Vue3 项目
目的:
搞清楚 uni-app 为什么有的项目没有 package.json 也能跑,
以及 Vue3 + Vite + @dcloudio/vite-plugin-uni 到底在做什么。
一、三种项目形态总览
在学习 uni-app 的过程中,常见会遇到三种“看起来很像,但本质完全不同”的项目:
- 传统 uni-app 模板(HBuilderX 内置)
- uni-app(Vue3 + TypeScript + Vite)模板
- 普通 Vue3 + Vite 项目
理解它们的差异,是理解 uni-app 编译原理的关键。
二、传统 uni-app 模板(最“黑盒”的一种)
2.1 项目结构特征
src/
├── pages/
├── components/
├── static/
├── App.vue
├── main.ts
├── pages.json
├── manifest.json特点:
- ❌ 没有
package.json - ❌ 没有
node_modules - ❌ 没有
vite.config - ❌ 不需要 npm / pnpm
- ✅ 可以直接在 HBuilderX 里右键运行
2️.2 它是如何运行的?
核心结论:
HBuilderX 本身就是一个「自带 uni-app 编译器的 IDE」
当你在 HBuilderX 中点击:
右键 → 运行 → 运行到 H5 / 小程序 / App
实际发生的是:
HBuilderX(内部)
↓
内置 uni-app 编译器
↓
解析 pages.json / manifest.json
↓
转换为目标平台代码📌 注意:
- 这个过程不依赖你项目里的任何 npm 配置
- Webpack / 编译器 / 插件 全部内置在 HBuilderX 内部
- 对开发者来说是一个“黑盒”
2.3️ 适合谁?
- uni-app 新手
- 不关心构建细节
- 只在 HBuilderX 内开发
- 不需要 CI / 自动化
三、uni-app(Vue3 + TS + Vite)模板(推荐主流)
3.1 项目结构特征
project-root/
├── src/
│ ├── pages/
│ ├── components/
│ ├── static/
│ ├── App.vue
│ ├── main.ts
│ ├── pages.json
│ └── manifest.json
│
├── package.json
├── vite.config.ts
├── tsconfig.json
├── env.d.ts
└── node_modules/特点:
- ✅ 有
package.json - ✅ 有
vite.config.ts - ✅ 使用 npm / pnpm
- ✅ 可脱离 HBuilderX(CLI)
- ✅ 支持 CI / 自动化
3.2 它是如何运行的?
当你使用:
npm run dev:h5
# 或
npm run dev:mp-weixin或者在 HBuilderX 中点击「运行」时:
HBuilderX / 终端
↓
uni CLI(入口)
↓
Vite
↓
@dcloudio/vite-plugin-uni
↓
多端编译输出📌 关键点:
HBuilderX 不再负责“编译”
它只是:
- 读取 scripts
- 帮你执行
uni dev xxx - 提供运行器 / 真机调试
3.3 vite.config.ts 的核心内容
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
export default defineConfig({
plugins: [uni()],
});这里的 uni() 插件不是普通 Vue 插件,而是:
- 重写
.vue编译逻辑 - 将模板转换为多端中间结构
- 注入 uni runtime
3.4 适合谁?
- Vue3 / TS / Vite 开发者
- 需要工程化、CI、自动化
- 中大型项目
- 长期维护项目
四、普通 Vue3 + Vite 项目(⚠️ 不是 uni-app)
4.1 典型配置
import vue from "@vitejs/plugin-vue";
export default {
plugins: [vue()],
};运行方式:
npm run dev
# 或 vite编译链路:
.vue
↓
@vitejs/plugin-vue
↓
浏览器 DOM4.2 为什么“装个 uni 插件”不够?
❌ 错误理解:
Vue3 + TS
- @dcloudio/vite-plugin-uni = uni-app
❗ 实际原因:
- 缺少
pages.json - 缺少
manifest.json - 入口不是
createSSRApp - 没有 uni runtime
- 没有 uni CLI 注入环境变量
- 运行目标仍然是 Web
五、plugins: [vue()] 和 plugins: [uni()] 的区别
我按 “一句话 → 编译阶段 → 运行阶段 → 对比总结” 的方式,帮你彻底讲清楚。
5.1 先给一句话结论(记住这句就够)
vue()是“把.vue编译成浏览器能跑的代码” >uni()是“把.vue改写成 uni-app 多端运行时能理解的中间代码”
它们目标完全不同。
5.2 vue()(@vitejs/plugin-vue)到底干了什么?
它解决的问题
“如何让浏览器认识
.vue文件?”
它的编译职责
当你写:
plugins: [vue()];编译链路是:
.vue
↓
@vitejs/plugin-vue
↓
JS + render function
↓
DOM / 浏览器它主要做了:
- 解析
<template> - 编译成 DOM render 函数
- 解析
<script setup> - 处理
<style> - 热更新(HMR)
📌 关键点: 👉 目标平台是浏览器 DOM
5.3 它的“世界假设”
vue() 默认假设:
- 有
document - 有
window - 有 DOM API
- 有浏览器事件模型
所以它天然不适合小程序 / App 原生环境。
5.3 uni()(@dcloudio/vite-plugin-uni)到底干了什么?
它解决的问题
“如何让同一份 Vue 代码运行在 H5 / 小程序 / App?”
它的编译职责(非常重)
当你写:
plugins: [uni()];编译链路变成:
.vue
↓
vite-plugin-uni
↓
AST 重写(模板 / 事件 / 指令)
↓
抽象平台中间层
↓
目标平台代码它会:
- 拦截
.vue编译 - 重写
<template>(不是 DOM) - 改写事件系统(tap / click)
- 改写生命周期
- 注入
uni.*运行时 - 支持条件编译(
#ifdef)
📌 关键点: 👉 目标不是 DOM,而是“多端运行时”
一个直观例子
你写的模板:
<view @click="onClick">
{{ msg }}
</view>vue() 编译结果(Web)
h("div", { onClick: onClick }, msg);uni() 编译结果(抽象)
view + tap
↓
微信小程序 / App / H55.4 它们在“生命周期”上的差异
Vue Web 生命周期(vue())
beforeMount
mounted
updated
unmounteduni-app 生命周期(uni())
onLoad
onShow
onHide
onUnload👉 uni() 会在编译阶段做生命周期映射
5.5 为什么 uni() 不需要 vue()?
很多人会疑惑:
“那 uni() 里面是不是也用了 vue()?”
答案是:是的,但不是直接暴露出来
vite-plugin-uni内部整合了 Vue 编译能力但在此基础上:
- 做了大量二次编译
- 改写了目标平台
👉 你不能手动写 plugins: [vue(), uni()]
5.6 两者的“责任边界”对比(核心表)
| 维度 | vue() | uni() |
|---|---|---|
| 所属 | Vite 官方 | DCloud |
| 目标 | 浏览器 DOM | 多端运行时 |
编译 .vue | ✅ | ✅(重写) |
| DOM API | 假设存在 | 不假设 |
| 多端 | ❌ | ✅ |
| 条件编译 | ❌ | ✅ |
| 生命周期映射 | ❌ | ✅ |
| 是否可互换 | ❌ | ❌ |
5.7 一个非常容易理解的类比
| 现实世界 | 技术类比 |
|---|---|
vue() | 把中文翻成“普通话” |
uni() | 把中文翻成“多国语言” |
| DOM | 普通话 |
| 小程序 | 粤语 / 英语 / 日语 |
5.8 你现在可以这样“心里建模”
- 写 Vue 代码 → 语法是 Vue
- 编译用
uni()→ 运行时是 uni- 平台差异 → 由 uni 编译器解决
vue()→ 只关心 Web
5.9 一句话终极总结
vue()解决的是“Vue 如何跑在浏览器” >uni()解决的是“Vue 如何跑在所有平台”
六、三种模式的核心差异对比
| 维度 | 传统 uni-app | uni-app + Vite | 普通 Vue3 |
|---|---|---|---|
| 编译器 | HBuilderX 内置 | uni CLI + Vite | Vite |
| package.json | ❌ | ✅ | ✅ |
| vite.config | ❌ | ✅ | ✅ |
| 多端编译 | ✅ | ✅ | ❌ |
| DOM API | ❌ | ❌ | ✅ |
| CI 支持 | ❌ | ✅ | ✅ |
| 可定制性 | 低 | 高 | 高 |
七、实践建议(经验总结)
新手:
- 先用传统模板理解 uni 约定
- 再切 Vue3 + Vite
Vue3 老手:
- 直接 Vue3 + TS + Vite
- 不要尝试“反向改造普通 Vue 项目”
迁移策略:
- 以 uni-app 模板为基准
- 迁移业务代码
- 而不是迁移工程结构
八、一句话总结
不是 uni-app 不需要 package.json,而是传统模式下,HBuilderX 把一切工程细节都帮你“藏起来了”。
Vue3 + Vite 模式,是把构建权交还给开发者。