别再搞混了!uni-app 中 node_modules 和 uni_modules 到底是什么关系?

本文面向刚接触 uni-app 的开发者(尤其是从原生 iOS/Android 转过来的同学)。如果你打开一个 uni-app 项目,看到根目录下同时躺着 node_modulesuni_modules 两个文件夹,脑子里冒出一句"这俩有啥区别?我到底该用哪个?"------那这篇文章就是为你写的。


一、先讲一个故事:原生开发者的困惑

你写了五年 iOS,闭着眼睛都能敲出 pod install。某天领导说:"咱们要搞跨端,你研究一下 uni-app。"

你打开项目,发现根目录下有两个长得很像的文件夹:

go 复制代码
my-project/
├── node_modules/      ← 这是什么?
├── uni_modules/       ← 这又是什么?
├── package.json
├── ...

你的第一反应是:"uni_modules 是不是就是 uni-app 版的 node_modules?"

答案是:不是。 它们是两个完全不同定位的东西,只是名字长得像。搞混了会走很多弯路。


二、一句话搞清楚

node_modules uni_modules
一句话 装"纯 JS 逻辑库"的文件夹 装"跨端功能插件"的文件夹
类比 你的"螺丝刀、扳手"工具箱 你的"发动机总成、变速箱总成"零件库

node_modules 里放的是 lodash、dayjs、pinia 这种纯代码逻辑,不涉及任何平台原生能力。

uni_modules 里放的是 DCloud 插件市场的插件,这些插件可能包含 UI 组件、原生 SDK 封装、云函数、页面模板,是一个完整的"功能包"。


三、node_modules 详解:你的 JS 工具箱

3.1 它是什么

node_modules 是 Node.js 生态的标准目录,存放通过 npm installyarn addpnpm add 安装的 JavaScript/TypeScript 库。

不是 uni-app 特有的,任何前端项目(React、Vue、Angular)都有这个目录。uni-app 只是沿用了这个机制。

3.2 里面装什么

类别 举例 说明
状态管理 pinia, vuex 管理全局数据
工具函数 lodash-es, dayjs, uuid 日期处理、防抖节流、生成ID
数据校验 zod, yup 表单验证、接口数据校验
类型定义 @types/xxx TypeScript 类型提示
构建工具 vite, webpack, @dcloudio/xxx 编译打包相关

3.3 怎么用

python 复制代码
# 安装一个库
npm install dayjs

# 安装并标记为开发依赖
npm install -D @types/lodash-es

# 在代码中直接 import
import dayjs from 'dayjs'
const now = dayjs().format('YYYY-MM-DD HH:mm:ss')

和你在任何 Vue/React 项目中的操作完全一样,没有任何 uni-app 特殊之处。

3.4 什么不能放这里

这是最容易踩坑的地方:

❌ 不要装的 为什么 该怎么做
axios 小程序环境没有 XMLHttpRequest,axios 直接报错 uni.request() 或 uni_modules 中的网络封装插件
jquery 依赖 DOM 操作,小程序和 App 端没有 DOM 用 uni-app 内置的 DOM 操作或组件方式
lottie-web 只能跑在浏览器里 App 端去插件市场找 lottie 原生插件
任何需要调摄像头/蓝牙/GPS 的库 JS 层无法直接访问硬件 去 uni_modules 找对应的原生插件

判断标准很简单: 如果一个库需要调用平台原生 API(硬件、系统权限、原生 UI),它就不属于 node_modules,而应该去 uni_modules 找。

3.5 注意事项

  • 优先选 ESM 版本lodash-es 优于 lodash,因为小程序对 CommonJS(require/module.exports)支持不完整
  • 检查是否依赖 Node APIfspathBuffer 这些在小程序和 App 端不存在
  • 控制体积:微信小程序主包限 2MB,别一股脑全装,按需引入
  • 配好 .gitignorenode_modules/ 要忽略,靠 package-lock.json 恢复

四、uni_modules 详解:你的跨端能力仓库

4.1 它是什么

uni_modulesDCloud 官方定义的插件规范 (HBuilderX 3.1.0+ 引入)。它不是一个包管理器,而是一个目录约定------只要你的插件按照这个规范放文件,HBuilderX 和 uni-app 编译器就能自动识别和加载。

你可以把它理解为:uni-app 的"插件安装目录"

4.2 里面装什么

一个 uni_module 插件可以包含以下任意组合

