uniapp源码解析(Vue3/Vite版)

背景

项目中遇到一个需求,路由不写在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源码写得简约,甚至可以说是简陋,哈哈哈。不过,思路应该是没有问题的。掌握解析源码的思路,后面的自然就水到渠成了。

个人总结的思路大致是:

  1. 找一个需求(有实际的业务需求最好),带着问题去找结果。
  2. 找到源码,跑起来,做好测试环境(这个非常重要,代码要不停的console,打印查看各个节点的结果,才能知道是怎么运作的)。
  3. 解析源码是怎样开局的。
  4. 根据需求开发一个相关联的插件或内容。

现在微信小程序也很火,挖个坑,下次有空,也解析uniapp的微信小程序看看。

相关推荐
JarvanMo2 小时前
Dart 3.10中的新的lint规则
前端
爱心发电丶2 小时前
基于UniappX开发电销APP,实现通话录音上传、通时通次
前端
sxjk19872 小时前
华为IMS系统主要接口备忘
运维·服务器·前端·核心网
T***u3332 小时前
前端Server Components性能分析 Server Components架构原理
前端
Q***f6352 小时前
前端动画性能优化,60fps实现技巧
前端
艾莉丝努力练剑2 小时前
【自动化测试实战篇】Web自动化测试实战:从用例编写到报告生成
前端·人工智能·爬虫·python·pycharm·自动化·测试
Mintopia2 小时前
💥 Trae Solo 编程 vs. Cursor:新机遇与新挑战
前端·人工智能·trae
Mintopia2 小时前
🌌 长上下文 AIGC 的性能瓶颈:Web 端技术的突破与妥协
前端·人工智能·trae
天蓝色的鱼鱼2 小时前
前端小白Express入门:初识Web框架与项目搭建
前端·node.js·express