阅读视图

发现新文章,点击刷新页面。

GitHub Fork 协作完整流程

GitHub Fork 协作完整流程

适用于:开源项目二次开发、插件扩展、给上游项目提交 PR(Pull Request)


一、Fork 是什么?

Fork 是把别人的 GitHub 仓库复制一份到你自己的账号下:

原仓库(upstream) → 你的仓库(origin)

你在自己的仓库里可以随意修改代码,而不会影响原项目。


二、整体协作流程

flowchart TD
    A[原项目 GitHub 仓库] -->|Fork| B[你自己的 GitHub 仓库]
    B -->|git clone| C[你的本地仓库]
    A -->|git fetch upstream| C
    C -->|git merge upstream/main| C
    C -->|git push origin| B
    B -->|Pull Request| A

三、Fork 项目(网页操作)

  1. 打开你要 Fork 的仓库,例如:

    https://github.com/vercel/next.js
    
  2. 点击右上角 Fork

  3. 选择你的 GitHub 账号

  4. 得到你的仓库:

    https://github.com/你的用户名/next.js
    

四、Clone 到本地

git clone https://github.com/你的用户名/next.js.git
cd next.js

五、配置 upstream(非常重要)

查看当前远程仓库:

git remote -v

你会看到:

origin https://github.com/你的用户名/next.js.git

添加原仓库:

git remote add upstream https://github.com/vercel/next.js.git

再确认:

git remote -v

应该是:

origin   你自己的仓库
upstream 原项目仓库

六、同步原项目最新代码(标准流程)

git checkout main
git fetch upstream
git merge upstream/main
git push origin main

含义:

用原作者的 main 更新你的 main,并推送到你自己的 GitHub


七、开发你的功能

git checkout -b my-feature
# 修改代码
git add .
git commit -m "Add my feature"
git push origin my-feature

八、提交 Pull Request

  1. 打开你的 GitHub 仓库

  2. GitHub 会提示:

    Compare & Pull Request
    
  3. 点击并填写说明

  4. 提交 → 原项目维护者审核


九、为什么必须使用 upstream?

远程名 作用
origin 你自己的 GitHub 仓库
upstream 原作者的 GitHub 仓库

你只向 origin push,但要从 upstream 获取最新版本。


十、fetch vs merge 的正确用法

sequenceDiagram
    participant U as GitHub upstream
    participant L as Local Repo
    participant O as GitHub origin

    U->>L: git fetch upstream
    L->>L: upstream/main 更新
    L->>L: git merge upstream/main
    L->>O: git push origin main
  • fetch = 下载真实世界
  • merge = 使用真实世界

永远不要跳过 fetch。


十一、错误示例(不要这样)

git merge upstream/main   # ❌ 如果你没先 fetch

你合并的可能是 几个月前缓存的 upstream,非常危险。


十二、最佳实践总结

Fork 项目的生存法则

只向 origin 写代码  
只从 upstream 读更新  
fetch 在前,merge 在后

node-sass 迁移 sass(dart-sass) 后样式报错?用 loader 先把构建救回来

Vue CLI 老项目迁移 dart-sass:用一个插件兼容 /deep/>>>calc(100%-16px)

仓库:vue-cli-plugin-sass-compat
GitHub:github.com/myltx/vue-c…

老的 Vue CLI 项目升级 Node/依赖后,经常会被迫从 node-sass(libsass) 迁移到 sass(dart-sass)。真正卡人的往往不是“装上 sass 就完事”,而是项目里存在大量历史写法,导致迁移后构建直接报错或样式编译不符合预期。

这篇笔记记录一个“过渡期方案”:通过 vue-cli-plugin-sass-compatsass-loader 后插入一个轻量 loader,对源码做字符串级兼容替换,让你先把构建跑起来,再逐步治理样式。

你可能遇到的两类典型坑

1) 深度选择器旧写法:/deep/>>>