内容 说明 举例
Vue 组件 跨端 UI 组件 日历选择器、文件上传器
JS SDK 封装好的 API 函数 加密解密、格式化
UTS 原生代码 编译为 Swift/Kotlin/ArkTS 的原生实现 支付SDK、地图、推送
uniCloud 云函数 Serverless 后端 用户登录、数据存储
页面模板 完整的页面 登录页、商品详情页
DB Schema 数据库校验规则 表单自动校验

4.3 长什么样(目录结构)

lua 复制代码
uni_modules/
└── uni-camera-pro/                ← 插件ID(唯一标识)
    ├── package.json               ← 插件的"身份证":名称、版本、依赖
    ├── readme.md                  ← 使用文档
    ├── changelog.md               ← 更新日志
    │
    ├── components/                ← Vue 组件
    │   └── camera-view.vue
    │
    ├── js_sdk/                    ← JS/TS API
    │   └── index.ts
    │
    ├── utssdk/                    ← 🔑 原生代码(核心!)
    │   ├── interface.uts          ← 统一接口定义
    │   ├── app-ios/               ← iOS 实现
    │   │   ├── index.uts
    │   │   └── config.json        ← 声明用哪些 CocoaPods 库
    │   ├── app-android/           ← Android 实现
    │   │   ├── index.uts
    │   │   └── config.json        ← 声明用哪些 Maven 库
    │   └── app-harmony/           ← 鸿蒙实现
    │       ├── index.uts
    │       └── config.json        ← 声明用哪些 OHPM 库
    │
    ├── uniCloud-aliyun/           ← 云函数(可选)
    └── pages/                     ← 页面模板(可选)

4.4 怎么安装

方式一:HBuilderX 插件市场导入(最推荐)

arduino 复制代码
HBuilderX 菜单栏 → 工具 → 插件安装 → 浏览插件市场
→ 搜索你需要的插件 → 点击"导入插件" → 选择目标项目 → 完成

这是最简单的方式,HBuilderX 会自动帮你:

  • 创建 uni_modules/插件名/ 目录
  • 下载所有文件
  • 处理子依赖
  • 合并 pages.json 配置(如果需要)

方式二:手动复制

markdown 复制代码
1. 去 https://ext.dcloud.net.cn 搜索插件
2. 下载 zip 包
3. 解压到项目的 uni_modules/ 目录下
4. HBuilderX 自动识别

方式三:Git 引入

bash 复制代码
# 如果是团队自研的私有插件,放在 Git 仓库中
git clone https://github.com/your-team/uni-payment-sdk.git uni_modules/uni-payment-sdk

⚠️ 重要:不能用 npm install 安装 uni_modules 插件。 它们不走 npm registry,有自己独立的分发和识别机制。

4.5 怎么使用

安装完成后,根据插件类型不同,使用方式不同:

Vue 组件类:

xml 复制代码
<template>
  <!-- 直接当组件用,无需 import(easycom 自动识别) -->
  <uni-file-picker
    file-mediatype="image"
    @select="onFileSelect"
  />
</template>

JS SDK 类:

javascript 复制代码
// 需要手动 import
import { encrypt, decrypt } from '@/uni_modules/my-crypto/js_sdk/index.ts'

const cipher = encrypt('hello world')

UTS 原生插件类:

javascript 复制代码
// 同样 import 使用
import { pay, isInstalled } from '@/uni_modules/uni-payment-sdk'

// 调用
const result = await pay({ orderId: '12345', amount: 9.9 })

4.6 版本管理

这是 uni_modules 最大的"短板"------没有 lock 文件

npm (node_modules) uni_modules
版本锁定 ✅ package-lock.json ❌ 无
团队一致性保障 lock 文件 Git 提交快照

所以,最佳实践是:把 uni_modules/ 目录完整纳入 Git 版本管理。

bash 复制代码
# .gitignore

# ✅ 忽略 node_modules(通过 lock 文件恢复)
node_modules/

# ❌ 绝对不要忽略 uni_modules!
# uni_modules/  ← 不要写这行!!!

