迁移说明:从 Apache Weex 迁移到 uni-app

May 22, 2026 · View on GitHub

本文档伴随 feat/migrate-uniapp 分支落地。请先阅读 README.md 了解新工程整体;本文聚焦"为什么迁、怎么迁、还剩什么"。


1. 背景与动机

1.1 Apache Weex 的现状

时间事件
2016-12阿里巴巴将 Weex 捐给 Apache 基金会,进入孵化器
2018从孵化器毕业,成为 Apache 顶级项目
2019–2021社区活跃度持续下降
2022-06进入 Apache Attic(项目坟场),官方不再发布版本,不再修复任何安全/兼容性问题

1.2 npm 生态状况

  • weex-vue-renderweex-loaderweex-toolkitweex-js-runtime 等核心包最近一次有意义发布停留在 2019–2020。
  • Vue 2(Weex 唯一支持的 Vue 版本)已于 2023-12-31 EOL
  • 老 Weex 工具链依赖 node-sass^4webpack^3babel-core^6,在 Node 18+ 下会因 native binding、glob 1.x、URL Whatwg 等差异编译失败。

1.3 用户需求约束

用户提出三条硬要求:

  1. 依赖必须使用近期发布版本
  2. 15 天供应链冷却:包发布时间 < 15 天的版本禁止安装(防 npm typosquat / supply-chain 攻击);
  3. 要可跑、能 e2e、能 release

在 Weex 生态下第 1、3 两条已不可能满足(生态 5 年没动了);只能换框架。


2. 候选方案对比

方案端覆盖与 Vue 兼容鸿蒙维护活跃度工作量
uni-app(传统 Vue 3 版)H5 / 小程序 / Android / iOSyes 直接复用 Vue 模板no
uni-app x(uvue + UTS)+ HarmonyOS Nextno 改写为 UTS 语言yes
React NativeAndroid / iOSno 完全异构partial 第三方支持极高
Flutter全端no 完全异构yes极高(已有 Flutter 版)

最终选 uni-app 传统版

  • 旧工程是 Vue 模板,迁移路径最直接;
  • 不需要鸿蒙;
  • 一次构建覆盖 H5 + 微信小程序 + Android/iOS App;
  • DCloud 至 2026 仍在每月滚动发版,包发布密度可满足"15 天内有合规版本"。

3. 关键技术决策

3.1 uni-app 版本号选择

uni-app 的 dist-tag 设计较特殊:

  • latest 指向 vue 2 版本(兼容旧项目)
  • vue3 指向 vue 3 版本

我们需要 vue 3,所以显式锁定3.0.0-5000720260410001(2026-04-13 发布,距今 38 天,安全过冷却)。

3.2 Vite 5 而非 8

npm view vite latest = 8.0.10(2026-04-23),但:

  • uni-app 官方至 2026-04 主线适配 vite 5/6;
  • vite 8 是 6 周前刚发布的大版本;
  • vite@5.2.8(2024-04-04,沉淀 25 个月)以保稳定;该版本被 @dcloudio/vite-plugin-uni 的 peerDependencies 钉死,不能升级。

3.3 axios 而非 uni.request

uni-app 自带 uni.request 已可工作,但:

  • 拦截器、取消、超时控制不如 axios;
  • 全栈类型支持差;
  • axios@1.16.0 满足冷却(2026-05-02 发布,19 天)。

成本:H5 端体积 +13 KB(gzipped),可接受。

3.4 登录方式:Basic Auth -> PAT

GitHub 在 2020-11-13 起强制弃用 POST /authorizations 端点(返回 410)。原 Weex 工程仍使用 Basic Auth + 自动换 OAuth token 流程,已不可用。

新版改为:

  • 用户在 GitHub 自助创建 Personal Access Token (classic)
  • 至少勾选 repousernotifications scope;
  • 在登录页粘贴 PAT,本工程仅做一次 GET /user 验证 PAT 有效性。

