背景
项目中遇到一个需求,路由不写在pages.json文件中,需要在解析pages.json文件前,把写在其他地方的路由,复制到pages.json中。
因此我需要了解uniapp中pages.json的解析逻辑,借此机会,正好解析一下uniapp的vite框架源码,搞起搞起。
步骤一 uni命令做了什么?

按照uniapp官方教程,创建uniapp-vite新项目=》pnpm i安装依赖
找"dev:h5": "uni"的命令
scipts的执行路径在node_modules/.bin。找到uni.CMD(window系统是这个),在uni.CMD中,找到执行文件是\node_modules@dcloudio\vite-plugin-uni\bin\uni.js。uni.js执行../dist/cli/index.js文件

\node_modules@dcloudio\vite-plugin-uniceshi\dist\cli\index.js是核心文件。主要是处理命令中携带的参数。例如:打包后文件的目录。 

由于我执行的是最基础的命令"dev:h5": "uni"。这个我可以不理,直接看配置完文件后,uniapp做了什么。追寻过程不赘述。最终执行的是\node_modules@dcloudio\vite-plugin-uni\dist\cli\server.js文件里面的createServer方法。也就是启动了vite的本地服务。 
总结:"dev:h5": "uni"命令作用是将命令行的参数进行来配置,最终启动了vite的服务。由于"dev:h5": "uni"命令是最基础的命令,因此启动的也是最基础的vite服务。整个过程中,没有对代码文件进行任何处理。那么说明uniapp是通过vite插件对代码文件进行处理。
步骤二 uniapp的vite插件做了什么?
uniapp的vite插件十分简单,vite.config.ts文件引入一个vite-plugin-uni插件即可。不过随着深入探索,发现并不简单。
ts
//vite.config.ts文件
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
export default defineConfig({
plugins: [uni()],
});
查阅@dcloudio/vite-plugin-uni的文件,发现编译后的代码有点难读懂。上uniapp官网找源码,没找到。官网没有,那就在GitHub找,GitHub也没有,那就上gitee找。本想gitee希望不大,没想到源码就在gitee。nice!源码链接
我仅知道这三个源码渠道,大佬如果有其他渠道跪求推荐一下,多条路子。
下载源码uni-app-next。源码版本是:3.0.0-alpha-4060120250328001。那么我们的示例也pnpm更新到此版本,避免差异。
源码pnpm i按照完依赖后。我图省事,将vite-plugin-uni插件直接复制到示例代码上,直接开干。事实证明歪路子看起来省事,实则麻烦更多。大家别学我,总之还是正道好。正道应该是按照别人插件库提供的测试方法,进行调试。如果舍不得弃之前的示例,可以将整个示例复制过去,然后使用pnpm的link方法,改变依赖引用路径即可。
以上准备就绪后,开始对着源码,解析vite-plugin-uni插件。 插件路径:packages\vite-plugin-uni\src\index.ts
先打印插件最终的结果看看。发现vite-plugin-uni最终分裂出了一堆插件。我的思路是:查看是如何分裂出来的,然后根据我现在的业务需求,找到pages.json的处理插件,了解其原理看看怎么调整。