# 可以忽略插件内部的 node_modules
uni_modules/*/node_modules/

这样,团队所有人 git pull 之后,拿到的插件版本完全一致,不需要额外的"安装"步骤。


五、两者的关系:不是替代,是协作

很多人以为 uni_modules 是 node_modules 的"替代品",这是错的。它们是并行协作的关系:

csharp 复制代码
┌─────────────────────────────────────────────────────────┐
│                   你的 uni-app 项目                      │
│                                                         │
│   ┌───────────────────┐    ┌─────────────────────────┐ │
│   │   node_modules/   │    │     uni_modules/        │ │
│   │                   │    │                         │ │
│   │  • pinia          │    │  • uni-ui (组件库)      │ │
│   │  • dayjs          │    │  • uni-file-picker      │ │
│   │  • lodash-es      │    │  • my-payment-sdk       │ │
│   │  • zod            │    │  • uni-permission       │ │
│   │                   │    │  • uni-cloud-jwt        │ │
│   │  纯 JS 逻辑       │    │  跨端功能插件            │ │
│   │  不涉及原生       │    │  可能包含原生代码        │ │
│   └───────────────────┘    └─────────────────────────┘ │
│                                                         │
│   安装方式:npm install        安装方式:插件市场导入      │
│   版本保障:lock 文件          版本保障:Git 提交          │
│   来源:npmjs.com            来源:ext.dcloud.net.cn    │
└─────────────────────────────────────────────────────────┘

一个 uni_modules 插件甚至可以依赖 node_modules 中的库:

json 复制代码
// uni_modules/uni-camera-pro/package.json
{
  "dependencies": {
    "dayjs": "^1.11.0"    // ← 这个插件内部用了 dayjs
  }
}

安装插件后,你需要在插件目录内执行 npm install 来安装它的 npm 依赖(或者 HBuilderX 右键"安装插件依赖")。


六、实战决策:我到底该用哪个?

当你需要引入一个三方能力时,按这个流程判断:

objectivec 复制代码
我需要某个功能(比如:日期格式化 / 支付 / 地图 / 文件上传)
        │
        ▼
这个功能需要调用原生 API 吗?
(摄像头?蓝牙?GPS?推送?支付SDK?地图SDK?)
        │
   ┌─── NO ───┐              YES
   ▼          ▼               │
纯 JS 逻辑   需要原生能力      │
   │          │               │
   ▼          ▼               ▼
用 npm      去 DCloud 插件市场搜索
   │        ext.dcloud.net.cn
   ▼               │
npm install   ┌─── 找到了 ───┐
   │          ▼              ▼ 没找到
   ▼       导入到         自己写 UTS 插件
完成 ✅    uni_modules/   (封装各端原生 SDK)
              │              │
              ▼              ▼
           完成 ✅        完成 ✅

举几个具体例子:

需求 判断 做法
日期格式化 纯 JS,不需要原生 npm install dayjs → node_modules
全局状态管理 纯 JS npm install pinia → node_modules
微信支付/支付宝支付 需要原生 SDK 插件市场找支付插件 → uni_modules
高德地图 需要原生 SDK 插件市场找地图插件 → uni_modules
图片选择 + 上传 需要原生(相册权限) 插件市场找 uni-file-picker → uni_modules
字符串加密 纯 JS npm install crypto-js → node_modules
蓝牙设备连接 需要原生 插件市场找蓝牙插件 → uni_modules
表单数据校验 纯 JS npm install zod → node_modules

七、常见踩坑与 FAQ

Q1:我在 node_modules 里装了 axios,为什么小程序端报错?

因为 axios 底层依赖 XMLHttpRequest,而小程序环境没有这个对象(小程序用的是 wx.request)。解决方案:uni.request() 或封装一个基于 uni.request 的请求库。

Q2:我能不能用 npm install 安装 uni_modules 里的插件?

不能。 uni_modules 插件不发布在 npm registry 上,它有自己的目录结构和识别机制。必须通过 HBuilderX 插件市场导入或手动复制。

Q3:uni_modules 需要加到 .gitignore 吗?

绝对不要。 uni_modules 没有 lock 文件,如果不纳入 Git,你的同事拉取代码后就没有这些插件,项目直接跑不起来。

Q4:node_modules 需要加到 .gitignore 吗?

需要。 node_modules 体积大(动辄几百 MB),而且可以通过 package-lock.json + npm ci 精确恢复,没必要提交到 Git。

Q5:一个插件既用了 npm 库又有原生代码,怎么管理?

这种情况很常见。比如一个相机插件,JS 层用了 dayjs 做时间戳格式化(npm 依赖),原生层用了 SDWebImage(CocoaPods 依赖)。

  • npm 依赖 → 写在插件的 package.jsondependencies 里 → 在插件目录内 npm install
  • 原生依赖 → 写在 utssdk/app-ios/config.json 里 → 构建时自动 pod install

两层各管各的,互不干扰。

Q6:我修改了 uni_modules 里插件的源码,升级时会不会被覆盖?

会。如果你从插件市场更新了插件,你手动修改的代码会被覆盖。建议: 如果需要深度定制,Fork 插件源码(购买源码授权版),然后在 Git 中独立管理,不再走插件市场的更新流程。

Q7:CI/CD 中如何处理这两种依赖?

bash 复制代码
# 1. 恢复 npm 依赖
npm ci    # 使用 lock 文件精确恢复

# 2. uni_modules 已在 Git 中,无需额外操作
# 但如果插件内部有 npm 依赖:
for dir in uni_modules/*/; do
  if [ -f "$dir/package.json" ]; then
    (cd "$dir" && npm install)
  fi