GitHub 没有官方 trending API。原工程 src/core/net/trending/GitHubTrending.js 通过:

  1. 直接 fetch https://github.com/trending/<lang>?since=daily
  2. himalaya 解析 HTML;
  3. 提取仓库列表。

H5 端会被 CORS 阻断(github.com 不允许跨域),且 GitHub 页面结构变更频繁,长期维护成本高。

当前实现src/api/trending.ts 三级回退:

  1. GSY 官方 trend APIhttps://guoshuyu.cn/github/trend/list + api-token: 4d65e2a5626103f92a71867d7b49fea0)——主路径,与同系列 RN/Flutter/Kotlin 项目一致;
  2. GitHub Search API/search/repositories?q=created:>...&sort=stars)——GSY 后端不可达时兜底;
  3. 本地 mock 数据——双双失败时保证 UI 不空白。

H5 端通过 vite.config.ts/gsy-trend 代理绕过 CORS 并注入 api-token;非 H5 端直发。GSY 后端在抓 github.com 超时时会返回 200 + body{status:500,error}trending.ts 会主动识别该错误形态并走兜底。


4. 供应链 15 天冷却策略

4.1 实现

scripts/check-deps-cooldown.mjs

  1. package.jsondependencies / devDependencies / optionalDependencies
  2. 跳过非精确版本(带 ^ ~ >=git:// 等)——这些应通过 lockfile 间接管理;
  3. 对每个精确版本调 https://registry.npmjs.org/<pkg> 拿到 time 字段,找出该版本的发布时间;
  4. 若发布时间距今 < 15 天,fail 并退出
  5. fallback:npmjs.org 不通时切到 npmmirror.com

4.2 触发点

  • npm install 自动通过 package.jsonpreinstall 钩子触发;
  • CI:npm run verify:cooldown(独立 step,见 .github/workflows/release.yml)。

4.3 调整窗口

环境变量 DEPS_COOLDOWN_DAYS,默认 15,可调小用于本地快速验证。生产 / 发版必须 ≥ 15。

4.4 当前 22 个直接依赖的合规情况

执行 npm run verify:cooldown 输出 [cooldown] OK — 22 package(s) pass.;具体每个版本的发布日见 package.json + npm view。


5. 目录结构对比

旧(Weex)新(uni-app)说明
src/entry.js + src/router.js + src/store.jssrc/main.ts + src/pages.json(声明式路由)+ src/stores/*.ts路由由 pages.json 声明;store 改 Pinia
src/components/*.vue(页面)src/pages/<route>/index.vueuni-app 强制每页一个目录
src/components/widget/*.vue(widget)src/components/*.vue(待补)命名空间合并;本轮还未搬
src/core/net/*.jssrc/api/*.tsTS + axios
src/core/store/modules/*.jssrc/stores/*.tsVuex -> Pinia
src/core/common/*.jssrc/utils/*.tsTS 化
src/config/Config.jssrc/config/index.tsTS 化
configs/webpack.*.conf.jsvite.config.ts全部消失,由 vite 接管
platforms/android + platforms/iossrc/manifest.json 配置不再维护原生壳,最终用离线打包工程或云打包
.babelrc .postcssrc.js已删除uni-app + vite 自带处理

5.5 UI 对齐策略(不重画原 App)

原则:所有可视产物按原 GSYGithubAppWeex 的设计稿/资源/色盘复刻,不引入新视觉语言

5.5.0 强制工作流(每动一个 page 必须遵守)

任何 src/pages/<name>/index.vue 的新建或修改,都必须按下面顺序完成,不允许跳步:

  1. 读原文件src/components/<同名>Page.vue(旧 Weex Vue2 实装),逐节看 template / script / style
  2. 读引用 widget:原 Page 引用的 src/components/widget/*.vue 全部读完,理解 list item / icon / 颜色 / 字号
  3. 读 tokensrc/config/Config.js + src/config/styles.scss,确认色值、间距、iconfont 编码
  4. 写差异清单:在 PR 描述或 commit body 列出「原版 X 的 UI 元素 -> 新版用 Y 实现」的对照
  5. 再写代码:严格用对照表里的元素,不允许"想象成 GSY 风格",不允许新造卡片/icon/色彩
  6. 截图比对(H5 dev 模式 + 原 app 截屏,并排对比):列出还存在的差异并说明是否平台限制

反例(已经发生过的,必须避免):

  • 自创"两行 crumb 卡片"代替原版「水平 scroller 面包屑 . > a > b >
  • 给目录 icon 涂蓝色(原版统一 $--theme-color 深灰)
  • 给文件行加右箭头(原版只有目录才有箭头 \ue610
  • <pre> 本地渲染代码(原版借 GitHub HTML 走 webview)
  • 用 iconfont 编码 \ue6e1 / \ue63e(原版是 \ue793 / \uea77,且字体族是 wxcIconFont

设计 token 对照(从原 src/config/Config.js 抽取)

Token旧(Weex Config)新(src/uni.scss
主题深灰primaryColor = '#3c3f41'$gsy-theme-color$uni-color-primary
主题深色primaryDarkColor = '#121917'$gsy-theme-dark
主题浅色primaryLightColor = '#42464b'$gsy-theme-light
强调蓝actionBlue = '#267aff'$gsy-action-blue
浅灰底miWhite = '#ececec'$gsy-mi-white
容器底subLightTextColor = '#f2f3f4'$gsy-container
输入字$gsy-input-color = #666666
灰阶subTextColor = rgba(97,97,97,0.9)$gsy-gray
阴影旧 styles.scss box-shadow$gsy-box-shadow

Utility 类(从原 [src/config/styles.scss] 移植到 [src/styles/global.scss])

gsy-card-white / gsy-card-black / gsy-card-black-full / gsy-text-line-three / gsy-text-line-one / gsy-content-text-black-bold / gsy-content-text-gray / gsy-content-text-white / gsy-name-text / gsy-name-text-theme / gsy-name-text-white / gsy-user-text-theme,全部用 rpx 重写,同时保留旧名 gsy-divider / gsy-text-primary / gsy-text-grey / gsy-text-error

资源迁移

已落地的页面对齐

  • WelcomePage -> pages/welcome/index.vue:全屏 welcome.png(aspectFill)+ 底部版本号
  • LoginPage -> pages/login/index.vue:深主题色背景 + 600rpx 居中白卡(border-radius 10rpx + box-shadow)+ 160rpx logo + "登录到 GitHub" 标题 + PAT 说明 + 主题色描边输入框 + 主题色按钮 + 主题色 switch(注意:因 GitHub 已弃用 Basic Auth,登录方式从用户名/密码改为 PAT)
  • MainPage -> pages/main/index.vue:顶部深主题色 NavigationBar(标题居中 + 右侧 iconfont 搜索图标)+ 主体白卡 + iconfont 占位
  • TrendPage -> pages/trend/index.vue:今日/本周/本月 segmented + 仓库白卡(作者蓝 + 仓库名主题色加粗 + 描述灰 + 语言 chip 浅白底主题色字 + iconfont star/xing + stars-added 警告色)
  • TabBar -> pages.json 全局 tabBar:深主题色选中、灰未选、白底,4 项(动态 / 趋势 / 搜索 / 我)

一处关键 bug(H5 资源路径)

manifest.json > h5.router.baseh5.publicPath 默认 ./(相对路径),导致 <image src="/static/logo.png">pages/login/index 路由下被解析为 /pages/login/static/logo.png 而 404。修复:两者均改为 /(站点根),见 src/manifest.json

5.6 原工程 UI 词汇表(强制 token 据点)

任何 page/widget 的 UI 决策必须从此表取值,不允许"想象"或"按 GSY 风格自由发挥"。 来源已 Read 通:Config.js / styles.scss / MainTabConfig.js / RepositoryTabConfig.js / IconConfig.js / iconfont.css / NavigationBar.vue / TabBar.vue / TopTabBar.vue / RepositoryHeadItem.vue / RepositoryItem.vue / SearchPage.vue / DynamicPage.vue / CodeDetailPage.vue / WebComponent.vue / PopoverComponent.vue

颜色(Config.js L17-L22

名称用途
primaryColor#3c3f41主题色,顶栏 / 深卡 / 主题 tab 选中字
primaryDarkColor#121917深色衬底
primaryLightColor#42464bwebDracula 等略浅
actionBlue#267affuserName 链接色、强调
miWhite#ececec浅白底 chip / 浅分割
webDraculaBackgroundColor#282a36code-detail webview 背景
subTextColorrgba(97,97,97,0.9)灰阶副文字
subLightTextColor#f2f3f4page 容器底

尺寸(750 设计稿,rpx 一致)

名称用途
navigatorbBarHeight100rpx顶栏(不含 status bar)
statusHeight32rpxiOS 状态栏占位(H5 用 safe-area)
mainTabBarHeight120rpx主页底部 tabBar
reposDetailTopTabBarHeight80rpx仓库详情 4tab
controlBarHeight80rpx"动态/提交"切换条

iconfont(family 必须wxcIconFont,已在 global.scss 注册)

用途unicode用在哪
动态 tab\e684主页底 tab 1
推荐 tab\e818主页底 tab 2
我的 tab\e6d0主页底 tab 3
搜索\e61c顶栏右上 / SearchPage
返回\e78a顶栏左上
右箭头\e610文件目录行 / cell 跳转
目录\e793RepositoryFileListPage
文件\ea77RepositoryFileListPage
star/watchers\e643RepositoryHeadItem
forks\e67eRepositoryHeadItem
subscribers\e681RepositoryHeadItem
issues\e661RepositoryHeadItem
GitHub\ea0a"在 GitHub 打开"等
评论\e6baIssueItem
user\e63eRepositoryItem 作者行

卡片体系(styles.scss L26-L33,已迁到 global.scss L150-L173

class背景圆角用途
card-white-wrapper700rpx#fff10rpxRepositoryItem 列表卡
card-black-wrapper710rpx$gsy-theme-color15rpxRepositoryHeadItem 顶部卡
card-black-full-wrapper100%$gsy-theme-color15rpx 仅下圆顶栏直连大块(如详情顶部融合)

主页 tabBar(MainTabConfig.js L8-L22

  • 文案:动态 / 推荐 / 我的(不是"动态/趋势/我"——要修 pages.json L130-L142
  • 选中字色:primaryColor,未选中:subTextColor
  • 背景:#fbfbfb,iconFontSize:40px
  • 实现:用 codePoint 渲染(uni 内置 tabBar 不支持 iconfont,必须自画 components/widget/TabBar.vue 等价物)

仓库详情顶 4tab(RepositoryTabConfig.js

  • 文案:详细信息 / 动态 / 文件 / Issue
  • 背景:primaryColor,选中字白 #FFFFFF,未选中字 subTextColor
  • 满宽 750rpx + 主题色背景 + box-shadow
  • 标题居中 36rpx 白字
  • 左右 30rpx wxcIconFont icon 各占 100rpx

"动态/提交"切换 control-container

  • 条高 80rpx,主题色背景,左右 30rpx 圆角,文字白色 32rpx
  • 活动项:略浅 primaryLightColor 圆角条衬底

6. 页面迁移落地

15 个页面全部迁移完成(与 pages.json 一致),详情见 docs/parity-checklist.md 的 parity 矩阵:

路由原工程文件状态
pages/welcome/index.vueWelcomePage.vue[x]
pages/login/index.vueLoginPage.vue[x] PAT 登录
pages/main/index.vueMainPage.vue + DynamicPage.vue[x] 合一
pages/trend/index.vueTrendPage.vue[x] GSY 官方 API + 三级回退 + 10 语言 chip
pages/person/index.vuePersonPage.vue[x]
pages/search/index.vueSearchPage.vue[x] 仓库/用户 2 tab + 搜索历史
pages/setting/index.vueSettingPage.vue[x]
pages/repository-detail/index.vueRepositoryDetailPage.vue + 三个 sub Tab[x] 4 tab 内 v-show + Star/Watch/Fork/Branch + tab 切不重拉
pages/user-info/index.vueUserInfoPage.vue[x]
pages/code-detail/index.vueCodeDetailPage.vue[x] Dracula 主题
pages/issue-detail/index.vueIssueDetailPage.vue[x] 4 操作栏 + 评论长按弹层 + 分页
pages/edit-issue/index.vueEditIssuePage.vue[x] 4 种 type
pages/common-list/index.vueCommonListPage.vue[x] 7 dataType 分支
pages/web/index.vueWebPage.vue[x]
pages/dynamic/index.vueDynamicPage.vue(独立入口保留)[x]

冗余路由清理:原 RepositoryDetailInfoPage / RepositoryFileListPage / RepositoryIssueListPage 这 3 个独立路由已被 RepositoryDetailPage 4 tab 内的 v-show 实现替代,独立路由文件已删除(见 commit a117106)。


7. 已知遗留问题

  1. easycom 自动注册pages.json 已配置 ^uni-(.*) 正则,组件用即引;自定义组件(GSY 自有)放 src/components/ 下也能 easycom,注意命名冲突。
  2. i18n 入口未启用:vue-i18n 已在 dependencies 里,但 main.ts 未挂;待写 src/locale/ 后启用。
  3. TypeScript 严格模式开启tsconfig.json strict=true;老代码搬运时会有大量 any,建议局部 // @ts-expect-error 标注 + Issue 跟踪。
  4. 图片资源:已从旧工程复制 logo.pngwelcome.pngdefault_img.pngsrc/static/
  5. iconfont:已从旧工程复制 iconfont.ttf / iconfont.woff / iconfont.csssrc/static/font/,并在 src/styles/global.scss 通过 @font-face 注入,常用图标(icon-shijian 动态、icon-rifangwenqushi 趋势、icon-sousuo 搜索、icon-ren 我、icon-star/icon-xing 星标/分支、icon-GitHubicon-fanhuiicon-pinglun)已声明对应 unicode。

8. 测试策略

单元测试

旧工程用 Karma + Mocha + PhantomJS,全部移除。新工程暂未内置单测框架;建议引入 Vitest(与 Vite 原生集成),但需校验冷却。

E2E 测试

  • H5 端:Playwright(上次 stable 1.55.x 在 2026-04 发布,合规)
  • 小程序端:@dcloudio/uni-automator(已在 devDependencies)
  • App 端:手工 + adb 真机;自动化方案推荐 Appium 2 + WebDriverIO

真机回归

  • Android:adb install -r dist/build/app-plus/...apk,按 README "Android 真机/模拟器调试" 步骤;
  • iOS:必须 Mac + Xcode + 离线打包工程,windows 环境无法验证。

9. 后续 Roadmap

  • 完成 15 个页面迁移
  • 关键 widget 组件就地内联(EventItem / IssueItem / IssueCommentItem / RepositoryItem / RepositoryHeadItem / UserHeadItem / UserItem / FileItem / NavigationBar / TabBar / TopTabBar / WebComponent / 长按弹层 等)
  • 接入 GSY 官方 trend API(替代原 himalaya 爬虫)
  • Playwright H5 e2e(14/14 PASS,含 trend 真实数据)
  • git 历史 author 全量重写为 carguo <35936982@qq.com>
  • 合并 feat/full-features -> master,发布 v2.0.0-uniapp GA
  • vue-i18n 入口接入 + 抽出现有中文 hardcode(保持原工程默认中文行为)
  • 引入 Vitest 单测覆盖 stores / utils
  • iOS 端真机验证(需 Mac 环境)
  • 小程序端 e2e 接 @dcloudio/uni-automator
  • RepositoryDetail Info tab 内"动态/提交"二级切换补完

License

MIT