普通视图

发现新文章,点击刷新页面。
今天 — 2025年7月3日首页

前端多层加密?让“伪安全”更“伪”一点(狗头)

2025年7月3日 08:38

在浏览器端做加密,听上去就有点自欺欺人。很多开发者对前端加密的态度是:

“反正浏览器都能看到源码,前端加密有意义吗?”

实际上,这个问题的答案并不简单。前端加密无法抗拒主动攻击,但在某些场景下,比如数据混淆、防小白逆向、教学演示,它仍然具备合理存在的价值。

本篇:想聊的,就是一种典型的“前端多层加密”的实现思路。

我做了一个网站,大家可以简单体验一下:encryption-demo.pages.dev/

image.png

多层加密的核心设计

前端多层加密本质上是将多个简单的加密/编码/哈希算法串联起来,形成一个相对复杂的处理链,从而提升对抗简单逆向的门槛。

image.png

  • 字符位移:基础可逆加密

  • 加盐:防止简单模式识别

  • Base64:编码增强,便于传输

  • 哈希迭代:引入不可逆过程,增加验证机制

  • 二次字符位移+编码:强化混淆效果

原理与实现

🔸 步骤1:字符位移加密

  • 类似凯撒密码,每个字母 +3
  • 极易破解,但适合作为第一步混淆
function simpleEncrypt(str) {
  return str.split('').map(c => {
    const code = c.charCodeAt(0);
    if (code >= 65 && code <= 90) {
      return String.fromCharCode(((code - 65 + 3) % 26) + 65);
    }
    if (code >= 97 && code <= 122) {
      return String.fromCharCode(((code - 97 + 3) % 26) + 97);
    }
    return c;
  }).join('');
}

🔸 步骤2:加盐

  • 在末尾增加一串随机盐(一般8位)
  • 盐 = 防止模式识别 + 防止撞库
function generateSalt(length = 8) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  return Array.from({length}).map(() => chars[Math.floor(Math.random() * chars.length)]).join('');
}

🔸 步骤3:Base64编码

  • 本质是编码,不是加密
  • 提供可见的编码形式,防止二进制乱码
const encoded = btoa(textWithSalt);

🔸 步骤4:再次字符位移

  • 加强混淆
  • 和第一次字符位移逻辑相同

🔸 步骤5:100次哈希迭代

  • 不可逆
  • 增加校验强度
  • 理论上,攻击者必须知道前面所有步骤,并暴力跑哈希
function customHash(str) {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = (hash << 5) - hash + str.charCodeAt(i);
    hash |= 0;
  }
  return hash.toString(16);
}
let result = input;
for (let i = 0; i < 100; i++) {
  result = customHash(result);
}

🔸 步骤6:最终Base64编码

  • 便于展示和传输
  • 增强文本兼容性

到底安全吗?

目标 多层加密能否达成?
防止数据被直接肉眼看到 ✅ 完全达成
防止抓包获取原始数据 ❌ 无法防御
防止逆向还原加密逻辑 ❌ 无法防御
增加小白逆向的时间成本 ✅ 有效
商业级数据安全保护 ❌ 完全不适用
教学/实验/流程展示 ✅ 非常合适

结论一句话:

这是一种“伪加密”...

尽管这种多层加密“不安全”,但它的流程化设计非常适合: 一步步拆解加密过程 —— 从字符到编码到哈希,看到每一步如何变化 —— 练习如何组合多个算法。

前端加密 ≠ 信息安全,但 ≠ 完全没用。

OK,以上便是本次分享~

欢迎加我:atar24,进技术群、交盆友,我会第一时间通过

昨天 — 2025年7月2日首页

OpenTiny训练营实操体验|轻松解锁TinyVue智能组件开发Web应用

2025年7月1日 09:27

1.背景描述

TinyVue 是一套企业级的 Vue 框架下的组件库,它具有跨端、跨框架的特性,同时支持 Vue2 和 Vue3,支持 PC 端和移动端浏览器。

本次实验是通过改造一个使用 TinyVue 组件的常规 Web 应用,秒变为一个能够对接大语言模型的智能应用。这个应用可以让用户通过自然语言与网页进行对话,直接“精准”操作网页上的组件。

2.实验简介

首先,我们提供了一个用  Vue3  搭建的商品列表管理的单页面应用,它使用 TinyVue 组件库中的  Select、Grid  等组件。该商品管理应用默认会显示商品管理的表格,并且已经实现了条件查询商品,添加、删除商品和保存商品等功能。但此应用目前不支持 AI 大模型的操作,比如打开智能助手,发送的信息不会正确执行。

对接 TinyVue Next 智能框架的相关包,给 Select、Grid  等组件添加业务描述后,让智能助手等功能可以遥控操作网页。

3.基本要求

  • 熟练使用 VSCode 或其他代码编辑器
  • 具有 Chrome、Edge 等浏览器的调试能力
  • 熟悉 Vue 开发,以及具有 TinyVue 或其它组件库的开发经验

4.代码实战

4.1 启动电商平台 demo 页面,熟悉功能

解压桌面上的工程压缩文件 agent-app-demo (可能位置与名称有调整,有疑问可咨询)

解压完成后用 VSCode 软件打开指定的工程目录,打开终端,执行以下命令安装依赖:

npm install

安装完成后执行以下命令,启动电商平台 demo 页面。

npm run dev

启动成功后,查看商品列表并简单的进行增删改查的操作。

但此时测试 AI 智能助手,则是无响应的。下一步就来对应用进行简单改造。

4.2 引用 vue-next 等智能包进行应用改造

将上面的普通应用,秒变为智能应用,需要以下 4 步, 需要按顺序完成。

4.2.1 引入智能包

在终端中执行以下命令,安装相关智能包。

npm install @opentiny/tiny-vue-mcp @opentiny/next-vue -S

4.2.2 全局注册 TinyVue MCP 的工具

main.ts 文件中,引入@opentiny/tiny-vue-mcp,注册 TinyVue MCP 的工具。

import { createMcpTools, getTinyVueMcpConfig } from "@opentiny/tiny-vue-mcp";

import { registerMcpConfig } from "@opentiny/vue-common";

// 1、 将 TinyVue MCP的工具注入到 vue-common 层中,之后应用可以TinyVue MCP的功能

registerMcpConfig(getTinyVueMcpConfig(), createMcpTools);

4.2.3  创建 MCP Client 的远端会话

App.vue文件中,引入@opentiny/next-vue, 创建 MCP Client 的远端会话。

注意:代码中连接需要自行提供
目前 agent 代理服务器代码未开源,如想进一步了解可以在 github 中提 issue 单
提交 issue 连接:github.com/opentiny/ti…

import { useNextClient } from"@opentiny/next-vue";
import { globalConversation } from"./composable/utils";

// 2、创建 sessionId , 一个 ref<string> 的值, 并保存起来,智能助手会访问该变量进行通讯

const { sessionId } = useNextClient({
clientInfo: { name"my-project"version"1.0.0" },
// url为工具调用方,token为对应的凭证
proxyOptions: { url"https://xxx/sse"token"" },
});

globalConversation.sessionId = sessionId;

4.2.4 在应用页面,创建 MCP Server, 并给组件声明“业务属性”

views/productManager/index.vue中,创建 MCP Server

import { useNextServer } from "@opentiny/next-vue";
// 3、创建web应用所需的 McpServer 变量, 用于注册页面中的组件到系统中。
const { server } = useNextServer({
  serverInfo: { name"company-list"version"1.0.0" },
});
// 4、 模板中,在AI智能助手要操作的组件上,去绑定 server 和 组件的 “业务描述”。  这样 AI就能关联mcp,并识别每个组件的业务定义了。

views/productManager/index.vue文件中, 可以改造 TinyVue 的组件,它们支持属性 tiny_mcp_config, 传入 server 和组件的业务描述,例如:

<tiny-base-select
  v-model="statusFilter"
  :options="statusOptions"
  placeholder="商品状态"
  clearable
  :tiny_mcp_config="{
    server,
    business: {
      id: 'category-status',
      description: '商品状态的下拉框',
    },
  }"
>
</tiny-base-select>

其中 business.id 是组件在页面上的唯一 id。 business.description 是描述组件的业务含义, 这样与大模型对话时,大模型可以“精准”的知道页面上有什么可以操纵。

接下来,给商品状态的 Select, 商品列表的表格,按钮等等,都可以添加上 :tiny_mcp_config 属性了。

给分类下拉框添加 tiny_mcp_config

<tiny-base-select
  v-model="categoryFilter"
  :options="categoryOptions"
  placeholder="商品分类"
  clearable
  :tiny_mcp_config="{
    server,
    business: {
      id: 'category-select',
      description: '商品分类的下拉框',
    },
  }"
>
</tiny-base-select>

给商品列表表格添加

<tiny-grid
  auto-resize
  ref="gridRef"
  :data="displayProducts"
  :height="520"
  :edit-config="{ trigger: 'click', mode: 'cell', showStatus: true }"
  :tiny_mcp_config="{
    server,
    business: {
      id: 'category-list',
      description: '商品列表',
    },
  }"
>
  <!--省略部分代码-->
</tiny-grid>

4.3 通过页面的 AI 智能助手进行网页的智能操作

经过 4.2 节, 我们已经将普通的应用秒变为智能应用。此时就可以通过“AI 智能体”工具跟网页对话了

对下拉框,你可以尝试提问:   在商品分类下拉框中,弹出下拉面板选中手机分类。

对于表格, 你可以问:帮我在商品列表中删除最贵的且分类为手机的商品。

这里就可以发挥想像力,对网页进行各种提问,看看大模型能否正确判断行为,并调用正确的 MCP 方法。

如果“AI 智能体”工具不能执行成功,可以打开浏览器控制台,参考以下内容进行排查:

控制台有无报错

打开“网络” 面板,查看 chat-messages 接口是否异常

图片

查看控制台打印的: SSE data: xxxx  信息。可以跟踪大模型的执行过程,查看调用 MCP 工具的中间过程等。

图片

4.4 页面添加可识别二维码,通过手机远程操作

通过文字去操作页面有点麻烦,如果能通过语音就是远程遥控网页是不是就好了。

在“App.vue”的顶部增加 的二维码组件:

<div class="qr-code">
  <span>
    页面识别二维码, 请用手机扫码后,在浏览器中打开。
    微信的浏览器可能没有语音功能,需要使用手机内置的浏览器访问。
  </span>
  <tiny-qr-code :value="sessionUrl" :size="100" color="#1677ff"></tiny-qr-code>
   
</div>

二维码的 value= sessionUrl ,它是通过 sessionId 计算出来的一个 url。在App.vue添加以下 js 代码:

注意:代码中连接需要自行实现并部署远程操控服务
如对此有疑问或者有兴趣交流,欢迎提交 issue: github.com/opentiny/ti…

import CryptoJSfrom"crypto-js";
import { computed } from"vue";

const sessionUrl = computed(() => {
// 未创建session时

if (!sessionId.valuereturn"no session"// 创建好 sessionId后,加密后拼接成 sessionUrl ,  这样手机扫码后会连接这个Url页面

const encryptedId = CryptoJS.AES.encrypt(
    sessionId.value,
    "secret-session-id"
  ).toString();

return"https://xxx?id=" + encodeURIComponent(encryptedId);
});

这样,我们的应用顶部会有一个二维码,用手机扫描后,使用手机上的浏览器打开,就可以选择“语音控制器”,跟它对话了,通过手机远程指挥网页。

因为上述流程中 proxyOptions.url 与 语音远程操控页面需要自行部署服务,因此提供了临时的线上体验连接:opentiny.design/tiny-vue/zh…

5.实验总结

通过本次实验,我们不仅掌握了将普通 Web 应用转化为智能 Web 应用的关键技能,还学会了利用智能助手或远程手机对网页进行精准操控。

该实验引导用户深入思考网页未来可能呈现的形态与无限可能,让每一位用户都能深切体验到人工智能为 Web 开发领域所带来的震撼,感受技术进步如何重塑我们的网页世界。

关于 OpenTiny

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网:opentiny.design
OpenTiny 代码仓库:github.com/opentiny
TinyVue 源码:github.com/opentiny/ti…
TinyEngine 源码: github.com/opentiny/ti…
欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI、TinyEditor~
如果你也想要共建,可以进入代码仓库,找到 good first issue 标签,一起参与开源贡献~

AI助力快速引入外部组件到TinyEngine低代码引擎

2025年7月2日 16:36

本文由羽毛笔记作者观默原创。

背景:低代码时代的开发挑战

TinyEngine作为一款优秀的低代码平台,以其强大的功能和快速迭代能力赢得了众多开发者的青睐。它让开发者能够通过可视化界面快速构建应用,大大提升了开发效率。

然而,就像一座美丽的花园需要更多花卉品种来装点一样,TinyEngine也面临着组件生态的挑战:官方提供的组件虽然精心设计,但数量有限,难以满足企业级项目的多样化需求。更具挑战性的是,要将私有组件库接入TinyEngine,必须先为每个组件生成对应的schema文件——这就像为每朵花制作专属的标签卡片。

从痛点到机遇:AI时代的解决方案

最近,我们公司面临着将私有组件库接入TinyEngine的需求。如果采用传统的人工方式为每个组件编写schema文件,不仅耗时费力,更重要的是缺乏那种让人兴奋的技术创新感。想象一下,如果有几十个组件需要处理,那将是一个令人望而却步的重复性工作。

这让我开始思考:在AI技术日新月异的今天,是否有更优雅的解决方案?

传统方案 VS AI方案

在AI时代之前,我可能会选择**Babel + AST(抽象语法树)**的技术路线:

  • 解析Vue组件源码
  • 遍历AST节点
  • 提取props、events、slots信息
  • 手工编写转换逻辑

这种方法虽然可行,但就像用显微镜逐个检查细胞一样繁琐。而现在,我们有了更强大的工具——AI大语言模型

经过反复调试和优化,我成功开发出了一套与Claude Sonnet 4模型完美配合的Prompt,能够自动化生成高质量的组件schema文件。这就像拥有了一位经验丰富的架构师助手,能够理解组件的每一个细节并生成标准化的配置文件。

schema 生成 Prompt:

注意:group/category/npm 这三个字段请根据实际情况调整,不要照抄!

# 任务:Vue 组件 Schema 生成器

你是一位精通 Vue 3 Composition API、TypeScript 及低代码平台组件集成的资深架构师。你的任务是接收一个 Vue 组件的源代码及相关项目文件,然后生成一个完全符合指定规则、高度精确且信息丰富的 JSON Schema 文件,用于驱动低代码平台。

你的输出必须是一个**完整的、格式正确的 JSON 对象**,不包含任何额外的解释性文字。

---

### 前置检查:验证输入信息

在开始生成 Schema 之前,你必须首先验证是否已收到所有必需的信息。

**以下项目是必需的:**

1.  **`组件源代码`**: 目标 `.vue` 文件的完整内容。
2.  **`package.json`**: 项目的 `package.json` 文件完整内容。

**可选的组件元数据***   `中文名称 (zh_CN)`: 如果未提供,从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   `图标 (icon)`: 如果未提供,从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   `描述 (description)`
*   `标签 (tags)`
*   `关键词 (keywords)`
*   `文档链接 (doc_url)`

**如果必需信息缺失,请不要继续。** 你的回应应该是一个清晰的请求,明确指出用户需要提供哪些缺失的具体内容。

只有在确认所有必需输入都已提供后,才能继续执行下面的生成步骤。

---

### 第一步:分析输入 (假设已通过前置检查)

你将收到以下输入:

1.  **`组件源代码`**: 一个完整的 Vue 组件 (`.vue`) 的文本内容。
2.  **`package.json`**: 项目的 `package.json` 文件内容。
3.  **`组件元数据` (可选)**:
    *   `中文名称 (zh_CN)`
    *   `图标 (icon)`
    *   `描述 (description)`
    *   `标签 (tags)`
    *   `关键词 (keywords)`
    *   `文档链接 (doc_url)`

---

### 第二步:执行生成规则

请严格按照以下规则,一步步构建最终的 JSON 对象:

#### 1. **顶层字段填充**

*   `component`: 从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   **`name.zh_CN`**: 
    - 如果 `组件元数据` 中提供了 `中文名称`,则使用该值
    - 否则根据 `component` 名称进行智能推断和翻译:
      - `Button` -> `"按钮"`
      - `LoginInfo` -> `"登录信息"`
      - `SixAxisRobot` -> `"六轴机械臂"`
      - `Chamber` -> `"腔体"`
      - `Aligner` -> `"对中器"`
      - `MainMenuButton` -> `"主菜单按钮"`
      - `CdsState` -> `"CDS状态"`
      - 其他名称按语义进行合理的中文翻译
*   **`icon`**: 
    - 如果 `组件元数据` 中提供了 `图标`,则使用该值
    - 否则默认使用 `component` 名称(如 `"SixAxisRobot"` -> `"SixAxisRobot"`)
*   **`group`**: 固定为 `"DCP"`
*   **`category`**: 固定为 `"DCP"`
*   `description`: 如果 `组件元数据` 中提供了 `description`,则使用该值;否则默认为 `""`。
*   `tags`: 如果 `组件元数据` 中提供了 `tags`,则使用该值;否则默认为 `""`。
*   `keywords`: 如果 `组件元数据` 中提供了 `keywords`,则使用该值;否则默认为 `""`。
*   `doc_url`: 如果 `组件元数据` 中提供了 `doc_url`,则使用该值;否则默认为 `""`。
*   `devMode`: 固定为 `"proCode"`。

#### 2. **`npm` 对象构建**

根据 `package.json` 的内容,动态构建 `npm` 对象:

*   `package`: 从 `package.json` 中读取 `name` 字段。
*   `exportName`: **必须**与顶层 `component` 字段的值保持一致。
*   `version`: 从 `package.json` 中读取 `version` 字段。
*   `script`: 基于 `package.json` 的信息,拼接成固定格式:`"http://192.168.0.212:4874/{package}@{version}/js/web-component.mjs"`。
*   `destructuring`: 固定为 `true`。
*   `npmrc`:
    1.  从 `package.json` 的 `name` 字段提取 scope (例如 `@dcp/component-library` -> `@dcp`)。
    2.  从 `package.json` 的 `publishConfig.registry` 字段提取 registry URL (并移除末尾的 `/`)。
    3.  拼接成 `"{scope}:registry={registry_url}"` 的格式。

#### 3. **`configure` 对象构建**

生成完整的 `configure` 对象,包含以下所有字段:

**基础行为控制***   `loop`: 固定为 `true`(支持循环渲染)
*   `condition`: 固定为 `true`(支持条件渲染)
*   `styles`: 固定为 `true`(支持样式配置)

**组件类型标识***   `isContainer`: 根据组件分析决定:
    - 如果组件模板中包含 `<slot>` 标签,设置为 `true`
    - 如果组件名称暗示容器用途(如 Layout、Container、Wrapper),设置为 `true`
    - 否则设置为 `false`
*   `isModal`: 固定为 `false`(除非组件明确是模态框)
*   `isPopper`: 固定为 `false`(除非组件明确是弹出框)
*   `isNullNode`: 固定为 `false`
*   `isLayout`: 根据组件用途判断,Layout 类组件设置为 `true`,否则为 `false`

**嵌套规则***   `nestingRule`: 对象包含以下字段,通常设置为默认值:
    - `childWhitelist`: `""`(允许的子组件白名单,通常为空)
    - `parentWhitelist`: `""`(允许的父组件白名单,通常为空)
    - `descendantBlacklist`: `""`(禁止的后代组件黑名单,通常为空)
    - `ancestorWhitelist`: `""`(允许的祖先组件白名单,通常为空)

**编辑器配置***   `rootSelector`: 固定为 `""`
*   `shortcuts.properties`: 识别出组件最核心、最常用的 1-3 个 props,填入此数组
*   `contextMenu`: 对象包含:
    - `actions`: 默认为 `["copy", "remove", "insert", "updateAttr", "bindEevent"]`
    - `disable`: 默认为 `[]`

**交互行为** (可选字段,根据组件类型添加):
*   `clickCapture`: 对于按钮类、交互类组件设置为 `true`,其他组件可省略或设置为 `false`
*   `framework`: 如果是第三方组件库保持原值,自定义组件设置为 `"Vue"`

#### 4. **`schema.properties` (Props 分组映射)**

将 Vue 组件的所有 props 按逻辑功能分组,生成一个**分组数组****分组策略***   **基础属性**: 核心功能相关的 props(如 name、size、type 等)
*   **样式属性**: 外观、颜色、尺寸相关的 props(如 width、height、backgroundColor、color 等)
*   **行为属性**: 交互、事件、状态相关的 props(如 disabled、loading、onClick 等)
*   **高级属性**: 可选的、专业配置项(如复杂对象配置、高级选项等)

**每个分组对象必须包含***   `name`: 分组标识符,使用数字字符串(如 `"0"`, `"1"`, `"2"`*   `label.zh_CN`: 分组的中文显示名称(如 `"基础属性"`, `"样式属性"`*   `description.zh_CN`: 分组的中文描述
*   `content`: 该分组下的具体属性配置数组

**`content` 数组中的每个属性对象必须包含以下固定字段***   `property`: Prop 的名称
*   `label.text.zh_CN`: 中文标签
*   `description`: 中文描述
*   `required`: 根据 Vue Prop 中的 `required` 字段决定,默认为 `false`
*   `readOnly`: 固定为 `false`
*   `disabled`: **固定为 `false`**
*   `cols`: **固定为 `12`**
*   `labelPosition`: 固定为 `"left"`
*   `type`: Vue 类型转换为小写字符串
*   `defaultValue`: Vue Prop 的默认值
*   `widget`: 根据以下规则推断

**Widget 推断规则 (按优先级顺序)**1. **validator 函数解析 (最高优先级)**:
   - 如果 Prop 定义中存在 `validator` 函数,解析函数体中的选项数组
   - 设置 `widget.component``"SelectConfigurator"`
   - 设置 `widget.props.options` 为解析出的选项数组

2. **属性名称模式匹配**:
   - 名称包含 `color` 或默认值以 `#` 开头 -> `"ColorConfigurator"`,props: `{}`
   - 名称包含 `icon` -> `"InputConfigurator"`,props: `{ "placeholder": "请输入图标名称" }`

3. **Vue 类型 + 语义推断**:
   - `Boolean` 类型:
     - 开关语义 (show*, enable*, is*) -> `"SwitchConfigurator"`,props: `{}`
     - 选项语义 (disabled, loading, plain, round, circle) -> `"CheckBoxConfigurator"`,props: `{}`
   - `Number` 类型 -> `"NumberConfigurator"`,根据属性名称设置 props:
     - 尺寸类 (width, height, size): `{ "min": 50, "max": 2000, "step": 10 }`
     - 角度类 (rotate, angle): `{ "min": 0, "max": 360, "step": 1 }`
     - 比例类 (scale): `{ "min": 0.1, "max": 5, "step": 0.1 }`
     - 时间类 (duration, delay): `{ "min": 0, "max": 50, "step": 0.1 }`
     - 默认: `{ "step": 1 }`
   - `String` 类型 -> `"InputConfigurator"`,props: `{ "placeholder": "请输入..." }`
   - `Object`/`Array` 类型 -> `"CodeConfigurator"`,props: `{ "language": "json", "height": 150 }`

4. **智能类型分析**:
   - 如果 Prop 类型为 `Array as PropType<SomeInterface[]>`,在 `description` 中补充接口结构信息

#### 5. **`schema.events` (事件映射)**

*   在组件 `<script>` 中搜索所有 `emit('event-name', ...)` 的调用。
*   每一个 `event-name` (kebab-case) 都对应 `events` 对象中的一个键。
*   该键的命名规则为 **`'on' +` 将 `event-name` 转换为首字母大写的驼峰式 (CamelCase)**。例如,`emit('menu-item-click')` 映射为 `onMenuItemClick`*   分析 `emit` 的参数,为该事件生成 `functionInfo.params` 数组。

#### 6. **`schema.slots` (插槽分析)**

*   扫描组件的 `<template>` 部分,寻找所有 `<slot>` 标签。
*   对于每一个**具名插槽** (例如 `<slot name="menu-items">`),在 `schema.slots` 对象中为其添加一个条目。
*   该条目的键为插槽名 (`menu-items`),值为一个包含 `label.zh_CN``description.zh_CN` 的对象,用于描述该插槽的用途。

#### 7. **`snippets` (智能代码片段生成)**

*   生成一个只包含**单个默认 Snippet** 的数组 `[]`*   `name.zh_CN`, `icon`, `snippetName` 与顶层字段保持一致。
*   `schema.props`:
    *   **优先策略**: 在工作区中查找与组件同名的 `.stories.ts` 文件。如果找到,请使用其 `args` 对象作为 `props` 的数据来源。
    *   **备用策略**: 如果找不到 Storybook 文件,请不要简单地使用 `defaultValue`。应根据每个 Prop 的语境,创建一组有意义、更具代表性的示例值(例如,`username` 使用 `'Admin'` 而不是 `'User'`)。

---

### 第三步:输出最终 JSON

请整合以上所有分析结果,生成最终的 JSON 文件。

实战指南:从理论到实践

第一步:智能生成Schema

运用AI生成schema的过程就像与一位专业顾问的对话。让我们以Cursor编辑器为例,一步步地体验这个过程:

操作步骤:

  1. 打开组件项目:启动Cursor,进入你的Vue组件库项目
  2. 准备上下文材料:在聊天界面中添加以下关键文件:
    • 项目根目录的package.json
    • 目标组件的.vue源码文件
  3. 粘贴Prompt:将上文提供的完整Prompt复制到输入框
  4. 发起对话:按下回车键,等待AI的魔法发生

为什么选择Claude Sonnet 4?

经过测试对比,我发现Claude Sonnet 4在理解Vue组件结构和生成精准schema方面表现出色,像deepseek、qwen这些国内模型暂时无法:

  • 精准的类型推断:能够正确识别Props的TypeScript类型并转换为对应的widget配置
  • 智能的语义理解:根据属性名称自动推断合适的编辑器组件(颜色选择器、数字输入框等)
  • 结构化输出:生成的JSON格式规范、完整,直接可用

实际效果展示

设想我们有一个名为CustomButton的Vue组件:

<template>
  <button 
    :class="buttonClass" 
    :disabled="disabled"
    @click="handleClick"
  >
    <slot>{{ label }}</slot>
  </button>
</template>

<script setup lang="ts">
import { computed } from 'vue'

interface Props {
  label?: string
  type?: 'primary' | 'secondary' | 'danger'
  size?: 'small' | 'medium' | 'large'
  disabled?: boolean
  loading?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  label: '按钮',
  type: 'primary',
  size: 'medium',
  disabled: false,
  loading: false
})

const emit = defineEmits<{
  click: [event: MouseEvent]
}>()

const handleClick = (event: MouseEvent) => {
  if (!props.disabled && !props.loading) {
    emit('click', event)
  }
}
</script>

通过AI处理后,会生成一个包含以下关键信息的schema:

  • Props分组:label、type、size被分类为“基础属性”,disabled、loading分类为“行为属性”
  • 组件类型:type属性自动配置为下拉选择器,并包含正确的选项
  • 事件映射:click事件被正确转换为onClick处理函数

第二步:添加物料

把上一步得到的json文件保存到TinyEngine项目根目录下的materials/components 文件夹下。

第三步:添加仓库

如果你没有添加过仓库配置,那么你需要在 项目根目录下的materials/packages.json 文件中添加你的仓库信息:

{
      "name": "DCP组件库",
      "package": "@dcp/component-library",
      "version": "0.0.60",
      "script": "http://192.168.0.212:4874/@dcp/component-library@0.0.60/js/web-component.mjs",
      "destructuring": true,
      "npmrc": "@dcp:registry=http://192.168.0.212:4873"
    }

第四步:构建物料

现在你可以直接在终端执行pnpm buildMaterials ,等终端不再有新的输出时,可以ctrl + c 退出脚本。

一切就绪,启动项目验收

至此,你可以pnpm serve:frontend 后访问项目来使用新增的组件了。

问题排查指南

在实际开发过程中,即使按照上述步骤操作,也可能遇到组件未正常显示的情况,这里为您提供了系统化的排查方法。

关键检查点一:组件获取机制

首先检查TinyEngine的组件获取机制是否正常工作。在以下位置添加调试信息:

文件位置: packages/canvas/render/src/material-function/material-getter.ts#L109

export const getComponent = (name) => {
  // 添加调试信息
  console.log(`正在获取组件: ${name}${getNative(name)}`)
  
  const result = Mapper[name] || getNative(name) || getBlock(name) || (isHTMLTag(name) ? name : getBlockComponent(name))
  
  console.log(`获取结果:`, result)
  return result
}

ℹ️ 排查要点:对于引入的组件,getNative(name)应该返回非空值。如果返回undefined,说明组件没有被正确注册。

关键检查点二:动态导入机制

如果上一步检查发现问题,接下来排查动态导入机制。在以下位置添加调试代码:

文件位置: packages/canvas/common/src/utils.ts#L100

const dynamicImportComponentLib = async ({ pkg, script }: DynamicImportParams): Promise<any> => {
  console.log(`开始导入组件库: ${pkg}`)
  
  if (window.TinyComponentLibs[pkg]) {
    console.log(`组件库已存在缓存中: ${pkg}`)
    return window.TinyComponentLibs[pkg]
  }

  if (!script) {
    console.warn(`组件库 ${pkg} 缺少 script 配置`)
    return {}
  }

  const href = window.parent.location.href || location.href
  const scriptUrl = script.startsWith('.') ? new URL(script, href).href : script
  
  console.log(`动态导入组件库脚本: ${scriptUrl}`)

  if (!window.TinyComponentLibs[pkg]) {
    try {
      const modules = await import(/* @vite-ignore */ scriptUrl)
      console.log(`组件库导入成功:`, modules)
      window.TinyComponentLibs[pkg] = modules
    } catch (error) {
      console.error(`组件库导入失败: ${pkg}`, error)
      return {}
    }
  }

  return window.TinyComponentLibs[pkg]
}

常见问题及解决方案

📌 最常见问题:组件未正确导出

在我的实际经验中,这个问题出现频率最高。很多时候我们在组件库中开发了新组件,但忘记在入口文件中导出它。

解决方案

  1. 检查组件库的index.tsindex.js文件
  2. 确认目标组件已经被正确导出:
    export { default as YourComponent } from './YourComponent.vue'
    

📌 脚本路径错误

检查packages.json中的script字段是否正确。常见错误包括:

  • 版本号不匹配
  • 域名或端口错误
  • 文件路径错误

通过以上系统化的排查步骤,绝大多数问题都能够得到快速解决。


如果您对AI开发感兴趣,欢迎关注我的公众号:观默视界

在这里,我会分享更多关于AI技术在实际开发中的实战经验和最新趋势。

关于OpenTiny

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网opentiny.design
OpenTiny 代码仓库github.com/opentiny
TinyVue 源码github.com/opentiny/ti…
TinyEngine 源码: github.com/opentiny/ti…
欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI、TinyEditor~
如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~

不允许还有人不会给开源项目提交代码

作者 comelong
2025年7月2日 16:04

前言

近期为了面试也是一直在看Vue3的部分源码实现思路,再结合AI可以很好的了解到一些源码的具体实现过程,这样在应对面试过程中也能做到举一反三,话不多说,直接上干货!!!

1. Fork 项目到自己的 GitHub 账号

在 GitHub 上点击 Fork 按钮

2. Clone 自己的 fork 到本地

git clone github.com/YOUR_USERNA…

3. 设置远程仓库

git remote add upstream github.com/original/re… # 添加上游仓库

git remote set-url origin github.com/YOUR_USERNA… # 设置你的 fork

创建新分支 git checkout -b fix-name # 分支名应该反映改动内容

2. 安装依赖

pnpm install # Vue 使用 pnpm

3. 进行代码修改

修改相关文件

4. 运行测试(非必须)

pnpm test

提交信息格式:

type(scope): description

类型(type):

  • fix: 修复bug
  • feat: 新功能
  • docs: 文档改动
  • style: 代码格式改动
  • refactor: 重构
  • test: 测试相关
  • chore: 构建过程或辅助工具的变动

示例:

   git add .
   git commit -m "fix(compiler-sfc): correct method name in templateUtils"
   这里意思就是提交了一个修改的正确方法名在templateUtils

1. 推送到你的 fork

git push origin fix-name

2. 在 GitHub 上创建 PR
- 使用清晰的标题(与 commit 消息一致)
- 写详细的描述
- 确保选择正确的基础分支

标题

fix(compiler-sfc): correct method name in templateUtils

描述

Problem

  • Describe the issue you're fixing

Solution

  • Explain your changes
  • What was changed and why

Additional Info

  • Any side effects?
  • Testing done?
  • Screenshots (if UI changes)?

注意事项

  • 确保一个 PR 只做一件事
  • 遵循项目的代码风格
  • 保持 commit 历史清晰
  • 及时响应 review 意见
  • 保持耐心,维护者可能比较忙

更新 fork 与上游同步

  • git fetch upstream
  • git checkout main
  • git merge upstream/main

压缩/整理 commit

git rebase -i HEAD~n # n是要合并的commit数量

如果需要修改 PR

  • git add .
  • git commit --amend # 修改最后一次提交
  • git push origin fix-name -f # 强制推送(谨慎使用)
昨天以前首页

记录App切后台时AppIcon变成默认雪花icon问题

2025年6月28日 12:46

xcode做新项目时,设置了app图标。发现点击app进入前台时,App Icon是正常的,但是回到桌面时App Icon又变成了默认的雪花图标。

之前也遇到过,但是不求甚解,在此列出解决方案。

问题1: AppIcon的设置

随便设置了个图片为app图标,编译报错xxx/Assets.xcassets: The stickers icon set or app icon set named "AppIcon" did not have any applicable content. 同时appIcon可视化窗口显示黄色⚠️图标。

Xcode 提示你在 Assets.xcassets 中名为 "AppIcon" 的 App 图标集合里没有提供任何有效的图片资源。

iOS 应用要求必须有完整的 AppIcon 集合,并且要包含适用于各种设备和分辨率的图标尺寸。如果没有正确设置这些图标,App 就无法通过 App Store 审核,甚至可能在某些模拟器或真机上运行异常。

我使用了makeappicon.com/ 生成appIcon图标。 网站生成的结果包含AppIcon.appiconset,直接把AppIcon.appiconset替换原项目中Assets中的appIcon即可。 image.png 结果如下 image.png

问题2: 切后台appIcon变成默认雪花icon

现在成功设置appIcon后,切后台时发现appIcon变成了默认的雪花icon。

原因是系统缓存了旧图标,iOS 系统有时会缓存应用的图标缩略图,尤其是多任务界面中的预览图。即使你更新了图标,也可能不会立即刷新。

解决办法: 卸载重装

现在能正常显示了

image.png

zsh | 程序员常用的 git 分支显示

2025年6月27日 20:13

本文主要用于

  1. 记录 终端指令
  2. 记录 zsh 无法设置为系统默认登录 shell => 单独修改每个应用的 默认登录 shell 为 zsh、
  3. 记录 zsh 的功能: 显示 git 分支,自动补全 ,及替代方案

切换 bash zsh

# 打开文件 修改 bash
open -e ~/.bash_profile 
# open 图形界面打开
# ✅ `-e`:是一个选项(option),表示使用默认的文本编辑器打开文件

vim ~/.bash_profile 
# ✅ vim 命令行打开


# 设置默认 shell 为 bash
chsh -s /bin/bash $USER

# 检查是否生效 (如果输出 /bin/bash,说明已经切换成功。
echo $SHELL
# 如果输出 /bin/bash,说明已经切换成功。
#  ✅ `echo`:输出命令,用于将其后面的参数显示在屏幕上
# `$SHELL`:是一个环境变量,存储了当前系统使用的 shell 程序的完整路径

无法修改登录 shell 为 zsh

# ❌ 命令行修改无效
# 切换默认 shell 为 zsh
chsh -s /bin/zsh $USER
# 终端 报错
chsh: Operation was denied because the current credentials do not have the appropriate privileges.
chsh: no changes made

# ❌ 设置无效
# 用“系统设置”图形界面修改(最保险):
# 打开“系统设置” → “用户与群组”
# 点击左下角的锁,输入密码解锁
# 右键你的用户名,选择“高级选项…”
# 找到“登录 shell”,改为 /bin/zsh
# 保存并重启终端
# 弹窗 报错
未能完成操作。(com.apple.extensionKit.errorDomain错误15。)

# 你的 Mac 可能被公司 MDM(移动设备管理)或配置文件限制,禁止更改登录 shell。
# 普通用户(即使是管理员)在某些公司策略下也无法修改 shell。
# 这是 macOS Ventura/Sonoma 等新系统下常见的权限限制报错。

# ✅ 临时使用 zsh
zsh
# 这样当前终端会话会进入 zsh,但关闭窗口后会恢复为 bash。

✅ 手动修改 编辑器 VSCode Cursor , 默认登录 shell 为 zsh

image.png

✅ 手动修改 终端 , 默认登录 shell 为 zsh image.png

✅ 手动修改 iTerm2 , 默认登录 shell 为 zsh image.png

终端显示 git 分支

程序员常用的 git 分支显示方式有以下几种,既美观又简约

1 自定义 Bash PS1(简约风)

这是最基础、最轻量的方式,适合喜欢极简风格的用户。 比如只显示分支名,分支前加个符号,分支名有颜色:

# 在 ~/.bash_profile 或 ~/.bashrc 末尾加:
# 显示 git 分支 1
parse_git_branch() {
  git branch 2>/dev/null | grep '\*' | sed 's/* //'
}
export PS1="\u@\h \W\[\033[32m\]\$(parse_git_branch)\[\033[00m\] $ "
#  ✅ parse_git_branch 是一个 shell 函数,用于获取当前 git 分支名。
#  ✅ PS1 是 bash 的提示符变量,\u 是用户名,\h 是主机名,
#     \W 是当前目录,\$(parse_git_branch) 是当前 git 分支。

git1.jpg

# 在 ~/.bash_profile 或 ~/.bashrc 末尾加:
# 显示 git 分支 2
parse_git_branch() {
  git rev-parse --abbrev-ref HEAD 2>/dev/null | sed 's/\(.*\)/ (\1)/'
}
export PS1="\[\e[36m\]\u@\h \W\[\e[33m\]\$(parse_git_branch)\[\e[0m\] $ "

git2.jpg

2 使用 bash-git-prompt (推荐,丰富美观)

bash-git-prompt 是一个开源项目,支持分支、状态、颜色、符号等,配置简单,效果美观。

# 显示 git 分支 3

# 下载 + 安装(保存到 ~/.bash-git-prompt 目录)
git clone https://github.com/magicmonty/bash-git-prompt.git ~/.bash-git-prompt

# 打开文件
open -e ~/.bash_profile
# vim ~/.bash_profile # (命令行打开)

# 在 ~/.bash_profile 或 ~/.bashrc 末尾加:
GIT_PROMPT_ONLY_IN_REPO=1
if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then
  source $HOME/.bash-git-prompt/gitprompt.sh
fi

# 让配置生效
source ~/.bash_profile

git3.jpg

# 显示 git 分支 4

# 复制默认主题 Default.bgptheme ,新建自定义主题 MySimple.bgptheme
cp ~/.bash-git-prompt/themes/Default.bgptheme ~/.bash-git-prompt/themes/MySimple.bgptheme

# 用编辑器打开 MySimple.bgptheme,在文件开头或合适位置添加:
GIT_PROMPT_START_USER="_LAST_COMMAND_INDICATOR_ ${Yellow}\W${ResetColor}"
GIT_PROMPT_END_USER=""
# _LAST_COMMAND_INDICATOR_ 就是你看到的 ✔
# \W 是当前文件夹名

# 在 ~/.bash_profile 或 ~/.bashrc 末尾加:
export GIT_PROMPT_ONLY_IN_REPO=1 
# GIT_PROMPT_ONLY_IN_REPO=1 只有在 git 仓库目录下才显示 bash-git-prompt 的 git 分支和状态提示,其他普通目录下不会显示 git 相关内容。
# export 主要用于将变量设置为“环境变量”,让它在当前 shell 及其子进程(比如你运行的脚本、程序)中都能访问。
export GIT_PROMPT_THEME=MySimple
# 使用 自定义主题
if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then
  source $HOME/.bash-git-prompt/gitprompt.sh
fi


# 让配置生效
source ~/.bash_profile

git4.jpg

3 使用 oh-my-zsh(极致美观,功能强大)

如果你愿意换成 zsh(macOS 新版默认),可以用 oh-my-zsh,内置 git 插件和主题,分支显示极其美观。

# 安装 oh-my-zsh:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

# 打开配置
open -e ~/.zshrc

# 选择主题(如 agnoster、powerlevel10k):
# 编辑 ~/.zshrc,设置:
export ZSH_THEME="agnoster"
# 或
export ZSH_THEME="powerlevel10k/powerlevel10k"
# 1. 查看 zsh 是否已安装
# macOS 10.15 及以上系统自带 zsh,一般都已安装。你可以用下面命令确认:
zsh --version
# zsh 5.9 (x86_64-apple-darwin22.0)

# 2. 切换默认 shell 为 zsh
chsh -s /bin/zsh $USER

# 检查 shell 是否切换成功
echo $SHELL
# 如果输出 /bin/zsh,说明已经切换成功。

输入 git 时 tab 自动提示

问题: 如何在终端中输入 git checkout ma 时,点击 tab 能自动 git checkout master

解决: 临时使用 zsh 。oh-my-zsh 默认自带 git 补全功能,只要 plugins=(git),就可以自动补全分支名。

# 1. 安装 git-completion 脚本
# macOS 自带 bash 可能没有自动补全脚本,你可以这样安装:
curl -o ~/.git-completion.bash https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
# ✅ 命令含义
# curl:是一个命令行工具,用于通过网络协议(如HTTP、HTTPS等)传输数据
# -o ~/.git-completion.bash:指定要把下载的文件保存为~/.git-completion.bash
# https://...:目标文件的URL,是GitHub仓库中的Git项目的补全脚本文件

# 输出如下:
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 72 94587   72 68331    0     0   2362      0  0:00:40  0:00:28  0:00:12     0
# ✅ 解释如下:
# % Total72总体完成百分比(72%)
# % Received72接收数据的完成百分比
# Xferd72已传输的数据百分比
# Average Speed2362平均下载速度(字节/秒)
# Time Spent0:00:28已经花费的时间
# Time Left0:00:12预计剩余时间
# Current Speed0当前下载速度(字节/秒)
# Dload94587已下载的总字节数
# Upload0上传的字节数(本次为纯下载操作)

# 2. 在 ~/.bash_profile 或 ~/.bashrc 里添加:
   if [ -f ~/.git-completion.bash ]; then
     source ~/.git-completion.bash
   fi

# 生效
   source ~/.bash_profile

命令行的进化:CMD、PowerShell 和 Bash

2025年6月26日 10:19

引子

在图形界面横行的今天,命令行看起来就像上个世纪的老古董。但真相是——每一行命令背后,藏着整个操作系统的灵魂。

无论是 Windows 上的 CMD,Linux/macOS 上的 Bash,还是微软后来力推的 PowerShell,它们不仅是开发者的得力助手,更是技术演进的缩影。每个命令、每个符号,都是一代工程师试图解决某个痛点的答案。

本篇,就让我们沿着命令行的时间轴,看看它如何从上世纪的字符终端,一步步走到云计算和 AI 驱动的今天。

🎯 演进时间线一图流

1981 ──────── 1989 ──────── 2006 ──────── 2016 ──────── 2025  
                                                      
├─ CMD (MS-DOS)                                         
             ├─ Bash 0.99               ├─ WSL 1.0      
                          ├─ PS 1.0     ├─ PS 6.0 (跨平台)  
                                       ├─ Bash on Windows  
                          AI + CLI 融合(Copilot时代)

一、诞生的背后:它们到底在解决什么问题?

CMD:MS-DOS 留下的“历史债务”

CMD 诞生的使命其实很朴素——兼容老软件,别让几十年前的脚本白写了。

1981 年,MS-DOS 面世,command.com 成为当时的默认命令行。后来进入 Windows 时代,cmd.exe 接过了接力棒。虽然功能有限,但它承担了“兼容老脚本”“帮 Windows 用户跑批处理”的重要角色。

一句话总结:CMD 不是为了创新,而是为了不让旧时代崩溃。


PowerShell:Windows 自动化的自我救赎

到了 2000 年代,Windows 运维界一片混乱:

  • CMD,功能太弱。
  • VBScript,写的人抓狂,看的人更崩溃。
  • 图形界面点点点,根本没法自动化。

于是,微软的 Jeffrey Snover 提出了一个惊世骇俗的想法:为什么命令行不能直接操作对象?

这就是后来 PowerShell 的核心思想——对象管道。命令不再传递一堆字符串,而是传递带属性、带方法的对象。你可以直接对文件、进程、服务下手,无需靠什么 awkgrep 拼命切字符串。

比如你想查看 nginx 进程的内存占用,只能先拿到一堆文本,再自己想办法提取出有用的字段。

ps aux | grep nginx | awk '{print $4}'

这段命令做了什么?

  • ps aux → 列出所有进程(输出一大坨文本)
  • grep nginx → 从文本中过滤出包含 nginx 的行
  • awk '{print $4}' → 拿到第四列(通常是内存占用)

但这里有个致命问题:一旦输出格式变了,这个命令就废了。

比如不同的 Linux 发行版、不同版本的 ps 命令,字段位置可能变,空格数量可能变,甚至列标题变长都会导致 awk '{print $4}' 报错或者结果错位。

而 PowerShell 传递的不是文本,而是 .NET 对象。每个对象都有属性,结构化、标准化,不再依赖格式推测。

对应的 PowerShell 写法:

Get-Process nginx | Select-Object -ExpandProperty CPU

解释一下:

  • Get-Process nginx → 返回的是一个 进程对象列表,每个对象都包含标准字段,比如 CPU、Memory、ProcessName 等。

  • Select-Object -ExpandProperty CPU → 直接提取这个对象的 CPU 使用率。

  • 没有文本解析,没有位置依赖、没有空格错位的问题

  • 哪怕操作的是一台 Windows、本地 Linux,还是云端容器,对象结构是统一的

  • 可以链式继续操作:

    Get-Process nginx | Where-Object { $_.CPU -gt 80 } | Stop-Process
    

Bash:开源世界的“共同语言”

Bash 的出现其实更像一场“自由之战”。

1988 年,GNU 项目需要一个开源的 Shell,来取代当时专有的 Bourne Shell。于是,Brian Fox 写下了 Bash(Bourne-Again Shell)。

它不仅保留了 Unix 世界的命令行传统,还不断加入诸如命令补全、作业管理、数组、函数等高级功能。Bash 逐渐成了 Linux 开发者的母语,也成了开源生态的命脉之一。


二、技术进化:谁在原地踏步,谁在狂飙?

CMD:稳如老狗,也老得发霉

  • 功能?能跑就行。
  • 创新?别想了。
  • 它的存在感,更多是“我不主动退役,你也没法彻底抛弃”。

PowerShell:从 Windows 到全世界

版本 年份 大事件
1.0 2006 初代发布,终于告别 VBScript
3.0 2012 支持模块自动加载,舒服多了
5.0 2015 加入类定义,开始支持 OOP
6.0+ 2018 跨平台,跑到 Linux 和 macOS 上

当微软宣布 PowerShell 开源、支持 Linux/macOS 时,很多人都知道,这已经不仅仅是 Windows 的工具,而是云时代的自动化中枢


Bash:从服务器跑进了 Windows

Bash 的强大来自社区,30 多年来,它不断进化:

  • 支持函数、数组、正则。
  • 有 Zsh、Fish 这样的兄弟竞品,但它依然是脚本领域的事实标准
  • 更牛的是,2016 年,微软通过 WSL(Windows Subsystem for Linux)让 Bash 正式进驻 Windows。开发者终于不用在 Windows 和 Linux 之间反复横跳了。

三、核心哲学:文本 vs 对象

管道的本质区别

  • Bash/CMD:传递的是“文本” 。所以你经常看到这样:

    ps aux | grep nginx | awk '{print $4}'
    

    ——输出是字符串,后续处理全靠你自己解析。

  • PowerShell:传递的是“对象” 。直接操作属性,省事又优雅:

    Get-Process nginx | Select-Object -ExpandProperty CPU
    

脚本能力对比

CMD Bash PowerShell
易用性 简单但功能弱 易上手,符号多稍绕 上手陡,功能极强
安全性 基本没 靠文件权限 有执行策略保护
扩展性 没有 社区脚本库丰富 模块化 + 面向对象
跨平台 Windows 独有 Linux/macOS/WSL Windows + Linux + Mac

四、今天的命令行,早就不是单打独斗

  • CMD:老系统的守门人,跑跑批处理,救急。
  • Bash:开发者的万能胶,从服务器到 CI/CD,无所不在。
  • PowerShell:企业级 DevOps 自动化的主力军,尤其在 Azure 和多云管理场景下一骑绝尘。

五、未来趋势:命令行不会死,反而更猛

  • Bash:依旧坚挺,特别是在云原生、容器编排(Kubernetes)里,它就是 glue code(胶水)。

  • PowerShell:越来越像 DevOps 世界的瑞士军刀,支持混合云、本地集群,甚至 AI Copilot 正在学会帮你写 PowerShell 脚本。

  • AI + CLI:未来的命令行,可能不仅是命令,更是自然语言驱动的半自动脚本。比如——

    “帮我查一下 A 集群 CPU 用量,把高于 80% 的节点拉出来重启。”
    ——Copilot 自动转成 PowerShell 或 Bash 脚本。


结语:命令行,从未过时

  • CMD,活在兼容性里
  • Bash,活在开源生态里
  • PowerShell,活在云端自动化里

命令行不是老掉牙的工具,而是这个数字世界里最简洁、最高效、最具创造力的接口之一。

技术的演化,从不是消灭谁,而是融合谁。


🚀 关于 Dev Odyssey

「Dev Odyssey」是一张为开发者绘制的成长航海图。
从计算机底层,到前沿技术;从系统思维,到智能时代。路线图、时间轴、技能矩阵,都是你穿越迷雾的指南针。

🌐 在线体验:dev-odyssey.pages.dev
💻 项目开源:github.com/tuaran/dev-…

愿每一个热爱技术的灵魂,都能在属于自己的奥赛德之旅中,驶向星辰大海。

AI 生成的 requirements.txt 害我部署搞了一上午。。

2025年6月27日 11:55

别盲目相信 AI 给你生成的 requirements.txt 。 它看起来像是帮你总结好了所有依赖,实际上准确率堪忧,经常会出现版本对不上、根本不存在的库,或者和你的 Python 版本完全不兼容。亲身实测,这玩意充其量是一个参考,而不是可以直接用在生产环境的配置文件。这是踩过坑之后换来的血的经验。

📜 前情提要

今天原本只是一次简单的部署更新,没想到硬生生搞成了一次完整的实战修炼。

项目是一个前后端分离的智能稽核平台,前端基于 Vue3,后端是 Python Flask + OpenAI API。目标很简单:把本地开发好的代码部署到服务器上,让前端能正常调用后端 API。

听起来是不是很简单?对,听起来。

🚑 第一波崩溃:API 全线扑街,但图片能访问

部署完成,前端一打开页面,所有的 API 请求直接扑街:

500 Internal Server Error

但是,很诡异——访问后端的图片是正常的。

这一下直接说明了一个事实:

  • ✅ 静态文件服务正常(Flask 的 static/ 文件夹在工作)。
  • ❌ 动态 API 全部挂掉(/api/*、/send_email、/parse_excel、/ask_to_json 全报500)。

我瞬间明白,这绝对不是端口、防火墙或者前后端没连通的问题。因为图片是从后端接口直接返回的,说明网络是通的、Nginx(如果有的话)是通的,CORS 也是通的。

🔥 问题的本质一定在 Flask 的动态路由服务启动失败

🕳️ 第二波深坑:Python 版本地狱

原来服务器默认 Python 是 3.7.9。

我看着 requirements.txt 里的 Flask==2.3.3,瞬间明白了。

✅ Flask 2.2 开始,最低支持 Python 3.8。 —— 这个是 AI 自动生成的,它也想不到,还有低于 3.8 的环境的吧??

直接装不上,pip 一顿狂报错:

ERROR: No matching distribution found for Flask==2.3.3

💡 行吧,Python 升级走起。

🔥 第三波对抗:pip 和路径的迷宫

Python 3.10 装完了,结果 pip 还默认指向老的 Python 3.7。

查 pip 路径,试图软链接、换源、改 PATH,所有能想到的招都上了。

不是权限问题,就是 pip 和 python 对不上,装的库永远在“平行世界”。

一度开始怀疑人生: “为什么 pip install 明明显示成功,python 一运行还在报 ModuleNotFoundError?”

🧠 第四波顿悟:虚拟环境才是真理

彻底放弃全局安装,直接项目内建一个 venv:

python3.10 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

结果——openai 又报错。

原因?AI帮我生成的 requirements.txt 里写着:

openai==0.2.3

什么鬼?直接 pip 官方都搜不到。

💥 瞬间明白:AI生成的requirements.txt 纯粹是“编的”,根本没测试过版本兼容。

🔧 第五波修正:全手动修复依赖

彻底不信 requirements.txt 了,直接上最原始的办法:

1️⃣ 本地跑得好的环境,pip freeze 导出:

pip freeze > requirements.txt

2️⃣ 上传到服务器,重新安装:

pip install -r requirements.txt

openai、flask、pandas、gunicorn,一个一个确认版本。

✅ 第六波成功:见证重启

杀掉占用的5000端口:

kill -9 $(lsof -t -i :5000)

启动服务:

source venv/bin/activate
gunicorn app:app -b 0.0.0.0:5000

前端刷新页面——所有API正常返回!

✅ 三个小时的地狱,终于走到了出口。

🚀 成长的瞬间

这次部署,带给我的绝不仅仅是“跑通了”这么简单。

✅ 深刻理解了 Python 多版本管理的地狱
✅ 明白了 requirements.txt 永远只是个历史快照,别全信
✅ 再也不手动改 PATH,虚拟环境永远是最优解
✅ openai 这种库更新太快,AI生成的依赖版本根本靠不住

最重要的是,彻底习得一条原则:

“部署,永远不是点击 run 那么简单。”

🏗️ 最后,附上部署代码,作为教训

cd py
source venv/bin/activate

# 杀掉5000端口
PID=$(lsof -t -i :5000)
if [ "$PID" ]; then
  kill -9 $PID
fi

# 启动
nohup gunicorn app:app -b 0.0.0.0:5000 > server.log 2>&1 &

后端启动完成 ✅

Rspack 1.4 发布:支持在浏览器中运行

作者 WebInfra
2025年6月26日 18:02

Rspack 1.4

Rspack 1.4 已经正式发布!

值得关注的变更如下:

  • 新功能
    • 在浏览器中运行
    • 更快的 SWC
    • 更小的构建产物
    • 默认启用增量构建
    • 新增 CssChunkingPlugin
    • 增强 lazy compilation
    • 自定义文件系统
    • 性能分析工具
  • Rstack 进展
    • Rsbuild 1.4
    • Rslib 0.10
    • Rspress 2.0 beta
    • Rsdoctor MCP
    • Rstest 发布
  • 生态系统
    • next-rspack
    • Kmi
  • 升级指南

新功能

在浏览器中运行

从 Rspack 1.4 开始,我们正式引入了 Wasm target 支持,这意味着 Rspack 现在可以在浏览器环境中运行,包括 StackBlitzWebContainers)等在线平台。这使得开发者无需配置本地环境,即可快速创建原型、分享代码示例。

你可以直接体验我们提供的 在线示例,也可以在 这篇文档 中了解 StackBlitz 的使用指南。

ezgif-86ca2fa2b8caa5.gif

在后续版本中,我们将继续优化 Wasm 版本的使用流程和包体积。

同时我们也在开发 @rspack/browser 包,它是专为浏览器环境设计的版本,允许你直接在任何现代浏览器中使用 Rspack,而无需依赖 WebContainers 或是特定平台。

更快的 SWC

在过去几个月中,我们与 SWC 团队持续合作,共同优化 JavaScript 工具链的性能和可靠性。经过一段时间的优化,我们很高兴地看到,SWC 的性能取得了显著提升,使 Rspack 用户和所有基于 SWC 的工具都从中受益:

  • JavaScript parser(解析器)的性能提升了 30%~35%
  • JavaScript minifier(压缩器)的性能提升了 10%
SWC benchmark

以上数据来自:CodSpeed - SWC,对比的基准为 Rspack 1.3 所使用的 SWC 16。

更小的构建产物

在当前版本中,SWC 加强了死代码消除(DCE)能力,结合 Rspack 强大的 tree shaking 功能,使 Rspack 1.4 能够生成体积更小的构建产物。

我们以 react-router 为例进行测试:在源代码中仅引入它的一部分导出,然后对比不同打包工具的构建结果,可以看到 Rspack 生成的包体积最小。

import { BrowserRouter, Routes, Route } from 'react-router';

console.log(BrowserRouter, Routes, Route);

各个打包工具输出的包体积如下:

打包工具 压缩后体积 Gzipped 后体积
Rspack (Rsbuild) 36.35 kB 13.26 kB
webpack 36.96 kB 13.37 kB
Vite 42.67 kB 15.67 kB
Rolldown 42.74 kB 15.17 kB
Rolldown Vite 43.42 kB 15.46 kB
Farm 43.42 kB 15.63 kB
Parcel 44.62 kB 16.07 kB
esbuild 46.12 kB 16.63 kB
Bun 57.73 kB 20.8 kB

以上数据来自:react-router-tree-shaking-compare

默认启用增量构建

通过不断的优化迭代,Rspack 的增量构建功能已趋于稳定,在 Rspack 1.4 中,我们将所有阶段的增量优化设为默认开启,这能够显著加快重新构建的速度,HMR 性能通常可提升 30%-40%,具体提升幅度因项目而异。

下面是一位用户开启增量构建后的性能对比:

incremental benchmark

如果你需要降级到之前的行为,可以设置 experiments.incremental'safe' ,但我们推荐大部分项目直接使用新的默认配置,以获得最佳性能。

export default {
  experiments: {
    // 降级到之前的行为
    incremental: 'safe',
  },
};

新增 CssChunkingPlugin

Rspack 1.4 新增了实验性的 CssChunkingPlugin 插件,专门用于处理 CSS 代码分割。该插件能够确保样式的加载顺序与源代码中的导入顺序保持一致,避免因 CSS 加载顺序错误而导致的 UI 问题。

import { rspack } from '@rspack/core';

export default {
  plugins: [
    new rspack.experiments.CssChunkingPlugin({
      // ...options
    }),
  ],
};

启用 CssChunkingPlugin 后,CSS 模块的代码分割将完全由该插件处理,optimization.splitChunks 配置将不再对 CSS 模块生效,你可以查看 使用文档 了解更多。

该插件由 Next.js 的 CSS Chunking 功能启发而来,感谢 Next.js 团队在这一领域的创新。

增强 lazy compilation

Rspack 现已支持在 MultiCompiler 中启用 lazy compilation,这意味着当你在单次构建中使用多份 Rspack 配置时,可以为不同的 compiler 实例独立设置各自的 lazyCompilation 选项

export default [
  {
    target: 'web',
    experiments: {
      // enable lazy compilation for client
      lazyCompilation: true,
    },
  },
  {
    target: 'node',
    experiments: {
      // disable lazy compilation for server
      lazyCompilation: false,
    },
  },
];

自定义文件读取系统

Rspack 现在允许你自定义 compiler.inputFileSystem(编译器的文件读取系统),该功能可以通过配置 experiments.useInputFileSystem 开启,典型的使用场景包括:

import VirtualModulesPlugin from 'webpack-virtual-modules';

export default {
  entry: './virtualEntry.js',
  plugins: [
    new VirtualModulesPlugin({
      'virtualEntry.js': `console.log('virtual entry')`,
    }),
  ],
  experiments: {
    useInputFileSystem: [/virtualEntry\.js$/],
  },
};

由于自定义的 inputFileSystem 是通过 JavaScript 实现的,可能导致性能下降。为了缓解这个问题,useInputFileSystem 允许你传入一个正则表达式数组,过滤哪些文件需要从自定义的 inputFileSystem 读取,避免因替换原生文件系统而导致的性能开销。

未来我们还计划在 Rspack 中内置虚拟模块支持,从而提供更好的性能和使用体验。

详细用法请参考 文档

性能分析工具

Rspack 1.4 引入了更精确的 tracing 能力,它可以基于 perfetto 进行性能分析,用于快速定位构建性能的瓶颈。

你可以通过 RSPACK_PROFILE 环境变量开启 tracing:

RSPACK_PROFILE=OVERVIEW rspack build

生成的 rspack.pftrace 文件可在 ui.perfetto.dev 中进行可视化分析:

tracing

详细的用法请参考 Tracing 文档

依赖升级

在 Rspack 1.4 中,我们升级了一些主要依赖的版本,包括:

  • Rspack 现在使用 Zod v4 来校验配置的正确性。
  • create-rspack 现在提供 Biome v2 作为可选的代码校验和格式化的工具。

Rstack 进展

Rstack 是一个围绕 Rspack 打造的 JavaScript 统一工具链,具有优秀的性能和一致的架构。

Rsbuild 1.4

Rsbuild 1.4 已与 Rspack 1.4 同步发布,值得关注的特性有:

Chrome DevTools 集成

我们引入了全新的 rsbuild-plugin-devtools-json 插件,通过该插件,你可以无缝集成 Chrome DevTools 的 自动工作区文件夹 (Automatic Workspace Folders) 新特性。这意味着你可以在 DevTools 中直接修改和调试源代码,并将改动保存到本地文件系统。

rsbuild plugin devtools json

改进查询参数

Rsbuild 现在内置支持 .js?raw 查询参数,允许你将 JavaScript、TypeScript 和 JSX 文件的原始内容作为文本导入。这在需要将代码作为字符串进行处理的场景下非常有用(例如展示代码示例)。

import rawJs from './script1.js?raw';
import rawTs from './script2.ts?raw';
import rawJsx from './script3.jsx?raw';

console.log(rawJs); // JS 文件的原始内容
console.log(rawTs); // TS 文件的原始内容
console.log(rawJsx); // JSX 文件的原始内容

改进浏览器兼容性

当你在 monorepo 中引用其他包的 JS 文件时,Rsbuild 现在默认会使用 SWC 编译它们,这有助于避免外部依赖引入的浏览器兼容性问题。

以下图为例,假设 app 的构建目标为 ES2016,utils 的构建目标为 ES2021,当 app/src/index.js 引用 utils/dist/index.js 时,SWC 现在会将它降级到 ES2016。

rsbuild monorepo compile scope

Rslib 0.10

Rslib 0.10 版本已发布,主要新增了以下功能:

ESM 产物优化

Rslib 现在默认生成更简洁清晰、体积更小的 ESM 产物。

rslib esm

构建 Vue 组件库

通过引入 rsbuild-plugin-unplugin-vue 插件,你可以使用 Rslib 生成 Vue 组件库的 bundleless 产物。

import { defineConfig } from '@rslib/core';
import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';

export default defineConfig({
  plugins: [pluginUnpluginVue()],
  lib: [
    {
      format: 'esm',
      bundle: false,
      output: {
        target: 'web',
      },
    },
  ],
});

输出 IIFE 格式

Rslib 现在可以生成 IIFE 格式 的产物,将代码包裹在函数表达式中。

rslib iife

阅读 博客 进一步了解 Rslib。

Rspress 2.0 beta

我们正在积极开发 Rspress 2.0,并发布了多个 beta 版本。目前,我们已完成大部分代码重构工作,并在 Rspress 2.0 中默认集成 Shiki 来提供更强大的代码高亮功能。

同时,我们正在开发全新的主题,预览效果如下:

rspress theme preview

Rsdoctor MCP

Rsdoctor 推出了 @rsdoctor/mcp-server,结合大模型来帮助你更好地分析构建数据。它能调用 Rsdoctor 的本地构建分析数据,提供智能的分析和优化建议。

Rsdoctor MCP 提供产物分析、依赖分析、产物优化建议和构建优化建议,能够分析产物的体积构成、依赖关系、重复依赖,并针对产物体积优化、代码分割以及构建性能提供相应的优化建议。

Rstest 发布

Rstest 是一个全新的基于 Rspack 的测试框架,它为 Rspack 生态提供了全面、一流的支持,能够轻松集成到现有的 Rspack 项目中,提供与 Jest 兼容的 API。

在这个月,我们发布了 Rstest 的 v0.0.3 版本,初步支持了 Node.js 和 UI 组件的测试,并在我们的 Rsbuild 等多个仓库中接入使用。

rstest

Rstest 目前仍处于早期阶段,我们建议你再关注一段时间,以确保它能够提供更完整的测试能力。

生态系统

next-rspack

自从 Rspack 加入 Next.js 生态 以来,我们的首要目标是提升 next-rspack 的稳定性和测试覆盖率。

在最新版本中,next-rspack 的功能已基本完善,测试覆盖率达到:

  • 生产构建 99.4%
  • 开发构建 98.4%

接下来,我们计划继续推进测试覆盖率至 100%,并进一步优化 next-rspack 的性能表现。

next-rspack

Kmi

Kmi 是一个基于 Umi 和 Rspack 的框架,通过集成 Rspack 作为打包工具,Kmi 带来了数倍于 webpack 版本的性能提升。

对于正在使用 Umi 框架的开发者而言,Kmi 提供了一种渐进式的迁移路径,让他们能够在保持项目稳定性的同时,享受 Rspack 带来的性能优势。

更多信息请参考 Kmi 仓库

升级指南

升级 SWC 插件

如果你的项目中使用了 SWC Wasm 插件(如 @swc/plugin-emotion 等),需要将插件升级至兼容 swc_core@29 的版本,否则可能因版本不兼容导致构建报错。

详情请查阅:常见问题 - SWC 插件版本不匹配

Lazy compilation 中间件

Lazy compilation 中间件的接入方式有所变化,该中间件现在可以从 compiler 实例中自动读取 lazyCompilation 选项,因此你不再需要手动传入 lazyCompilation 选项。

import { experiments, rspack } from '@rspack/core';
import { RspackDevServer } from '@rspack/dev-server';

const compiler = rspack([
  // ...multiple configs
]);

// no longer need to pass options to the middleware
const middleware = experiments.lazyCompilationMiddleware(compiler);

const server = new RspackDevServer(
  {
    setupMiddlewares: other => [middleware, ...other],
  },
  compiler,
);

Git指令及相关原理

2025年6月26日 16:56

Git 常见命令汇总

  1. 初始化

    git init : 初始化代码仓库,生成.git目录。在根目录下的 .gitconfig 中会包含配置信息,如用户email、用户名等

  2. git config

    git默认使用全局的用户和邮箱信息

    # 设置
    # 全局
    git config --global user.name <user_name>
    git config --global user.email <user_email>
    # 当前
    git config user.name <user_name>
    git config user.email <user_email>
    
    
    # 获取
    # 全局
    git config --global user.name
    git config --global user.email
    # 当前
    git config user.name
    git config user.email
    
    # 解决每次提交和拉取代码都需要输入用户名和密码
    git config --global credential.helper store
    
  3. git add

    将文件的更改添加到暂存区(Staging Area)。跟踪指定的文件,将其添加到 Git 的索引中。索引包含了将在下次提交中包含的文件列表。

    • Git 会计算文件的哈希值,生成文件的唯一标识符(SHA-1 值),更新Objects目录以及index文件。对于生成的objects中的文件可以查看详细信息。

      git cat-file -t <filename> # 文件类型
      git cat-file -s <filename> # 内容字节大小
      git cat-file -p <filename> # 文件内容
      
    • 新建一个同内容不同名的文件,可以发现.git下的文件目录没有变化,说明objects中保存的是文件类型和文件内容,不包括文件名。 echo "hello git">hello.txt

    • Git 用到的hash算法是SHA1算法

      echo "hello git" |  shasum    # d6a96ae3b442218a91512b9e1c57b9578b487a0b
      
    • Git的计算方式

      echo "blob 10\0hello git" | shasum  # 这里就是objects中使用的hash算法
      
  4. git commit

    在objects目录下生成一个commit类型的对象,一个tree类型的对象。

    1. 树对象(Tree Object):树对象代表了该次提交时整个项目目录结构的快照。它记录了当前提交状态下的文件和目录的组织结构。树对象保存了每个文件或子目录的名称、类型(blob 或 tree)和相应的哈希值。

      • 文件(blob):文件类型的树对象表示一个具体的文件,它包含文件名、文件模式(如文件权限)和文件内容的哈希值。文件内容的哈希值在 Git 中以 blob 对象的形式进行存储。
      • 目录(tree):目录类型的树对象表示一个子目录,它包含子目录或文件的引用。每个子目录都对应一个树对象,而文件对应的是 blob 对象。
    2. 提交对象(Commit Object):提交对象代表了一次具体的提交操作。它包含了该次提交的作者、提交时间戳、父提交引用和提交消息等信息。

      • 作者和时间戳:提交对象记录了该次提交的作者信息和提交时间。

      • 父提交引用:除了第一次提交外,每个提交对象都包含一个或多个父提交的引用。这些引用指向了该次提交的直接前导提交。这样形成了提交历史的有向无环图(DAG)结构,记录了提交的衍生关系。

      • 提交消息:提交对象包含了提交时输入的提交消息,用于描述该次提交的目的和变更内容。

  5. git 分支管理

  • HEAD是一个特殊指针,始终指向当前分支的最新commit

    image.png

  • 分支操作

    git branch  //列出当前所有本地分支
    git branch -r // 查看所有的远程分支
    git branch -a // 查看所有分支
    git branch <branch_name>  // 创建分支,如过已经存在,报错
    git branch -D <branch_name>  // 删除分支,不能删除当前分支
    
    git checkout <branch_name> // 切换分支,也可以直接checkout到特定的commit(detached Head)
    
    git branch -m <new-branch-name> // 分支重命名
    git branch -v // 查看每个分支的最后一次提交信息
    git branch -vv // 用于查看分支列表及其对应的追踪信息
    
    git switch <branch> // 切换到已存在的分支
    git switch -c <new-branch> // 创建并切换到新的分支,等价于branch + switch
    git switch -f <branch>  // 强制切换分支(丢弃当前更改)
    git switch <commit/branch/tag> --detach // 切换到某个提交或标签:
    

假设删除了一个分支,可以通过detached Head去找回删除分支的commit

```
git checkout <commit_code>
git checkout -b <branch_name>
```
  • git diff

    比较文件或提交之间差异,例如当前工作目录和最新提交之间的差异,或者两个提交之间的差异。

    # 比较工作目录和最新的commit的差异
    git diff
    
    # 比较指定文件在工作目录和最新提交之间的差异
    git diff <file-name>
    
    # 比较两个提交之间的差异:
    git diff <commit1> <commit2>
    
    # 比较指定分支和当前分支之间的差异
    git diff <branch-name>
    
    # 比较指定提交和当前工作目录之间的差异
    git diff <commit>
    
    # 比较指定提交和当前工作目录之间的差异(包括未暂存的更改
    git diff <branch1> <branch2>
    
    # 比较两个分支之间的差异
    git diff <commit> -- <file-name>
    
    # 对比索引区和代码 仓库
    git diff —cached
    

    image.png

  1. 仓库管理

    1. 新建一个仓库

    2. 将远程仓库添加本地:

      1. git remote add origin url

      2. 查看配置:cat .git/config ,发现出现了remote origin相关信息

      3. 上传到远程仓库:git push -u origin master

      4. 查看.git文件目录:出现了origin目录。

      5. 此时,查看heads和origin远程master和head对应的版本号一致。

      cat ..git\refs\remotes\origin\master cat ..git\refs\heads\master

  2. 压缩、解压

    每次修改都会生成新的bolb对象,通过压缩可以提高传输效率。将object中的文件压缩,全部放入pack中

    git gc  // 压缩
    git verify-pack -v ..git\objects\pack\pack-c992458971df8d681e44771882f19e49298fc75b.idx  //  查看具体的压缩情况
    git unpack-objects < .git/<pack_url> // 解压缩
    
  3. 垃圾回收

    当我们将同一个文件的多次修改,分别add到索引区时,会产生3个对应的blob,在进行commit,会生成对应的tree和commit文件,但是前两次修改产生的blob就是垃圾对象。git gc会将一个blob、一个commit、一个tree压缩,但是多余的blob不会删除

    git prune  // 删除多余的对象
    

    分支删除之后遗留的垃圾对象

    git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 \
      -c gc.rerereresolved=0 -c gc.rerereunresolved=0 \
      -c gc.pruneExpire=npw gc "$@"
    
  4. git merge

    1. fast forward:如果目标分支在源分支的前进历史中没有额外的提交,那么 Git 将执行快进合并。这种合并模式可以产生一个干净的、线性的提交历史。切换回master分支进行merge,生成一个ORIG_HEAD可以用于回滚。 git merge bugfix

    2. 3 way merge

      • 工作原理:3-way merge比较目标分支、源分支和共同祖先之间的差异,并尝试自动合并这些更改。

      • 优势:3-way merge能够自动处理没有冲突的更改,因此对于大多数情况下的合并是简单且有效的。

      • 适用场景:3-way merge适用于多人协作的项目,特别是当多个人在同一个分支上工作并推送更改时,以及当存在冲突时需要手动解决。

      • 分叉的merge会导致parent出现两个,如果c3、c4 修改了同一个文件会出现冲突。

        image.png

        冲突,会保存三个不同版本的blob,冲突解决commit之后会只剩一个。

    3. Rebase merge(合并衍合):

      • 工作原理:Rebase merge将源分支的每个提交重新应用到目标分支上,形成一系列新的提交。它不会创建合并提交,而是重演源分支的提交历史,并将它们放在目标分支的顶部。
      • 优势:Rebase merge可以产生一个干净、线性的提交历史,不会产生合并提交。它使得提交历史更易于理解、回溯和维护。
      • 适用场景:Rebase merge适用于个人分支或仅自己使用的分支上的合并
  5. Rebase

    要实现fast-forward,必须让新建分支用于master分支最新的commit。要实现这个功能需要使用git rebase

    rebase存在的问题:

    1. 一般不会在master分支上rebase
    2. master的commit(EC5BE)可能和dev分支上的commit在rebase的时候存在冲突。
    3. 如果之前dev分支已经上传到仓库,协作人员拉在本地进行修改,这时再进行rebase会影响他人工作。
  6. Tag

    使用带message的tag会生成一个新的类型为tag的objects,且删除tag之后会残留垃圾。同时在refs/tags中会生成新的文件指向commit对象。

    如果只是生成一个tag,不会生成对应的object,只会在refs/tags中会生成新的文件指向commit对象

    git tag <tag_name>  // 执行commit的hash
    git tag -a <tag_name> -m <tag message>
    git tag -a <tag_name> <commit value>
    git tag  // list
    git tag -d <tag_name> -M <tag message>
    
  7. 本地分支和远程分支

    远程分支的信息被压缩在packed-refs中,不实时

    // 克隆远程代码
    git clone <remote_url>
    // 同步远程分支
    git fetch 
    // 远程仓库的名字和url
    git remote -v
    // 联网检查远程分支和本地分支的一个连接关系
    git remote show origin
    
  8. git fetch

    检查远程仓库的代码情况,更新本地分支,对于远程分支删除的情况是不被处理的。

    git fetch —prune : 先剪枝再同步

    fetch结束可以通过merge让本地分支和远程分支保持一致

    情况1:

    git clone url
    // someone push new commit to github
    git fetch 
    git merge origin/master
    

    image.png

    情况2:

    git clone url
    // someone push new commit to github
    // local master have a new commit
    git fetch 
    git merge origin/master
    

    image.png

  9. git pull

    FETCH_HEAD记录执行git fetch/ git pull分支的对应远程分支的最新的commit

    ORIG_HEAD 记录git pull之前对应的commit

    git pull  // 默认远程仓库获取并合并更改,origin
    
    git pull <remote>  // 从指定远程仓库获取并合并更改
    
    git pull --rebase  //获取并在合并前进行变基(rebase)操作
    
    git pull --no-commit   // 获取并忽略远程仓库的更改
    
  10. git push

    git push // 没有分支关联的时候会报错
    
    git push origin newBranch  // 推送
    
    git push -u origin newBranch  // 推送且将远程分支和本地分支相连 
    
    git push origin -d branch_name // 删除远程分支
    
  11. 类似指令对比

  • git fetch & git pull

    1、git pull:从远程获取最新版本并merge到本地,会自动合并或修改当前的工作。相当于git fetch与git merge两条命令

    2、git fetch :会将数据拉取到本地仓库 ,它并不会自动合并或修改当前的工作

    3、在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并

  • git pull & git clone

    1、git clone是将整个工程复制下来所以,不需要本地是仓库(没有.git文件夹),第一次拉取项目不需要使用git init 初始化本地仓库

    2、git pull需要使用git init初始化本地仓库,

    3、git clone 可以直接切换远程分支,git pull需要切换到当前分支

    4、git clone 可以直接指定远程分支推送,git pull需要关联远程仓库(git add origin 远程仓库地址)才能push

  • git switch & git checkout

    • git switch 命令提供更直观和一致的分支切换语义。它的主要用法是 git switch <branch>,用于切换到已存在的分支或创建并切换到新的分支(使用 -c 参数)。git checkout 命令在早期版本的 Git 中,它的语义较为复杂且多样化。除了切换分支外,git checkout 还用于切换提交、还原文件等。
    • git switch 在切换分支时具有更高的安全性。如果存在未提交的更改或未保存的工作目录状态,git switch 会拒绝切换分支,以避免意外丢失或混淆更改。git checkout 在切换分支时较为宽松,它不会检查工作目录的状态,因此可能会在存在未提交的更改时执行切换,潜在地导致更改的丢失。
  • git log & git reflog

    git log 用于查看提交历史记录,显示了当前分支上的所有提交。

    git reflog 用于查看引用日志,显示了当前仓库的引用(如分支、HEAD)的移动历史记录。git reflog 对于恢复误操作、找回丢失的提交等非常有用。

  • git rm & git reset & git restore & git revert

    • git rm 用于从 Git 仓库中删除文件或目录。

      git rm <file>  // 删除已跟踪的文件,从 Git 仓库中删除指定的文件放入暂存区,下一次提交时从仓库中彻底删除。
      
      git rm -r <directory>  // 递归删除目录
      
      git rm -f <file/directory>  // 强制删除,即使它们处于修改或未跟踪状态
      
      git rm --cached <file>  // 删除 Git 仓库中跟踪的文件,但保留本地工作目录中的文件
      
      git rm <pattern> // 使用通配符或正则表达式指定一个模式来删除多个文件
      
    • git reset 用于重置 Git 仓库状态的命令,它可以在不同的模式下修改 HEAD 和分支指针,以及更改索引和工作目录的内容。

      git reset [--hard] <commit>  // 将当前分支的 HEAD、索引和工作目录全部重置为指定的 <commit>。丢弃当前的所有更改,慎用。
      
      git reset [--mixed] <commit> // 将当前分支的索引重置为指定的 <commit>,但保留工作目录中的更改。这意味着您可以重新提交工作目录的更改。
      
      git reset [--soft] <commit> // 将当前分支的 HEAD 指针移动到指定的 <commit>,但不更改索引和工作目录。这样可以重新提交以前的提交或将它们合并为单个提交。
      
      git reset [--hard] HEAD@{<n>}  // 使用引用(reflog)中的记录恢复到以前的提交状态
      
    • git restore 用于还原文件的修改,恢复到先前的状态。它可以撤销工作目录和索引中的更改,或恢复已删除的文件。

      git restore <file>  // 恢复工作目录到最近一次提交的状态,丢弃在工作目录中的任何修改。
      
      git restore --staged <file> // 将指定的文件恢复到最近一次提交的状态,同时将其从索引中移除。这可以撤销 git add 操作。
      
      git restore --source=<commit> <file>  // 恢复已删除的文件
      
      git restore .   // 使用 . 可以批量恢复整个工作目录中的所有文件,将它们恢复到最近一次提交的状态。
      
    • git revert <commit>:使用指定的提交哈希 <commit> 来创建一个新的提交,该提交撤销了指定提交的更改。这种回滚方法会保留原始提交历史,并在回滚提交后生成一个新的提交来撤销先前的更改。

❌
❌