done

# 3. 正常构建
npm run build:app-android

八、对照原生世界的类比

如果你是原生开发者,这个对照表可以帮你快速建立认知:

原生世界 uni-app 世界 说明
CocoaPods / SPM uni_modules 中的 app-ios/config.json 管理 iOS 原生 SDK 依赖
Gradle / Maven uni_modules 中的 app-android/config.json 管理 Android 原生 SDK 依赖
OHPM uni_modules 中的 app-harmony/config.json 管理鸿蒙原生 SDK 依赖
纯 Swift 算法库(无 UIKit 依赖) node_modules 中的纯 JS 库 不涉及平台 API,哪都能跑
完整的 iOS SDK(含 UI + 原生 + 资源) uni_modules 中的跨端插件 一个包解决一个完整功能
Podfile.lock Git 提交(uni_modules 无 lock) 版本锁定机制

九、总结:一张图记住

perl 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   node_modules                    uni_modules                   │
│   ═══════════                    ═══════════                    │
│                                                                 │
│   📦 纯 JS 逻辑库                 📦 跨端功能插件                 │
│                                                                 │
│   来源:npmjs.com                来源:ext.dcloud.net.cn         │
│   安装:npm install              安装:HBuilderX 导入            │
│   锁定:package-lock.json        锁定:Git 提交                  │
│   能力:仅 JS 运行时              能力:JS + 原生 + 云函数        │
│                                                                 │
│   例子:                          例子:                          │
│   • pinia(状态管理)             • uni-ui(UI 组件库)           │
│   • dayjs(日期处理)             • uni-file-picker(文件选择)    │
│   • lodash-es(工具函数)         • my-payment(支付 UTS 插件)   │
│   • zod(数据校验)               • uni-push(推送插件)          │
│                                                                 │
│   ⚠️ 加到 .gitignore             ✅ 必须纳入 Git                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

两者不是替代关系,是协作关系。
一个项目通常两个都有。

十、最后的建议

  1. 新项目第一件事: 配好 .gitignore(忽略 node_modules/,保留 uni_modules/
  2. 纯逻辑需求: 先想能不能用 npm 解决,能用就用,简单直接
  3. 涉及原生能力: 先去 ext.dcloud.net.cn 搜,90% 的需求都有现成插件
  4. 找不到现成插件: 学 UTS,自己封装。config.json 就是你的 "Podfile / build.gradle"
  5. 团队协作: uni_modules 必须提交 Git,这是没有 lock 文件的唯一补救方案
  6. 不要混用: 别试图把原生 SDK 塞进 node_modules,也别把纯 JS 库放进 uni_modules

记住这句话:node_modules 管"逻辑",uni_modules 管"能力"。逻辑是代码,能力是功能。两者配合,才是一个完整的 uni-app 项目。


本文基于 uni-app 官方文档(uniapp.dcloud.net.cn)及 DCloud 插件市场规范整理。uni-app 框架持续迭代,如有细节变化,以官方最新文档为准。

相关推荐
kyriewen2 小时前
面试官问"这段逻辑为什么这样写"——我才发现,这半年我写的代码,我自己都解释不了
前端·javascript·面试
谢小飞2 小时前
大文件上传很难?学会这招,GB文件秒传不是梦
前端·node.js
程序员黑豆2 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
To_OC3 小时前
别只拿 useRef 绑 DOM 了,搭配 Web Worker 解决页面卡顿才是真的香
前端·react.js·dom
IT_陈寒4 小时前
Vue的响应式让我原地破防,原来问题出在这
前端·人工智能·后端
子兮曰4 小时前
Bun vs Node.js 深度对决:跑分快 4 倍,真实业务只剩 3%,2026 年到底该怎么选?
前端·后端·typescript
计算机魔术师4 小时前
终端用户
前端
码事漫谈5 小时前
把 AI 拆掉,你的系统还能跑吗?
前端·后端
默_笙6 小时前
⛵ 我用 React + TS 做了个"调色盘",顺便学会了企业级项目的目录架构
前端·javascript