每本书有唯一的 ISBN(资源标识)
+{{ JSON.stringify(example.body, null, 2) }}
+ {{ description }}
+展示RESTful API中的错误处理机制
+{{ JSON.stringify(example.body, null, 2) }}
+ 详解 HTTP 请求的组成部分
+GET /api/users/123 HTTP/1.1
+ Host: api.example.comContent-Type: application/jsonAuthorization: Bearer token123{
+ "name": "张三",
+ "email": "zhangsan@example.com"
+}
+ 通过生活中的类比理解 RESTful 资源概念
+每本书有唯一的 ISBN(资源标识)
+/library/books/123 表示第 123 号书
+GET(查看)、POST(借书)、PUT(修改)、DELETE(还书)
+{{ description }}
+{{ activePrinciple.explanation }}
+{{ activePrinciple.badExample }}
+ {{ activePrinciple.goodExample }}
+ {{ code.description }}
+{{ code.example.path }}
+ {{ JSON.stringify(code.example.response, null, 2) }}
+ {{ description }}
+四个时代的核心架构特征对比
+理解容器如何让应用"一次打包,到处运行"
+从手工部署到自动化流水线的变化
+用一个餐厅的成长历程,理解后端架构的 30 年变迁
+{{ stages[currentStage].scenario }}
+{{ stages[currentStage].mapping }}
+观察 K8s 如何自动调度容器、实现负载均衡和故障恢复
+观察多个独立服务如何协作,以及服务间通信方式
+观察单体应用如何处理请求,以及模块间的依赖关系
+点击"发送请求",观察早期 CGI 服务器的处理瓶颈
++ 这就是物理服务器 + CGI时代的核心问题:进程级隔离带来了稳定性,但也带来了巨大的性能开销。 +
+垂直扩展 vs 水平扩展
+观察 Serverless 如何按需执行函数、自动扩缩容
+每个时代的主流技术栈
+分层架构是整洁架构的基础,理解两者的关系有助于构建更灵活的系统
+| 特性 | +传统分层架构 | +整洁架构 | +
|---|---|---|
| 依赖方向 | +从上到下 | +从外到内 | +
| 核心业务位置 | +Service 层 | +Domain 层(中心) | +
| 框架依赖 | +较深(如 Spring) | +较浅(通过接口隔离) | +
| 可测试性 | +需要集成测试 | +核心可单元测试 | +
| 学习曲线 | +平缓 | +较陡 | +
| 适用场景 | +中小型项目、快速迭代 | +大型复杂业务、长期维护 | +
点击流程节点查看 Controller 如何接收和处理请求
+理解依赖方向,才能真正掌握分层架构
+上层模块不应该依赖下层模块的具体实现,而应该依赖于抽象。
++┌─────────────────────────────────────────────────────────────┐ +│ Controller Layer │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ UserController │ │ +│ │ - @Autowired private IUserService userService; │ │ +│ │ ✅ 依赖接口,不依赖实现 │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ 依赖(Dependency) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Service Layer │ │ +│ │ ┌────────────────────────────────────────────────┐ │ │ +│ │ │ UserServiceImpl │ │ │ +│ │ │ - @Autowired private UserRepository repository; │ │ │ +│ │ │ ✅ 依赖 Repository 接口 │ │ │ +│ │ └────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ 依赖 │ │ +│ │ ┌────────────────────────────────────────────────┐ │ │ +│ │ │ Repository Layer │ │ │ +│ │ │ ┌──────────────────────────────────────────┐ │ │ │ +│ │ │ │ UserRepository │ │ │ │ +│ │ │ │ - extends JpaRepository<User, Long> │ │ │ │ +│ │ │ └──────────────────────────────────────────┘ │ │ │ +│ │ │ │ │ │ │ +│ │ │ ▼ 依赖 │ │ │ +│ │ │ ┌──────────────────────────────────────────┐ │ │ │ +│ │ │ │ Domain Layer (核心领域) │ │ │ │ +│ │ │ │ ┌────────────────────────────────────┐ │ │ │ │ +│ │ │ │ │ User (Entity) │ │ │ │ │ +│ │ │ │ │ - 不包含任何层依赖 │ │ │ │ │ +│ │ │ │ │ - 被所有层依赖 │ │ │ │ │ +│ │ │ │ └────────────────────────────────────┘ │ │ │ │ +│ │ │ └──────────────────────────────────────────┘ │ │ │ +│ │ └────────────────────────────────────────────────┘ │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ ++
Domain 是业务概念的载体,所有层的依赖基础
+@Entity
+public class Order {
+ @Id
+ private Long id;
+ private Long userId;
+ private BigDecimal totalAmount;
+ private OrderStatus status;
+ private LocalDateTime createdAt;
+
+ // 只有 getter/setter,没有业务逻辑
+ public Long getId() { return id; }
+ public void setId(Long id) { this.id = id; }
+ // ... 其他 getter/setter
+}
+ @Service
+public class OrderService {
+
+ public void cancelOrder(Long orderId) {
+ Order order = orderRepository.findById(orderId)
+ .orElseThrow();
+
+ // 贫血模型:业务逻辑散落在 Service 里
+ if (order.getStatus() == OrderStatus.SHIPPED) {
+ throw new IllegalStateException("已发货订单不能取消");
+ }
+ if (order.getStatus() == OrderStatus.CANCELLED) {
+ throw new IllegalStateException("订单已取消");
+ }
+
+ // 修改状态
+ order.setStatus(OrderStatus.CANCELLED);
+ orderRepository.save(order);
+ }
+}
+ @Entity
+public class Order {
+ @Id
+ private Long id;
+ private Long userId;
+ private BigDecimal totalAmount;
+ private OrderStatus status;
+ private LocalDateTime createdAt;
+
+ // 🎯 业务行为封装在实体里
+
+ /**
+ * 取消订单
+ */
+ public void cancel() {
+ // 状态校验内聚在方法里
+ if (this.status == OrderStatus.SHIPPED) {
+ throw new IllegalStateException("已发货订单不能取消");
+ }
+ if (this.status == OrderStatus.CANCELLED) {
+ throw new IllegalStateException("订单已取消");
+ }
+
+ this.status = OrderStatus.CANCELLED;
+ // 可以触发领域事件
+ registerEvent(new OrderCancelledEvent(this.id));
+ }
+
+ /**
+ * 支付订单
+ */
+ public void pay(Payment payment) {
+ if (this.status != OrderStatus.PENDING_PAYMENT) {
+ throw new IllegalStateException("订单状态不正确");
+ }
+ if (!payment.getAmount().equals(this.totalAmount)) {
+ throw new IllegalArgumentException("支付金额不匹配");
+ }
+
+ this.status = OrderStatus.PAID;
+ this.paymentTime = LocalDateTime.now();
+ }
+
+ // ... 其他业务方法
+}
+ @Service
+@RequiredArgsConstructor
+public class OrderService {
+
+ private final OrderRepository orderRepository;
+ private final DomainEventPublisher eventPublisher;
+
+ @Transactional
+ public void cancelOrder(Long orderId) {
+ // 1. 加载聚合根
+ Order order = orderRepository.findById(orderId)
+ .orElseThrow(() -> new OrderNotFoundException(orderId));
+
+ // 2. 💡 调用领域对象的业务方法
+ // 业务规则封装在 Order 里,Service 只做协调
+ order.cancel();
+
+ // 3. 保存变更
+ orderRepository.save(order);
+
+ // 4. 发布领域事件
+ order.getDomainEvents().forEach(eventPublisher::publish);
+ order.clearDomainEvents();
+ }
+}
+ 值对象是没有唯一标识、不可变的对象,它描述了某种特征或属性。两个值对象如果所有属性相等,就被认为是同一个对象。
+// 值对象:不可变、无 ID
+public record Address(
+ String province, // 省
+ String city, // 市
+ String district, // 区
+ String street, // 街道
+ String zipCode // 邮编
+) {
+ // 值对象的方法通常是转换或计算
+ public String toDisplayString() {
+ return String.format("%s%s%s%s",
+ province, city, district, street);
+ }
+
+ // 校验逻辑
+ public boolean isValid() {
+ return StringUtils.isNotBlank(province)
+ && StringUtils.isNotBlank(city);
+ }
+}
+
+// 使用:地址相等只要属性相同
+Address addr1 = new Address("广东", "深圳", "南山", "科技园", "518000");
+Address addr2 = new Address("广东", "深圳", "南山", "科技园", "518000");
+
+System.out.println(addr1.equals(addr2)); // true - 值对象比较的是值
+ // 金钱是经典的值对象
+public record Money(
+ BigDecimal amount,
+ Currency currency
+) {
+
+ // 工厂方法
+ public static Money of(BigDecimal amount, String currencyCode) {
+ return new Money(amount, Currency.getInstance(currencyCode));
+ }
+
+ public static Money yuan(BigDecimal amount) {
+ return new Money(amount, Currency.getInstance("CNY"));
+ }
+
+ // 值对象的核心:运算返回新的值对象
+ public Money add(Money other) {
+ if (!this.currency.equals(other.currency)) {
+ throw new IllegalArgumentException("Cannot add different currencies");
+ }
+ return new Money(this.amount.add(other.amount), this.currency);
+ }
+
+ public Money multiply(int factor) {
+ return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), currency);
+ }
+
+ public boolean isGreaterThan(Money other) {
+ return this.amount.compareTo(other.amount) > 0;
+ }
+
+ // 格式化显示
+ public String toDisplayString() {
+ return currency.getSymbol() + amount.setScale(2, RoundingMode.HALF_UP);
+ }
+}
+
+// 使用示例
+Money price = Money.yuan(new BigDecimal("199.99"));
+Money shipping = Money.yuan(new BigDecimal("10.00"));
+Money discount = Money.yuan(new BigDecimal("20.00"));
+
+Money total = price.add(shipping).add(discount.negate());
+System.out.println(total.toDisplayString()); // ¥189.99
+ DTO(Data Transfer Object)是层与层之间传递数据的载体
+UserCreateRequest
+ UserCreateParam
+ UserEntity
+ {{ layerInfo.description }}
+Repository 封装数据访问逻辑,让上层无需关心数据库细节
+@Service
+public class OrderService {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate; // 直接依赖底层 JDBC
+
+ public List<Order> getUserOrders(Long userId) {
+ // ❌ 问题1:SQL 硬编码在 Service 里
+ // ❌ 问题2:更换数据库需要改业务代码
+ // ❌ 问题3:无法单元测试,必须连真实数据库
+ String sql = "SELECT * FROM orders WHERE user_id = ? AND deleted = 0";
+
+ return jdbcTemplate.query(sql, (rs, rowNum) -> {
+ Order order = new Order();
+ order.setId(rs.getLong("id"));
+ order.setUserId(rs.getLong("user_id"));
+ order.setTotalAmount(rs.getBigDecimal("total_amount"));
+ // ... 更多字段映射
+ return order;
+ }, userId);
+ }
+
+ public void updateOrderStatus(Long orderId, OrderStatus status) {
+ // ❌ 问题4:到处重复 SQL 片段
+ String sql = "UPDATE orders SET status = ?, updated_at = NOW() WHERE id = ?";
+ jdbcTemplate.update(sql, status.name(), orderId);
+ }
+}
+
+
+
+ // ========== 1. 实体定义(Domain) ==========
+@Entity
+@Table(name = "orders")
+public class Order {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "user_id", nullable = false)
+ private Long userId;
+
+ @Column(name = "total_amount")
+ private BigDecimal totalAmount;
+
+ @Enumerated(EnumType.STRING)
+ private OrderStatus status;
+
+ @Column(name = "deleted")
+ private Boolean deleted = false;
+
+ // getters and setters...
+}
+
+// ========== 2. Repository 接口定义 ==========
+@Repository
+public interface OrderRepository extends JpaRepository<Order, Long> {
+
+ // ✅ Spring Data 自动生成查询 - 简单查询
+ List<Order> findByUserIdAndDeletedFalse(Long userId);
+
+ // ✅ 自定义 JPQL 查询 - 复杂统计
+ @Query("""
+ SELECT new com.example.OrderStatistics(
+ o.userId,
+ COUNT(o),
+ SUM(o.totalAmount),
+ MAX(o.createdAt)
+ )
+ FROM Order o
+ WHERE o.createdAt BETWEEN :startDate AND :endDate
+ AND o.deleted = false
+ GROUP BY o.userId
+ HAVING COUNT(o) >= :minOrderCount
+ ORDER BY SUM(o.totalAmount) DESC
+ """)
+ List<OrderStatistics> findUserOrderStatistics(
+ @Param("startDate") LocalDateTime startDate,
+ @Param("endDate") LocalDateTime endDate,
+ @Param("minOrderCount") Long minOrderCount,
+ Pageable pageable
+ );
+
+ // ✅ 批量更新 - 修改状态
+ @Modifying
+ @Query("UPDATE Order o SET o.status = :newStatus, " +
+ "o.updatedAt = CURRENT_TIMESTAMP WHERE o.id IN :ids")
+ int batchUpdateStatus(
+ @Param("ids") List<Long> orderIds,
+ @Param("newStatus") OrderStatus newStatus
+ );
+}
+
+// ========== 3. Service 层(纯业务逻辑) ==========
+@Service
+public class OrderService {
+
+ @Autowired
+ private OrderRepository orderRepository; // ✅ 依赖接口,不依赖具体实现
+
+ @Autowired
+ private UserRepository userRepository;
+
+ // ✅ 业务方法清晰简洁,不关心数据怎么存
+ public List<OrderDTO> getUserOrders(Long userId) {
+ // 可以在这里加业务校验
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new UserNotFoundException(userId));
+
+ // 直接调用 Repository,SQL 藏在后面
+ List<Order> orders = orderRepository.findByUserIdAndDeletedFalse(userId);
+
+ // 转换为 DTO 返回
+ return orders.stream()
+ .map(OrderDTO::from)
+ .collect(Collectors.toList());
+ }
+}
+
+
+
+ Service 层编排业务逻辑,协调多个 Repository,管理事务边界
+{{ step.code }}
+ {{ principle.example }}
+ {{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+点击每个阶段查看详情,观察数据如何在管线中流动
+ +{{ stages[activeStage].description }}
+ +{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+模拟 AK/SK 的创建、使用和轮换流程
+点击查看详细的实施指南和代码示例
+{{ practice.description }}
+ +{{ practice.code }}
+ 角色扮演(AssumeRole)获取临时凭证
+import boto3
+
+# 在账号 A 中使用 IAM 用户凭证
+sts_client = boto3.client('sts')
+
+# 扮演账号 B 的角色
+assumed_role = sts_client.assume_role(
+ RoleArn='arn:aws:iam::123456789012:role/CrossAccountRole',
+ RoleSessionName='MySession',
+ DurationSeconds=3600
+)
+
+# 获取临时凭证
+credentials = assumed_role['Credentials']
+
+# 使用临时凭证访问账号 B 的资源
+s3_client = boto3.client(
+ 's3',
+ aws_access_key_id=credentials['AccessKeyId'],
+ aws_secret_access_key=credentials['SecretAccessKey'],
+ aws_session_token=credentials['SessionToken']
+)
+ 点击各个模块查看详细对比
+{{ selectedFeatureData.awsDetail }}
+{{ selectedFeatureData.awsExample }}
+ {{ selectedFeatureData.ramDetail }}
+{{ selectedFeatureData.ramExample }}
+ 点击步骤查看 SSO 单点登录流程
+{{ currentStepData.detail }}
+ +{{ currentStepData.code }}
+ 体验 MFA 双因素认证流程
+拖动查看角色如何关联多个策略
+点击切换查看不同维度的对比
+点击各个板块查看 AWS 与阿里云的对应服务
+拖动滑块调整场景参数,获取最佳计算方案
+根据您的业务特点,推荐最适合的数据库方案
+探索 AWS 和阿里云上的 K8s 服务及配套生态
+拖拽组件构建您的云上网络架构
+输入您的使用场景,对比不同计费模式的成本
+选择您的业务场景,一键生成安全防护方案
+回答几个简单问题,获取最适合您的云服务方案
+根据您的使用场景,推荐最适合的存储方案
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+当前视图:{{ viewModeText }}
+选中云商:{{ selectedProvider || '未选择' }}
+选中地域:{{ selectedRegion || '未选择' }}
+💡 提示:点击云服务商和地域可以查看不同组合的资源拓扑
+{{ subnet.range }}
+ /24 表示网络位占 24 位,主机位 8 位
+ 2^(32-24) = 256 个
+ 256 - 3 = 253 个(减去网络、广播、网关地址)
+ {{ vpcCidr }}
+ /{{ subnetMask }}
+ {{ subnets.length }} 个
+ {{ ipsPerSubnet }} 个
+ 每个子网至少预留 20% 的 IP 作为扩容缓冲
+核心数据放在私网子网,通过 NAT 访问外网
+同一 VPC 的不同子网放在不同可用区
+点击组件查看详情,观察组件树如何组织
+{{ selectedNodeInfo.description }}
+观察多个独立组件如何通过事件总线进行通信,注意内存管理的重要性
+使用 Event Bus 时,如果组件销毁前没有取消订阅($off),会导致内存泄漏。推荐在 beforeUnmount 钩子中清理订阅。
+// 正确做法
+export default {
+ created() {
+ this.$bus.$on('event', this.handler)
+ },
+ beforeUnmount() {
+ this.$bus.$off('event', this.handler) // 必须取消订阅
+ }
+}
+ 体验 MobX 的自动依赖追踪机制,理解 Observable、Action 和 Reaction 的关系
+使用 observable 或类属性装饰器 @observable 定义的状态。当状态变化时,所有依赖它的 Reaction 会自动重新执行。
使用 action 或 @action 装饰器标记的方法。用于修改 Observable 状态。Action 会批量处理变更通知,避免中间状态的重复渲染。
当 Observable 状态变化时自动执行的副作用。包括:
+autorun: 自动追踪依赖并执行reaction: 对特定数据变化作出反应when: 条件满足时执行一次观察父组件如何通过 props 向子组件传递数据,以及子组件如何通过事件向父组件通信
+理解 Redux 的单向数据流:Action → Reducer → Store → View
+{
+ type: "{{ currentAction.type }}",
+ payload: {{ currentAction.payload || 'undefined' }}
+}
+ function reducer(state, action) {
+ switch (action.type) {
+ case "{{ currentAction.type }}":
+ return {
+ ...state,
+ count: (state?.count ?? 0) {{ currentAction.operator }} {{ currentAction.step || 1 }}
+ };
+ default:
+ return state;
+ }
+}
+ {
+ count: {{ count }}
+}
+ 整个应用的 state 储存在唯一的 store 中
+唯一改变 state 的方法是触发 action
+Reducer 必须是纯函数,接收旧 state 返回新 state
+全面对比主流状态管理方案的特性、适用场景和学习曲线
+| 特性 | +
+
+
+ {{ lib.name }}
+
+ |
+
|---|---|
| 学习曲线 | ++ + {{ getCurveLabel(lib.learningCurve) }} + | +
| 包大小 | ++ {{ lib.bundleSize }} + | +
| TypeScript | ++ + {{ lib.typescript ? '✓' : '✗' }} + + | +
| 开发工具 | ++ + {{ lib.devtools ? '✓' : '✗' }} + + | +
| SSR 支持 | ++ + {{ lib.ssr ? '✓' : '✗' }} + + | +
| 适用框架 | ++ {{ lib.framework }} + | +
体验 Vue 生态两种主流状态管理方案在语法、类型支持和开发体验上的差异
+import { createStore } from 'vuex'
+
+export default createStore({
+ // State
+ state: {
+ count: 0,
+ user: null
+ },
+
+ // Getters
+ getters: {
+ doubleCount: state => {
+ return (state?.count ?? 0) * 2
+ },
+ isLoggedIn: state => !!(state?.user)
+ },
+
+ // Mutations (同步)
+ mutations: {
+ INCREMENT(state) {
+ state.count = (state?.count ?? 0) + 1
+ },
+ SET_USER(state, user) {
+ state.user = user
+ }
+ },
+
+ // Actions (可异步)
+ actions: {
+ incrementAsync({ commit }) {
+ setTimeout(() => {
+ commit('INCREMENT')
+ }, 1000)
+ },
+ async fetchUser({ commit }, userId) {
+ const response =
+ await fetch(\`/api/users/\${userId}\`)
+ const user = await response.json()
+ commit('SET_USER', user)
+ }
+ }
+})
+ <template>
+ <div>
+ <p>Count: {{ count }}</p>
+ <p>Double: {{ doubleCount }}</p>
+ <button @click="increment">+</button>
+ <button @click="incrementAsync">+ (async)</button>
+ </div>
+</template>
+
+<script>
+import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
+
+export default {
+ computed: {
+ ...mapState(['count']),
+ ...mapGetters(['doubleCount'])
+ },
+ methods: {
+ ...mapMutations(['INCREMENT']),
+ ...mapActions(['incrementAsync']),
+
+ increment() {
+ this.INCREMENT()
+ }
+ }
+}
+</script>
+ import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+
+// 方式1: 组合式 API (推荐)
+export const useCounterStore = defineStore('counter', () => {
+ // State
+ const count = ref(0)
+ const user = ref(null)
+
+ // Getters
+ const doubleCount = computed(() => (count.value ?? 0) * 2)
+ const isLoggedIn = computed(() => !!user.value)
+
+ // Actions
+ function increment() {
+ count.value = (count.value ?? 0) + 1
+ }
+
+ async function incrementAsync() {
+ await new Promise(r => setTimeout(r, 1000))
+ increment()
+ }
+
+ async function fetchUser(userId) {
+ const response = await fetch(\`/api/users/\${userId}\`)
+ user.value = await response.json()
+ }
+
+ return {
+ count, user,
+ doubleCount, isLoggedIn,
+ increment, incrementAsync, fetchUser
+ }
+})
+
+// 方式2: 选项式 API
+export const useCounterStoreOld = defineStore('counter', {
+ state: () => ({
+ count: 0,
+ user: null
+ }),
+ getters: {
+ doubleCount: (state) => (state?.count ?? 0) * 2
+ },
+ actions: {
+ increment() {
+ this.count = (this?.count ?? 0) + 1
+ }
+ }
+})
+ <template>
+ <div>
+ <p>Count: {{ counter.count }}</p>
+ <p>Double: {{ counter.doubleCount }}</p>
+ <button @click="counter.increment()">+</button>
+ <button @click="counter.incrementAsync()">+ (async)</button>
+ </div>
+</template>
+
+<script setup>
+import { useCounterStore } from '@/stores/counter'
+
+// 直接获取 store 实例
+const counter = useCounterStore()
+
+// 或者直接解构(但会失去响应式!)
+// const { count, increment } = useCounterStore() // ❌ 错误
+
+// 正确解构方式:使用 storeToRefs
+// import { storeToRefs } from 'pinia'
+// const { count, doubleCount } = storeToRefs(counter)
+// const { increment } = counter
+</script>
+ 探索现代 React 生态中最简洁的状态管理方案,体验"钩子即状态"的开发模式
+{{ getZustandFileContent() }}
+ {{ apiResponse }}
+ import { atom } from 'jotai'
+import { atomFamily } from 'jotai/utils'
+
+// 基础原子
+const countAtom = atom(0)
+
+// 派生原子 - 自动追踪依赖
+const doubleAtom = atom((get) => get(countAtom) * 2)
+const isEvenAtom = atom((get) => get(countAtom) % 2 === 0)
+
+// 可写派生原子
+const countAndDoubleAtom = atom(
+ (get) => ({
+ count: get(countAtom),
+ double: get(doubleAtom)
+ }),
+ (get, set, newCount) => {
+ set(countAtom, newCount)
+ }
+)
+
+// 原子家族 - 动态创建原子
+const todoAtomFamily = atomFamily((id) =>
+ atom({ id, text: '', completed: false })
+)
+
+// 异步原子
+const userAtom = atom(null)
+const fetchUserAtom = atom(
+ (get) => get(userAtom),
+ async (get, set, userId) => {
+ const response = await fetch(\`/api/users/\${userId}\`)
+ const user = await response.json()
+ set(userAtom, user)
+ }
+)
+
+export {
+ countAtom,
+ doubleAtom,
+ isEvenAtom,
+ countAndDoubleAtom,
+ todoAtomFamily,
+ fetchUserAtom
+}
+ import asyncio
+
+async def fetch_data(url):
+ # await 挂起,让出 CPU
+ response = await aiohttp.get(url)
+ # I/O 完成后继续执行
+ return response.json()
+
+async def main():
+ # 并发执行
+ tasks = [fetch_data(url) for url in urls]
+ results = await asyncio.gather(*tasks)
+ {{ algorithmDescription }}
+拖动轮次,看看这三个数字是怎么一起变化的。
-+ 💡 一切正常:当前 Token 数 ({{ totalTokens }}) 未超过窗口限制。模型能完美回忆起所有对话细节。 +
++ ⚠️ 发生遗忘:Token 总量 ({{ totalTokens }}) 已超过窗口限制 ({{ windowLimit }})。 + 为了放入新对话,系统被迫丢弃了最早的 {{ forgottenRounds }} 轮历史记录。 +
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+{{ description }}
+
+ ✅ 启用 Hash:文件名包含内容哈希 ({{ selectedFile.hash }})。
+ 文件内容变化时,URL 会改变,浏览器会重新请求。
+ 适合配置 Cache-Control: immutable 长期缓存。
+
+ ⚠️ 无 Hash:文件名固定为 {{ selectedFile.baseName }}.{{ selectedFile.ext }}。
+ 更新文件后,需要手动刷新缓存或使用版本号查询参数。
+ 容易遇到"缓存不更新"的问题。
+
+ 💡 + 资源指纹的作用: + 通过给文件名添加内容哈希(如 main.a3f7b2c.js),可以实现 + 永久缓存策略。 + 只有文件内容变化时哈希才会改变,浏览器才会重新下载。 + 这样用户每次访问都能享受极速加载,同时又能及时获取最新代码。 +
+{{ stages[selectedStage].description }}
++ 💡 + 构建流水线的作用: + 就像工厂的生产线一样,代码也需要经过一系列"加工工序"才能变成用户可以访问的网站。 + 每个阶段都有特定的任务,确保最终产出的代码是优化过、无错误且性能良好的。 +
+| 对比维度 | ++ + + {{ tool.name }} + + | +
|---|---|
| + + {{ dim.name }} + | ++ + | +
{{ scenario.bestReason }}
+{{ scenario.altReason }}
++ 💡 + 选择建议: + {{ currentView === 'radar' ? '雷达图展示了各工具在多个维度的能力分布,面积越大代表综合能力越强。' : + currentView === 'table' ? '表格详细对比了各工具在每个维度的具体得分,方便精确对比。' : + '根据你的项目类型和团队情况,选择最适合的工具往往比选择"最好"的工具更重要。' }} +
+按需加载,提升首屏速度
+💡 点击上方模块可模拟按需加载
++ 💡 + 代码分割的核心思想: + 不是所有代码都需要在首屏加载。通过动态导入 `import()`, + 我们可以把非核心功能延迟到真正需要时再加载。 + 这就像餐厅的点餐制——不是把所有菜一次性端上来,而是按需上菜。 +
++ 💡 + 依赖图谱的作用: + 就像地图一样,依赖图谱帮助你理解项目中的模块是如何相互关联的。 + 你可以快速找到某个模块被哪些地方引用,或者发现循环依赖等问题。 + 在大型项目中,良好的依赖结构是维护性的关键。 +
+修改代码无需刷新页面,即时生效
+| 构建工具 | +HMR 支持 | +更新速度 | +特点 | +
|---|---|---|---|
| {{ tool.name }} | ++ {{ tool.support }} + | +{{ tool.speed }} | +{{ tool.feature }} | +
+ 💡 + HMR 的核心原理: + 构建工具通过 WebSocket 与浏览器保持连接。当文件修改后,工具编译变更模块,通过 WebSocket 通知浏览器。 + 浏览器中的 HMR Runtime 接收更新,替换旧模块,同时保持应用状态不变。 + 这就像是给飞行中的飞机换引擎——不停机就能完成更新。 +
+调试压缩代码的秘密武器
+function calculateSum(a, b) {
+ // 计算两个数的和
+ const result = a + b;
+ console.log('结果:', result);
+ return result;
+}
+
+const sum = calculateSum(10, 20);
+console.log('总和:', sum);
+ function n(n,r){var t=n+r;return console.log("结果:",t),t}var r=n(10,20);console.log("总和:",r);
+//# sourceMappingURL=app.js.map
+ {
+ "version": 3,
+ "sources": ["src/utils.js", "src/main.js"],
+ "names": ["calculateSum", "a", "b", "result"],
+ "mappings": "AAAA,SAASA...",
+ "file": "app.min.js"
+}
+ 开启 SourceMap,方便调试
+不部署 .map 文件,防止源码泄露
+使用 `sourceMappingURL` 指向独立服务器
++ 💡 + SourceMap 工作原理: + 压缩代码时,构建工具会记录每个字符在源代码中的位置,生成 .map 文件。 + 浏览器调试时,通过映射关系把压缩后的代码"还原"成源代码显示。 + 注意:生产环境不要暴露 .map 文件,防止源码泄露! +
+选择你需要的功能,观察包体积变化
++ 💡 + Tree Shaking 原理: + 现代打包工具会分析 ES 模块的导出/导入关系,自动移除未被使用的代码。 + 前提条件:1) 使用 ES 模块 (import/export);2) 代码无副作用;3) 打包工具支持(Webpack、Rollup 等) +
+想象一下你在餐厅当服务员,有两种工作方式,你会选哪种?
+
+ 老张开了家餐厅,每天要点菜、做菜、算账。有两种记账方式:
+ 传统方式:老张手工记(jQuery 模式) vs 智能方式:请个管家(Vue/React 模式)
+ 看看哪种更轻松?
+
// 需要手动更新每个元素
-function updateCounter(change) {
- var count = parseInt($('#counter').text());
- var newCount = count + change;
-
- // 更新计数显示
- $('#counter').text(newCount);
-
- // 更新进度条
- var progress = (newCount / 10) * 100;
- $('#progress').css('width', progress + '%');
-
- // 更新状态文字
- if (newCount > 5) {
- $('#status').text('高!').addClass('warning');
- } else {
- $('#status').text('正常').removeClass('warning');
- }
-
- // 如果忘了更新某个地方...
- // 界面就会不一致!😱
-}
- // 只需要定义数据和规则
-data() {
- return {
- count: 0
- }
-},
-computed: {
- // 进度自动计算
- progress() {
- return (this.count / 10) * 100;
- },
- // 状态自动判断
- status() {
- return this.count > 5 ? '高!' : '正常';
- },
- isWarning() {
- return this.count > 5;
- }
-}
-
-// 模板里只需要声明关系
-<template>
- <div class="status" :class="{ warning: isWarning }">
- {{ status }}
- </div>
-</template>
- 想象你有一张照片,它会自动调整大小和布局,在任何相框里都好看!
+
+ 小美有一件神奇的魔法衣柜!不管你把它放在大房间还是小房间,
+ 里面的衣服都会自动叠好、排好,完美适应空间大小!
+