对packages\vite-plugin-uni\src\index.ts进行解读,入口方法是uniPlugin()。追寻路径是uniPlugin()=>createPlugins()=>./utils/plugin.ts目录=>initExtraPlugins()=>resolvePluginsByCliRoot()=>resolvePlugins()。
resolvePluginsByCliRoot和resolvePlugins方法就是vite-plugin-uni分裂出了一堆插件的始作俑者。 resolvePluginsByCliRoot读取项目的package.json文件,导入项目全部的依赖。resolvePlugins方法则是筛选出需要用到的依赖。筛选的方式是根据依赖的package.json文件,比如@dcloudio/uni-h5依赖目录下的package.json文件。判断package.json内容是否含有"uni-app"这个字段。
举个例子:
ts
{
"name": "@dcloudio/uni-h5",
"version": "3.0.0-alpha-4060120250328001"
//这个字段是关键!!!
//这个字段是关键!!!
//这个字段是关键!!!
"uni-app": {
"name": "uni-h5",
"apply": [
"app",
"h5",
"mp-weixin",
],
"uvue": true
},
"dependencies": {
"@dcloudio/uni-cli-shared": "workspace:*",
"detect-port": "^1.5.1",
"express": "catalog:",
}
}
筛选出含有"uni-app"的依赖后,还需要通过[uni-app][apply]进行二次筛选。比如上面示例中,apply对象包含"app", "h5","mp-weixin"。则是在app,h5,微信小程序三种环境下,都需要运行此依赖。若apply只有"h5",则只有编译H5才会使用此依赖。
总结: vite-plugin-uni除了导入自身的插件,还会根据不同的运行环境(比如H5,微信小程序)导入项目package.json中有特殊标记的依赖。
这样的设计思路,可以简化vite插件引入,vite.config.ts文件中只需要引入一个插件即可。且后续更新,也可以按照模块化进行更新,不需要把全部的内容都放到一个插件中。vite-plugin-uni插件的作用是制定了一套插件引入的规则,大家可以按照这个规则导入或开发所需要的插件。
步骤三 uniapp是怎么运作的?
由于我运行的环境是H5,我仅用H5进行举例。大家可根据此思路,解析自己所在运行环境的源码。
写个简单插件看一下代码文件编译的路径
ts
import type { Plugin } from "vite";
export default function (): Plugin {
return {
name: "测试插件",
transform(code, id) {
console.log('查看编译的文件顺序和代码', id, code)
},
};
}
路径和vite的入口文件一致,都是从@/src/main.ts开始。main.ts经过h5-manifest-json插件的作用,编译结果如下图: 
可以看到h5-manifest-json插件除了增加了一些配置外,还引入了pages.json文件,这正是我想了解的部分。
翻阅了一下源码,找到了处理pages.json的插件是uni:h5-pages-json。这插件的作用大概是加入了各种全局的方法、样式、变量等,把pages.json的路径,转换成页面的路由。插件编译结果如下图::

其他运行环境如微信小程序,APP等,估计还需要更多的插件进行改造。但H5不需要做太多的转义,直接按vite正常服务启动即可,uniapp的运作思路,大致就是如此。我就不一一解析每个插件的作用了。
依据我项目的需求,我原本是想着做一个插件,在uniapp插件之前生效,对pages.json文件进行改造,然后结束后再恢复原样,这样就能完全和uniapp的插件进行解构,但这样做弊端其实很多。但不和uniapp插件进行解耦,我又担心uniapp插件,我不可控。
这时候解析源码的好处就来了,我在uni:h5-pages-json插件中,发现了一个方法指向pages.json文件的方法defineUniPagesJsonPlugin。原来uniapp有专门的指向方法。这我就不用担心uniapp他们引入pages.json的方式变化,可以轻松的在uniapp插件修改pages.json之前,我们自己先行一步进行修改。
我的插件代码:
ts
import fs from "fs";
import path from "path";
import type { Plugin } from "vite";
import {
defineUniPagesJsonPlugin
} from '@dcloudio/uni-cli-shared'
export default function (): Plugin {
//使用uniapp提供的封装方法
return defineUniPagesJsonPlugin((opts) => {
return {
name: "vite-plugin-handleFile",
enforce: 'pre',//uniapp插件也是提前生效的,因此我们也要如此
transform(code, id) {
//使用uniapp提供的方法,找到pages.json文件,对pages.json进行处理
if (opts.filter(id)) {
.....
....
....//项目业务代码
}
},
};
})
}
结语
这篇uniapp源码写得简约,甚至可以说是简陋,哈哈哈。不过,思路应该是没有问题的。掌握解析源码的思路,后面的自然就水到渠成了。
个人总结的思路大致是:
- 找一个需求(有实际的业务需求最好),带着问题去找结果。
- 找到源码,跑起来,做好测试环境(这个非常重要,代码要不停的console,打印查看各个节点的结果,才能知道是怎么运作的)。
- 解析源码是怎样开局的。
- 根据需求开发一个相关联的插件或内容。
现在微信小程序也很火,挖个坑,下次有空,也解析uniapp的微信小程序看看。