子弈的 2024 年度总结

4,350 阅读42分钟

2025 年已经到来,在 Web 前端这个岗位上不知不觉已经工作了 7 年。这里将自己 2024 年的学习和写作情况做一些简单总结。

温馨提示:感兴趣的同学可以追溯 2023 前端年度技术总结

2024 技能更新

紫色部分是今年有所实践的技术。总体来看,在 AI 技术上进行了一些简单实践: Front End.png

2024 学习情况

AI

今年在 AI 技术上涉及最多,如果说去年对 AI 的原理和基础知识有了基本了解,那么今年在 AI 上进行了几个简单的学习和实践,主要包括以下内容:

方向细分学习关键词
Open AI 基础Prompt Engineering(提示工程)提示词要素(指令、上下文、输入数据、输出指示)、零样本提示、少样本提示、链式思考 CoT 提示(中间推理步骤、零样本 CoT 提示)、Prompt Chaining、思维数 ToT、检索增强生成 RAGFew Shot 等
Open AI 基础Fine- Tuning(微调)训练模型(数据探索、数据准备)、测试模型、使用微调模型、根据统计数据迭代微调模型(分析微调模型、迭代数据质量、迭代数据量、迭代参数和)、微调模型和基本模型评估对比等
低代码 AIAI  生成 & 修改竞品分析、 Prompt 组装(含 RAG、多模态生成、JSON PatchYoga 跨平台布局引擎、HTML & JSON Schema 转换
Java Copilot简单 Copilot 设计尝试Java 工程配置、检索增强生成 RAG、多任务调度代码生成
CopilotGithub Copilot 原理解析竞品分析、Code 模型能力对比、Sourcemap 逆向还原、VS Code Extension API、Prompt 提取策略(Prompt 配置、相邻文件文件获取、相似 Snnippets 滑动窗口计算)、Prompt 组装策略(剩余 Token 计算、组装优先级)、补全策略(单行补全、多行补全)、补全缓存、补全请求、LSP(Language Server Protocol)、Tree Sitter(AST 解析)

低代码 AI

在低代码 AI Prompt 的组装设计中,使用了 Prompt Engineering(提示工程)的 CoT 和 RAG 提示能力,以增强 AI 生成的稳定性和准确性。在 AI 修改(在已有页面中发送修改指令)的设计中,为了节省请求的 Token 数量,仅让 AI 返回需要增删改的节点信息,并通过 JSON Patch 的方式更新节点。由于低代码编辑器采用自由布局(类似于 iOS 的 Frame based layout),需要 AI 返回 Frame 信息(lefttopwidth 和 height 等位置和大小信息)的 JSON Schema,但 AI 往往无法准确计算出节点的位置信息(例如节点位置重叠、忽略 padding 处理等)。为此,尝试让 AI 返回 Flex 布局的 JSON Schema 或者 HTML,然后利用算法计算出 Frame 信息,粗略的实现流程如下所示:

image.png

计算 Frame 信息的方案包括静态和动态两种方式,静态计算采用  Yoga 跨平台布局引擎 实现,需要强制让 AI 返回符合 Flex 布局的 JSON Schema(对于 AI 而言具备解释成本,但是相对于 Frame Layout 解释成本变低,例如需要防止 AI 生成 Grid 布局),然后通过 Yoga Layout 静态计算出 Frame 信息(如何考虑数据的动态填充渲染计算呢)。动态计算则是将 JSON Schema 直接通过低代码引擎(支持 Flex 布局渲染 Antd 组件库)进行浏览器渲染,经过浏览器渲染后计算出对应的 Frame 信息,然后转化成 Frame based Layout 编辑态布局。当然,也可以直接让 AI 返回 HTML 信息(HTML 不支持 Antd 组件库,那么如何在 HTML 中渲染出类似于 Antd 组件库的能力呢?),然后进行浏览器渲染,经过渲染后再转化成编辑态需要的 JSON Schema。大家可以猜猜哪一种方案相对于 AI 而言生成效果更好,那种方案对于 AI 而言解释成本更高?

Java Copilot 设计尝试

为了方便 Java 开发人员快速基于数据模型生成对应的 CRUD 接口实现,一起参与共建了 Java Copilot 的 CLI 形态设计。在此期间,简单学习了 Java 服务端的一些基础知识,包括包管理器、编译器和  Java 虚拟机等:

image.png

并简单尝试了 JDK、Meavn 的安装、配置和 IDEA 的使用。Java Copilot 起初的设想是一个非常完备的设计方案,具体的设计思路如下所示:

image.png

温馨提示:前期简单调研和参考了 Github Copilot 的实现原理。

当时为了快速设计 Demo 原型,最终的实现方案如下所示:

image.png

Github Copilot 原理解析

在设计 Java Copilot 的同时额外研究了 Github Copilot 的实现原理。在研究原理之前,首先对 Github Copilot 的 VS Code Extension 工程项目进行了逆向还原,根据 Sourcemap 对编译代码进行了 AST 解析,将源代码的变量名进行了还原处理,如下所示:

image.png

温馨提示:为什么要增加注释信息,增加注释信息的 extension.js 不会破坏原有的代码结构,支持运行和打印调试,而 extension- recovery.js 并不能完整还原源代码,只能用于源码阅读,运行时存在一些其它问题(做了各种尝试,最终发现 Copilot 官方在生成 Sourcemap 时专门去除了一些源代码信息)。

尽管还原了工程项目,但是根据 VS Code 扩展入口深入阅读代码非常费劲,第一个星期基本上在阅读一些扩展上下文初始化信息,没有找到核心内容(也是因为对 VS Code Extension 的补全 API 不熟悉,当时就没有想到先找补全 API 的实现),除此之外,如果对 VS Code Exntension 的开发不熟悉,还会继续增加解析成本,中途一度差点放弃。第二个星期突然找到了核心代码部分,还是要感谢坚持的自己,至此一发不可收拾,断断续续花了将近两个月的时间将核心解析完毕,补上一张凌乱的 XMind 核心图感受一下(就像我当时的解析心情一样凌乱):

Github Copilot 核心分析.png

将上述内容进行总结抽象,Github Copilot 的整个执行流程大致如下所示:

image.png

整个实现的核心是围绕 VS Code  Extension 的补全 API 进行 Prompt 组装和 AI 请求补全,当然内部还会涉及 LSP(Language Server Protocol)Tree Sitter(多语言 AST 的 WebAssembly 解析器)、Web Worker 多线程以及一些相似性算法,例如相邻文件的代码片段利用滑动窗口算法进行相似度匹配计算

image.png

温馨提示:多线程的执行还需要涉及将对应的 JS 脚本进行工程的逆向还原处理,但是在解析的过程中发现在 extension.js 中同样存在多线程中执行的代码,猜想是 Github 的工程师们一开始没有设计多线程,后续为了提升性能进行了 Webpack 多入口的配置。为了减少逆向成本,索性直接更改了扩展脚本,关闭了多线程进行调试。

整个 Github Copilot 的核心解析即将在《深入浅出微前端》的番外篇中出现,整体包括 Github Copilot 的功能介绍、竞品分析、Code 模型介绍 & 对比、原理解析(Prompt 提取策略、组装策略、补全策略和补全请求)和  LSP Demo 实现等,后续也会想办法在社区进行一次分享,感兴趣的同学可以关注一下。

温馨提示:你知道 Github Copilot 的代码补全使用的是什么 AI 模型吗?

LSP

Github Copilot 使用 AI 进行智能提示,但是也内置了 LSP 服务的补全能力(该功能默认不开启,作为 AI Copilot 的备选)。LSP(Language Server Protocol)是一种协议,类似于 CDP 协议,它是由微软定义,主要是为了解决编辑器中语言的补全、诊断、跳转到定义等功能:

image.png

当然,如果直接将语言服务直接集成到 IDE 中会占据大量的 CPU 资源,因为它会对大量的文件进行 AST 解析和静态分析。为了确保语言功能不影响 VS Code (IDE)本身的性能,可以通过 LSP 协议将 IDE 和 LSP 语言服务功能解耦开,语言服务器可以用任何服务语言实现(例如 Node.js、PHP、Java 等),并在自己的独立进程中运行,从而避免 IDE 的性能损耗,而 IDE 只需要请求语言服务器进行通信即可。除此之外,任何符合 LSP 的语言工具都可以与多个符合 LSP 的代码编辑器集成,例如:

image.png

可以通过 Node SDK 来集成 LSP,让 LSP 服务运行在 Node 的环境中。例如不同的语言可以创建不同的语言服务器,如下所示:

5eecdaf48460cde5e25f0ada718686d26457f3cdc98aed2a58e70b814913bc360a414d3de9277d871abf3af1cbd752498502f534c277d0d4acb43caf8e0cade2184fd0ea91fdc2ccdba3319c9354f026606326575311938dfc653b69905bac42.png

了解了基础知识后,简单尝试使用 TypeScript 编写 LSP 服务实现了 VS Code 的补全示例,例如以下通过键入 dd 自动提示 dd.ready(钉钉开放平台的 JS API):

image.png

温馨提示:示例参考了 Language Server Extension Guide 进行设计。

微前端小册

原计划今年完成《深入浅出微前端》小册的撰写,但是因为番外篇(居然有人觉得番外篇比正片好看...)和生了一个不大不小的病,导致国庆之后两个月没有更新,在这里对于小册读者说一声抱歉。当然,小册的内容也在不断地增加,从原来的 42 个章节增加到了 59 个章节,今年小册的质量相对于去年应该有所提升,核心原理均已解析完毕,主要更新的章节内容包括:

方向细分学习内容
微前端小册框架示例single- spa 的 NPM、Script、Fetch 和 Code Splitting 示例,在 Script 示例中提供了微应用生命周期的 SDK 设计方式。在 Fetch 示例中额外讲解了 Webpack libirary 、libraryTarget 配置和 Webpack 模块化的运行时原理,并重点讲解了如何提供通用化的识别方式获取微应用的生命周期对象。在 Code Splitting 示例中额外解析了 Code Splitting 的 Webpack 运行时原理。
微前端小册框架解析single- spa 的源码解析、qiankun 的使用示例、import- html- entry的源码解析,并循序渐进地讲解了 qiankun 无沙箱、CSS 隔离、CSS 动态隔离、快照隔离、Legacy Proxy 隔离以及 Proxy 隔离的实现原理。
微前端小册框架设计在状态管理设计中额外讲解了 Monorepo 的设计结构以及 SDK 的调试工程化设计,隔离、性能优化、通信和整体解决方案预计在 2025 年完成。
微前端小册番外篇在依赖注入解析篇中讲解了元编程(Metaprogramming)、反射(Reflect)、反射元编程、元数据( Reflect Metadata)、五种 TypeScript 装饰器类型、依赖注入(Dependency Injection,简称 DI)和控制反转(Inversion of Control,简称 IoC)等概念,并基于以上基础概念简单实现了一个 Midway 依赖注入的框架示例。

撰写小册其实花了我非常多的时间,尤其是画一些 UML 图、流程图和框架图,这里给出一张 single-spa 的运行 UML 图设计示例,供大家进行参考:

vue.svg

除了小册的质量提升,小册章节的内容量也在提升,如下所示:

章节字数学习时长
19.框架解析:single-spa 的 NPM 示例81461小时46分
20.框架解析:single-spa 的 Script 示例63901小时12分
21.框架解析:single-spa 的 Fetch 示例63902小时36分
22.框架解析:single-spa 的 Code Splitting 示例166293小时14分
23.框架解析:single-spa 源码解析178114小时1分
24.框架解析:qiankun 使用示例64641小时59分
25.框架解析:import-html-entry 源码解析174034小时50分
26.框架解析:qiankun 源码解析 - 无沙箱模式187134小时48分
27.框架解析:qiankun 源码解析 - CSS 隔离95712小时6分
28.框架解析:qiankun 源码解析 - CSS 动态隔离115093小时35分
29.框架解析:qiankun 源码解析 - 快照隔离84632小时50分
30.框架解析:qiankun 源码解析 - Legacy Proxy 隔离56531小时19分
31.框架解析:qiankun 源码解析 - Proxy 隔离186086小时2分
44.框架设计:状态管理101821小时38分
56.番外篇:依赖注入解析101182小时19分

每次撰写小册章节时都会先调研一些理论基础,并花费大量的时间对一些周边相关的运行原理进行解析,例如工程化原理、Webpack 编译产物运行时原理等,用于加强整个小册的深度和通用性设计,帮助大家掌握微前端的同时也对前端的其它知识有更深入的了解。

DI & IoC

依赖注入(Dependency Injection,简称 DI)和控制反转(Inversion of Control,简称 IoC)是软件设计中的两个密切相关的概念,它们通常用于实现松耦合的设计,从而提高代码的可维护性和可扩展性。Midway 是一个服务端 IoC 依赖注入的 Node.js 应用框架,为了完整掌握 Midway 的内部实现原理,简单实现了一个 IoC 自动依赖注入的示例,实现示例的项目工程如下所示:

.
├── src                           # 源码目录(可以理解为 midway 中的 src 目录)
│   ├── animal.ts                 
│   ├── cat.ts                    
│   └── dog.ts    
├── ioc                           # 自动识别文件和自动依赖注入功能(需实现)(midway 不对外提供)     
│   ├── container.ts              # IoC 容器(注册和获取实例)
│   ├── inject.ts                 # 属性装饰器(通过元数据建立依赖关系)
│   ├── key.ts                    # 定义元数据的 key
│   ├── load.ts                   # 自动扫描和执行 src 目录的模块文件,实现 IoC 自动绑定功能
│   └── provider.ts               # 通过元数据建立 IoC 容器的绑定关系
└── index.ts                      # 启动脚本

src 目录下的代码可以理解为已经实现了 IoC 依赖注入功能的代码,主要提供了对应的类和装饰器,通过装饰器声明类之间的依赖关系:

// src/animal.ts
import { Inject } from "../ioc/inject"; // 需实现
import Cat from "./cat";
import Dog from "./dog";

@Provider()
export default class Animal {
  @Inject()
  private cat: Cat;

  @Inject()
  private dog: Dog;

  print() {
    this.cat.print();
    this.dog.print();
  }
}


// src/cat.ts
import { Provider } from "../ioc/provider"; // 需实现

@Provider()
export default class Cat {
  print() {
    console.log("Cat");
  }
}

// src/dog.ts
import { Provider } from "../ioc/provider"; // 需实现

@Provider()
export default class Dog {
  print() {
    console.log("Dog");
  }
}


// src/index.ts 启动脚本

// IoC 容器,需实现
import { IoCContainer } from "./ioc/container"; 
 // 程序加载,负责扫描 @Provide、@Inject 相应的实例化及绑定处理,需实现
import { load } from "./ioc/load";

const container = new IoCContainer();
load(container, "./src");

// 无需手动绑定,自动绑定
// Animal 依赖 Cat 和 Dog,通过 @Inject 装饰器自动绑定
// 这里的 Animal 字符串是 Animal 类上的元数据 id,对应的是 Animal 类的构造函数名称
// INFO: 这里也可以通过  Reflect.getMetadata(providerKey, Animal) 获取到 Animal 类上的元数据;
const animal = container.get("Animal");
animal.print(); // Cat Dog

ioc 目录大致实现流程如下所示:

image.png

依赖注入在服务端设计是非常常见的一种技术,当然在前端领域也可以参考此类技术实现一些框架,例如扩展框架。当然,如果你想使用 IoC 技术来实现一些公司通用的框架设计,可以使用社区中现有的 IoC 库,例如 InversifyJS

温馨提示:DI 和  IoC 的关系是什么?

编译工具

设计了 Webpack Loader & Plugin,用于优化低代码编译的体积问题,并用于解决开发态 JSON Schema 的 URL 请求问题。假设存在如下开发态代码:

// @xxx/lowcode-render-engine 内部默认集成了所有的 Antd 组件和 Antd Icons
import LowCodeRenderEngine from '@xxx/lowcode-render-engine';

// 组件化的使用方式
// LowCodeRenderEngine 开发态支持 URL 传递,URL 对应的是低代码平台产生的 JSON Schema 地址(固定地址)
// LowCodeRenderEngine 支持 schema 传递,对应的是 JSON Schema
// LowCodeRenderEngine 内部会根据 JSON Schame 声明的 Antd 组件名称进行动态渲染处理
<LowCodeRenderEngine url="http://aaa.com/bbb.json"></LowCodeRenderEngine>
<LowCodeRenderEngine url="http://aaa.com/ccc.json"></LowCodeRenderEngine>

我们希望生产态时,它能转化成如下功能(注意不是编译后的代码):

// @xxx/lowcode-render-engine 内部的 Antd 和 Icons 能够按需引入
// 根据 JSON Schema 中的 Antd 和 Icons 使用情况而定
import LowCodeRenderEngine from '@xxx/lowcode-render-engine';

// 组件化的使用方式
// 生产态部署页面所在的环境可能无法请求到 URL 地址对应的服务环境,因此需要将 URL 转换成真实的 JSON Schema 值
<LowCodeRenderEngine schema=[{/- url 地址对应的 JSON Schema  - /}]></LowCodeRenderEngine>

在 LowCodeRenderEngine 中全量引入了 Antd 组件库和 Icon,从而实现低代码的组件动态渲染。但是真正在开发时可能只需要使用组件库中的一些组件,那么如何使得上述情况可以实现按需引入呢?除此之外,我们希望开发态引入的是 url 属性对应的 JSON Schema 地址,而生产态引入 schema 属性对应的真正 JSON Schame 值。为了实现上述功能,我们可以借助 Webpack Loader 和 Webpack Plugin 来实现该功能,如下所示:

image.png

第一阶段需要对所有的 TSX 或者 JSX 文件通过 Webpack  Loader 一一进行 AST 解析,提取出对应的 URL 地址,然后批量请求 URL 地址对应的 JSON Schema 值,并将其重新设置到组件的 schema 属性上。第二阶段则是通过 Webpack Plugin 对所有使用的 schema 属性进行 AST 解析,解析出所有 JSON Schema 的集合,判断整个项目使用了那些 Antd 组件库和 Icons,并再次通过 AST 操作 @xxx/lowcode-render-engine 内部的 Antd 和 Icons 全量引入逻辑,将其替换成按需引入语法。例如:

// 内部原有实现
import * as designComponents from 'antd';
// AST 替换
// 根据 JSON Schema 动态分析需要按需引入的组件
import * as Button from "antd/es/button";
import * as FlexLayout from "antd/es/flex- layout";
import * as Layout from "antd/es/layout/Layout";
import Page from "antd/es/page";
const designComponents = {
  ...Button,
  ...FlexLayout,
  ...Layout,
  ...Page
};

2024 收藏书签

这里将平时学习时查阅的博客、文档、电子书、PPT、视频以及 Github 仓库等进行书签汇总,并按照收藏时间进行倒序排序。如果想浏览某一项技术,建议按照年份从低到高进行浏览。通过整理年度书签,可以用于分析自己在这一年中主要学习了哪些技术。

作者文章

面试

Copilot

Model

Open AI

Awesome

Sourcemap

模块化

服务端

协议

AST

VS Code

IoC

Chromium

TypeScript

JavaScript

HTML

iframe

Web Component

CSS

React

Vue

Svelte

HTTP

Git

NPM & Yarn

版本规范

Node.js

Lint & Prettier

Module Federation

Babel

构建工具

静态站点生成器

CI / CD

测试

Chrome DevTools

调试

扩展

组件库

性能优化

工程化

Monorepo

监控

运维

SSR

客户端

跨端

WebAssembly

微前端

Rust

低代码

IDE

风格指南

编程指南

解决方案

编译器相关

Mac App

Chrome 插件

VS Code 插件

终端

总结