在一些链路/组合下,旧写法可能触发解析问题(例如 dart-sass 报 Expected selector),或者在升级过程中需要统一成 Vue 推荐的写法。

目标:将这些旧写法尽量自动转换为 ::v-deep

2) calc() 运算符空格:calc(100%-16px)

sass(dart-sass)calc() 表达式更严格,常见历史写法例如:

.a { width: calc(100%-16px); }

可能需要改成:

.a { width: calc(100% - 16px); }

目标:在迁移过渡期,自动补上二元运算符(+/-)两侧空格,避免全仓库手工替换造成巨大 diff。

方案:vue-cli-plugin-sass-compat

这个插件做的事情很克制:

  • 作为 Vue CLI Service 插件,通过 chainWebpacksass-loader 后插入一个 loader
  • 只处理你项目内的 .scss/.sass 文件(默认跳过 node_modules
  • 以“迁移过渡”为目标做最小替换:
    • /deep/>>>::v-deep
    • calc(...) 中的二元 +/- 自动补空格(尽量避开一元运算等场景)

使用前置:先完成依赖迁移(必做)

本插件不负责替你替换依赖。使用前请先把项目从 node-sass 迁移到 sass(dart-sass)

npm rm node-sass
npm i -D sass

然后正常跑一遍安装:

rm -rf node_modules
npm i

安装与使用

方式 A:已发布到 npm(推荐)

npm i -D vue-cli-plugin-sass-compat

可选:迁移检查命令(doctor)

插件在 serve/build 首次执行时会做一次轻量检查:如果检测到仍存在 node-sass 或尚未安装 sass,会打印提示。

也可以手动运行:

vue-cli-service sass-compat:doctor

可选配置(vue.config.js

默认两项修复都开启(true)。需要精细控制时可以这样写:

module.exports = {
  pluginOptions: {
    sassCompat: {
      fixDeep: true,
      fixCalc: true
    }
  }
}
  • fixDeep:是否将 /deep/>>> 等旧写法转换为 ::v-deep
  • fixCalc:是否修复 calc(100%-16px)calc()+/- 运算符空格

转换示例

深度选择器

输入:

.a /deep/ .b {}
.a >>> .b {}

输出(示意):

.a ::v-deep .b {}
.a ::v-deep .b {}

calc() 空格

输入:

.a { width: calc(100%-16px); }

输出:

.a { width: calc(100% - 16px); }

工作原理(简述)

  • index.js:通过 api.chainWebpack,在 sass/scss 规则里找到 sass-loader,并在其后插入 sass-compat-loader
  • sass-compat-loader.js:拿到每个样式文件的源码做字符串替换
    • 跳过 node_modules
    • /deep/ 直接替换为 ::v-deep
    • >>> 替换为 ::v-deep,并尽量补齐必要空格
    • calc(...) 做一次括号配对扫描,只在 calc() 内尝试给二元 +/- 补空格

边界与建议

  • 这是“迁移过渡”工具:建议你在构建恢复稳定后,逐步把业务代码里真正的历史写法治理掉,最终可以移除该插件
  • calc() 修复目前只处理二元 +/-,不会尝试覆盖 */ 等更复杂场景
  • 如果你项目里对 ::v-deep 的使用有更严格的团队规范,建议在过渡期结束后统一做一次规范化替换

最后

如果你也在做 Vue CLI 老项目的 node-sass -> sass(dart-sass) 迁移,欢迎直接试用这个插件;也欢迎提 issue 描述你遇到的“历史写法”,我会优先考虑把高频场景纳入兼容范围。

记一次Vue 2主应用集成Vue 3子项目的Monorepo迁移踩坑指南

前言

最近在进行Monorepo架构调整,需要将一个现有的Vue 3(Vite)项目作为一个子应用 (apps/wj) 迁移到由Vue 2(Webpack)主导的大仓中。本以为只是简单的“文件夹移动”,结果在依赖管理、网络代理和端口映射上踩了一圈坑。

本文记录了从迁移到跑通全流程遇到的4个典型问题及解决方案。

坑点一:pnpm 严格模式下的“幽灵依赖”

💥 现象

将项目移入大仓后,执行 dev 脚本报错:

'vite' 不是内部或外部命令,也不是可运行的程序

或者启动后报错找不到 unplugin-auto-importvue-request 等插件。

🔍 原因

原项目可能使用 npm/yarn,存在依赖提升 (Hoisting) ,即 devDependencies 即使没写在 package.json 里,依靠根目录 node_modules 也能跑。 但迁移到 pnpm Monorepo 后,pnpm 的严格机制要求所有使用的包必须显式声明

✅ 解决

在根目录通过 --filter 为子应用补全依赖:

# 补全构建工具
pnpm add vite @vitejs/plugin-vue vue-tsc -D --filter wj

# 补全缺失的业务/构建插件
pnpm add unplugin-auto-import unplugin-vue-components -D --filter wj
pnpm add vue-request --filter wj

坑点二:Workspace 内部包的正确引用

💥 现象

Vite 启动报错:

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'configs' imported from ...

子应用试图引用大仓共享的配置包(packages/configs),但找不到模块。

🔍 原因

子应用虽然物理上在 monorepo 里,但 package.json 里没有声明对内部包的依赖,导致软链接未建立。

✅ 解决

使用 --workspace 协议建立软链:

# 将内部包链接给子应用
pnpm add configs --workspace --filter wj

注意:如果共享包内部也用了某些插件(如 @vitejs/plugin-vue),共享包自己也必须安装该插件,否则会报“父级依赖缺失”。


坑点三:Node 高版本 localhost 解析陷阱 (IPv6)

💥 现象

主应用配置了代理转发到子应用,但在浏览器访问时报 HTTP 500,终端报错:

Error: connect EACCES ::1:5192

🔍 原因

  • 环境: Node.js v17+
  • 机制: 主应用代理配置写了 target: 'http://localhost:5192'。Node 默认将 localhost 解析为 IPv6 地址 ::1
  • 冲突: 子应用 (Vite) 默认只监听 IPv4 (127.0.0.1)。主应用去 IPv6 端口找人,自然连不上。

✅ 解决

方案A(推荐): 修改主应用代理配置,强制使用 IPv4 IP。

// 主应用 vite.config.ts / vue.config.js
proxy: {
  '/wj': {
    target: 'http://127.0.0.1:5192', // 👈 不要写 localhost
    changeOrigin: true
  }
}

方案B: 让子应用监听所有地址。启动命令改为 vite --host


坑点四:主应用代理“漏气” (接口返回 HTML)

💥 现象

页面加载成功,但业务接口(如 /cmisp/api/xxx)报 304200,查看 Response 内容竟然是 index.html 的代码,导致 JSON 解析失败。

🔍 原因

主应用只代理了页面路由 /wj,但子应用发出的 API 请求是 /cmisp 开头的。 主应用不认识 /cmisp,将其当成了前端路由处理,直接返回了 index.html

✅ 解决

在主应用中补全 API 的代理转发规则:

// 主应用 vite.config.ts
server: {
  proxy: {
    // 1. 子应用页面资源
    '/wj': {
      target: 'http://127.0.0.1:5192',
      changeOrigin: true
    },
    // 2. 子应用 API 请求 (新增)
    '/cmisp': {
      target: 'http://127.0.0.1:5192', // 如果是 mock 数据走这里;如果是真实后端填后端 IP
      changeOrigin: true
    }
  }
}

总结

Monorepo 迁移不仅仅是文件搬运,核心在于:

  1. 依赖边界:pnpm 下必须“谁用谁装”。
  2. 网络互通:Node 高版本下 localhost 的 IPv6 坑需要格外注意。
  3. 路由接管:主应用作为网关,必须接管子应用的所有请求(包括静态资源和 API)。
❌