SSF0SSF0
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
  • HTML

    • html1
    • html2
  • CSS

    • Flex 布局常见问题与解决方案
  • JavaScript

    • 数据类型及引用问题
    • 处理 Blob 类型文件下载问题总结
    • localStorage 与 sessionStorage 区别
    • JavaScript 中的 script 标签问题详解
    • JavaScript 中的 this 指向问题详解
    • SessionStorage踩坑记录
    • 动态加载 JS 脚本方法对比
    • 浏览器页面关闭场景下的数据上报
    • HTTP流式接口详解与封装实践
    • JavaScript 中 this 绑定与解构:为什么 Pinia Store 可以解构,但 Class 实例方法不行?
  • es6

    • Promise
    • es6 模块导出方式全解析
    • reduce
    • forEach 中使用 async/await 的异步陷阱
  • Vue2

    • created VS mounted 发起异步请求
    • vue2-2
  • Vue3

    • Vite + Vue3 修改 element-plus 源码
    • Vue v-if 与 v-show
    • Vue3 ref 获取组件
    • Vue3 路由传参
    • 父子组件与组件里 Hooks 加载顺序
    • 第三方组件传入参数TS提示
    • Vue3 Props 在 Hooks 中的响应性处理
    • Vue Router 的两种历史模式及部署配置详解
    • 在Vue 3项目中顺利集成Tailwind CSS的完整指南
    • Vue Scoped CSS 深度解析
    • Vue 3 Teleport 组件完全指南:从原理到实战
    • Vant List 组件中双向绑定 loading 的注意事项
    • Vue 3 + Vite 中的路径别名与静态资源处理深度解析
    • 获取图片高度
    • 深入理解 Pinia:storeToRefs vs toRefs - 避开 TypeScript 类型推断的坑
  • webSocket

    • WebSocket 鉴权入门(实战版):握手票据 / 首帧鉴权 + 连接级 cid
  • Electron

    • 快速构建 electron + vue3 项目
  • TS

    • TS 泛型
    • 记录模板使用断言的问题
    • type 与 interface
  • WebPack

    • Webpack 介绍
  • Vite

    • Vite CLI 常见命令
    • vite 与 webpack 比较
    • Vite base 配置与 public 静态资源路径问题
  • 客户端数据库

    • IndexedDB 索引陷阱:字段重命名后索引返回旧数据
    • IndexedDB 事务陷阱与最佳实践:TransactionInactiveError 完全解析
  • UniApp

    • uni-app + Monorepo + 微信小程序热更新失效问题排查与结论总结
    • Monorepo + uni-app 开发中监听 / 热更新机制差异与解决方案
    • uni-app 运行与编译原理梳理
    • 为什么 uni-app 在 Monorepo 中必须做「环境区分」,而 Web 项目却不用?
  • 项目工程

    • 前端代码风格
    • Vue3 项目规范
    • npm 镜像问题
    • 包管理工具
    • 使用 engines 限制开发环境依赖
    • 打包与shell交互指定模式
    • Monorepo示例
    • PC 项目兼容 uni-app:基于 pnpm Monorepo 的工程化重构实践
    • Vue3 + Vite + pnpm Monorepo 迁移问题指南
    • 前端工程化:深入理解 npm 依赖管理机制
    • pnpm 修改依赖源码打包报错
    • 代码片段
  • 移动端

    • 浏览器跳转外部应用时请求状态码问题
  • 记录一下小程序
  • 控制浏览器正确保存网站账号密码的技巧
  • 信息泄露
  • 使用Gulp实现前端项目哈希化压缩打包
  • 前端缓存策略与文件哈希化的关联解析
  • 浏览器刷新方式与 Vue 生命周期、缓存行为解析
  • 浏览器消息铃声实现记录

uni-app 运行与编译原理梳理

传统 uniapp 模板 Vs Vue3 + Vite + Ts + uniapp 模板 Vs 普通 Vue3 项目

目的:
搞清楚 uni-app 为什么有的项目没有 package.json 也能跑,
以及 Vue3 + Vite + @dcloudio/vite-plugin-uni 到底在做什么。


一、三种项目形态总览

在学习 uni-app 的过程中,常见会遇到三种“看起来很像,但本质完全不同”的项目:

  1. 传统 uni-app 模板(HBuilderX 内置)
  2. uni-app(Vue3 + TypeScript + Vite)模板
  3. 普通 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
↓
浏览器 DOM

4.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 / H5

5.4 它们在“生命周期”上的差异

Vue Web 生命周期(vue())

beforeMount
mounted
updated
unmounted

uni-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-appuni-app + Vite普通 Vue3
编译器HBuilderX 内置uni CLI + ViteVite
package.json❌✅✅
vite.config❌✅✅
多端编译✅✅❌
DOM API❌❌✅
CI 支持❌✅✅
可定制性低高高

七、实践建议(经验总结)

新手:

  • 先用传统模板理解 uni 约定
  • 再切 Vue3 + Vite

Vue3 老手:

  • 直接 Vue3 + TS + Vite
  • 不要尝试“反向改造普通 Vue 项目”

迁移策略:

  • 以 uni-app 模板为基准
  • 迁移业务代码
  • 而不是迁移工程结构

八、一句话总结

不是 uni-app 不需要 package.json,而是传统模式下,HBuilderX 把一切工程细节都帮你“藏起来了”。

Vue3 + Vite 模式,是把构建权交还给开发者。

最后更新时间:
贡献者: 何风顺
上一页
Monorepo + uni-app 开发中监听 / 热更新机制差异与解决方案
下一页
为什么 uni-app 在 Monorepo 中必须做「环境区分」,而 Web 项目却不用?