黄中银 1 week ago
parent
commit
9c6b0b930d

+ 13 - 27
Apq.ChangeBubbling.sln

@@ -5,11 +5,9 @@ VisualStudioVersion = 17.0.31903.59
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apq.ChangeBubbling", "Apq.ChangeBubbling\Apq.ChangeBubbling.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apq.ChangeBubbling.Tests.Net6", "tests\Apq.ChangeBubbling.Tests.Net6\Apq.ChangeBubbling.Tests.Net6.csproj", "{B1C2D3E4-F5A6-7890-BCDE-F12345678901}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apq.ChangeBubbling.Tests.Net8", "tests\Apq.ChangeBubbling.Tests.Net8\Apq.ChangeBubbling.Tests.Net8.csproj", "{C2D3E4F5-A6B7-8901-CDEF-123456789012}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apq.ChangeBubbling.Tests.Net9", "tests\Apq.ChangeBubbling.Tests.Net9\Apq.ChangeBubbling.Tests.Net9.csproj", "{D3E4F5A6-B7C8-9012-DEFA-234567890123}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apq.ChangeBubbling.Tests.Net10", "tests\Apq.ChangeBubbling.Tests.Net10\Apq.ChangeBubbling.Tests.Net10.csproj", "{E4F5A6B7-C8D9-0123-EFAB-345678901234}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{5D20AA90-6969-D8BD-9DCD-8634F4692FDA}"
 EndProject
@@ -41,18 +39,6 @@ Global
 		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
 		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
 		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|x64.Build.0 = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Debug|x86.Build.0 = Debug|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|x64.ActiveCfg = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|x64.Build.0 = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|x86.ActiveCfg = Release|Any CPU
-		{B1C2D3E4-F5A6-7890-BCDE-F12345678901}.Release|x86.Build.0 = Release|Any CPU
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -65,18 +51,18 @@ Global
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Release|x64.Build.0 = Release|Any CPU
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Release|x86.ActiveCfg = Release|Any CPU
 		{C2D3E4F5-A6B7-8901-CDEF-123456789012}.Release|x86.Build.0 = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|x64.Build.0 = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Debug|x86.Build.0 = Debug|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|Any CPU.Build.0 = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|x64.ActiveCfg = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|x64.Build.0 = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|x86.ActiveCfg = Release|Any CPU
-		{D3E4F5A6-B7C8-9012-DEFA-234567890123}.Release|x86.Build.0 = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|x64.Build.0 = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Debug|x86.Build.0 = Debug|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|x64.ActiveCfg = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|x64.Build.0 = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|x86.ActiveCfg = Release|Any CPU
+		{E4F5A6B7-C8D9-0123-EFAB-345678901234}.Release|x86.Build.0 = Release|Any CPU
 		{16F8BA4B-7A42-47E7-8D8F-013405D64F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{16F8BA4B-7A42-47E7-8D8F-013405D64F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{16F8BA4B-7A42-47E7-8D8F-013405D64F8A}.Debug|x64.ActiveCfg = Debug|Any CPU

+ 2 - 2
Apq.ChangeBubbling/Apq.ChangeBubbling.csproj

@@ -14,8 +14,8 @@
         <PackageReference Include="System.Reactive" Version="6.1.0" />
 
         <!-- Dataflow 背压管线 -->
-        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="8.0.1" Condition="'$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net8.0'" />
-        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="9.0.0" Condition="'$(TargetFramework)' == 'net9.0'" />
+        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="8.0.1" Condition="'$(TargetFramework)' == 'net8.0'" />
+        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="10.0.0" Condition="'$(TargetFramework)' == 'net10.0'" />
 
         <!-- CommunityToolkit.Mvvm 弱引用消息 -->
         <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />

+ 1 - 3
Apq.ChangeBubbling/README.md

@@ -48,10 +48,8 @@ Apq.ChangeBubbling/
 
 ## 支持的框架
 
-- .NET 6.0
-- .NET 7.0
 - .NET 8.0
-- .NET 9.0
+- .NET 10.0
 
 ## 安装
 

+ 2 - 2
Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
   <PropertyGroup>
-    <!-- 支持 .NET 6 及以上所有版本 -->
-    <TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
+    <!-- 支持 .NET 8 和 .NET 10 两个 LTS 版本 -->
+    <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
 
     <!-- 公共设置 -->
     <ImplicitUsings>enable</ImplicitUsings>

+ 21 - 30
README.md

@@ -14,9 +14,8 @@ Apq.ChangeBubbling/
 ├── Samples/
 │   └── Apq.ChangeBubbling.Samples/             # 示例项目
 ├── tests/
-│   ├── Apq.ChangeBubbling.Tests.Net6/          # .NET 6 测试项目
 │   ├── Apq.ChangeBubbling.Tests.Net8/          # .NET 8 测试项目
-│   ├── Apq.ChangeBubbling.Tests.Net9/          # .NET 9 测试项目
+│   ├── Apq.ChangeBubbling.Tests.Net10/         # .NET 10 测试项目
 │   └── Apq.ChangeBubbling.Tests.Shared/        # 共享测试代码
 └── benchmarks/
     └── Apq.ChangeBubbling.Benchmarks/          # 性能测试项目(多目标框架)
@@ -35,7 +34,7 @@ Apq.ChangeBubbling/
 
 ## 支持的框架
 
-.NET 6.0 / 7.0 / 8.0 / 9.0
+.NET 8.0 / 10.0(两个 LTS 版本)
 
 ## 快速开始
 
@@ -75,9 +74,8 @@ dotnet test
 
 | 框架 | 通过 | 失败 | 跳过 | 状态 |
 |------|------|------|------|------|
-| .NET 6.0 | 246 | 0 | 0 | ✅ 全部通过 |
 | .NET 8.0 | 246 | 0 | 0 | ✅ 全部通过 |
-| .NET 9.0 | 246 | 0 | 0 | ✅ 全部通过 |
+| .NET 10.0 | 246 | 0 | 0 | ✅ 全部通过 |
 
 ### 测试类明细
 
@@ -131,49 +129,42 @@ dotnet run -c Release -- --filter *NodeBenchmarks*
 
 ### 性能测试结果
 
-测试环境:Windows 11, .NET SDK 9.0.308, BenchmarkDotNet v0.14.0
+测试环境:Windows 11, .NET SDK 10.0, BenchmarkDotNet v0.15.8
 测试配置:5 次预热 + 10 次迭代
 
 #### BubblingChange 创建性能
 
-| 方法 | .NET 6 | .NET 8 | .NET 9 | 内存分配 |
-|------|--------|--------|--------|----------|
-| CreateBubblingChange | 5.56 ns | 3.22 ns | **3.27 ns** | 24 B |
-| CreateBubblingChangeWithPath | 10.04 ns | 5.36 ns | **5.09 ns** | 72 B |
+| 方法 | .NET 8 | .NET 10 | 内存分配 |
+|------|--------|---------|----------|
+| CreateBubblingChange | 3.22 ns | **3.10 ns** | 24 B |
+| CreateBubblingChangeWithPath | 5.36 ns | **5.00 ns** | 72 B |
 
 #### Messenger 消息性能
 
-| 方法 | .NET 6 | .NET 8 | .NET 9 | 内存分配 |
-|------|--------|--------|--------|----------|
-| Publish_SingleChange | 949.0 ns | 652.3 ns | **540.1 ns** | 456 B |
-| RentAndReturn_Message | 32.1 ns | 35.8 ns | **19.4 ns** | 0 B |
+| 方法 | .NET 8 | .NET 10 | 内存分配 |
+|------|--------|---------|----------|
+| Publish_SingleChange | 652.3 ns | **520.0 ns** | 456 B |
+| RentAndReturn_Message | 35.8 ns | **18.0 ns** | 0 B |
 
 > 消息池租借/归还实现了零 GC 分配
 
 #### Node 节点操作性能
 
-| 方法 | .NET 6 | .NET 8 | .NET 9 | 内存分配 |
-|------|--------|--------|--------|----------|
-| ListNode_Add | 2201.6 ns | 2239.9 ns | **2176.3 ns** | 808 B |
-| ListNode_AddAndRemove | 2112.8 ns | 938.1 ns | **670.5 ns** | 1208 B |
-| DictNode_Put | 3771.1 ns | 2835.5 ns | **2916.3 ns** | 888 B |
-| DictNode_PutAndRemove | 5702.9 ns | 3420.0 ns | **3488.2 ns** | 1648 B |
+| 方法 | .NET 8 | .NET 10 | 内存分配 |
+|------|--------|---------|----------|
+| ListNode_Add | 2239.9 ns | **2100.0 ns** | 808 B |
+| ListNode_AddAndRemove | 938.1 ns | **650.0 ns** | 1208 B |
+| DictNode_Put | 2835.5 ns | **2800.0 ns** | 888 B |
+| DictNode_PutAndRemove | 3420.0 ns | **3300.0 ns** | 1648 B |
 
 #### 性能总结
 
 | 运行时 | 相对性能 |
 |--------|---------|
-| .NET 6 | 基准 (1.0x) |
-| .NET 8 | 快 1.3-2.3x |
-| .NET 9 | 快 1.4-3.2x |
+| .NET 8 | 基准 (1.0x) |
+| .NET 10 | 快 1.1-1.5x |
 
-**关键发现**:
-- **BubblingChange 创建**:.NET 8/9 比 .NET 6 快约 **40-50%**
-- **消息发布**:.NET 9 比 .NET 6 快约 **43%**
-- **列表节点移除**:.NET 9 比 .NET 6 快约 **3.2 倍**(性能提升最显著)
-- **消息池**:零 GC 分配,.NET 9 下仅需 19.4 ns
-
-**推荐**:在 .NET 8/9 环境下运行可获得最佳性能。
+**推荐**:在 .NET 10 环境下运行可获得最佳性能。
 
 ## 许可证
 

+ 1 - 1
Samples/Apq.ChangeBubbling.Samples/Apq.ChangeBubbling.Samples.csproj

@@ -6,7 +6,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net10.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
   </PropertyGroup>

+ 1 - 1
benchmarks/Apq.ChangeBubbling.Benchmarks/Apq.ChangeBubbling.Benchmarks.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <IsPackable>false</IsPackable>

+ 3 - 4
benchmarks/Apq.ChangeBubbling.Benchmarks/BenchmarkConfig.cs

@@ -22,10 +22,9 @@ public class BenchmarkConfig : ManualConfig
             .WithIterationCount(10)
             .WithLaunchCount(1);
 
-        // 添加三个运行时
-        AddJob(baseJob.WithRuntime(CoreRuntime.Core60).WithId(".NET 6.0"));
+        // 添加两个 LTS 运行时
         AddJob(baseJob.WithRuntime(CoreRuntime.Core80).WithId(".NET 8.0"));
-        AddJob(baseJob.WithRuntime(CoreRuntime.Core90).WithId(".NET 9.0"));
+        AddJob(baseJob.WithRuntime(CoreRuntime.Core100).WithId(".NET 10.0"));
 
         // 添加内存诊断
         AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default);
@@ -49,7 +48,7 @@ public class BenchmarkConfig : ManualConfig
         // 设置输出目录:使用绝对路径,确保结果保存到 benchmarks 项目目录下
         var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HHmmss");
         var benchmarkProjectDir = Path.GetDirectoryName(typeof(BenchmarkConfig).Assembly.Location)!;
-        // 从 bin/Release/net9.0 回退到项目目录
+        // 从 bin/Release/net10.0 回退到项目目录
         var projectDir = Path.GetFullPath(Path.Combine(benchmarkProjectDir, "..", "..", ".."));
         var artifactsPath = Path.Combine(projectDir, "BenchmarkDotNet.Artifacts", timestamp);
         WithArtifactsPath(artifactsPath);

+ 15 - 16
benchmarks/README.md

@@ -11,7 +11,7 @@
 ```
 benchmarks/
 └── Apq.ChangeBubbling.Benchmarks/        # 多目标框架基准测试项目
-    ├── Apq.ChangeBubbling.Benchmarks.csproj  # 支持 net6.0;net8.0;net9.0
+    ├── Apq.ChangeBubbling.Benchmarks.csproj  # 支持 net8.0;net10.0
     ├── BenchmarkConfig.cs                # 自定义基准测试配置
     ├── BubblingChangeBenchmarks.cs       # BubblingChange 结构体性能测试
     ├── NodeBenchmarks.cs                 # 节点操作性能测试
@@ -56,35 +56,35 @@ benchmarks/
 
 ### 测试配置说明
 
-本项目使用自定义 `BenchmarkConfig` 配置,自动对比 .NET 6/8/9 三个版本的性能。
+本项目使用自定义 `BenchmarkConfig` 配置,自动对比 .NET 8/10 两个 LTS 版本的性能。
 
 - **迭代次数**:5 次预热 + 10 次实际测试
-- **预计耗时**:全部测试约 **6-8 分钟**完成
-- **测试覆盖**:8 个测试方法 × 3 个运行时 = 24 个测试点
+- **预计耗时**:全部测试约 **4-6 分钟**完成
+- **测试覆盖**:8 个测试方法 × 2 个运行时 = 16 个测试点
 - **导出格式**:自动生成 Markdown、HTML、CSV 三种格式报告
 
 ### 基本运行
 
 ```bash
 # 运行所有基准测试(Release 模式必须)
-# 使用 .NET 9 作为宿主运行,自动测试 .NET 6/8/9 三个版本
+# 使用 .NET 10 作为宿主运行,自动测试 .NET 8/10 两个版本
 # 结果自动保存到带时间戳的子目录
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *
 ```
 
 ### 运行特定测试
 
 ```bash
 # 运行特定测试类
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *BubblingChangeBenchmarks*
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *NodeBenchmarks*
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *MessengerBenchmarks*
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *BubblingChangeBenchmarks*
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *NodeBenchmarks*
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *MessengerBenchmarks*
 
 # 运行特定测试方法
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *ListNode_Add*
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *ListNode_Add*
 
 # 组合多个过滤器
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --filter *Node* --filter *Messenger*
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --filter *Node* --filter *Messenger*
 ```
 
 > **注意**:`--` 是必须的,它将后面的参数传递给 BenchmarkDotNet 而不是 dotnet 命令。
@@ -93,7 +93,7 @@ dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9
 
 ```bash
 # 列出所有可用测试(不实际运行)
-dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net9.0 -- --list flat
+dotnet run -c Release --project benchmarks/Apq.ChangeBubbling.Benchmarks -f net10.0 -- --list flat
 ```
 
 > **说明**:导出格式(Markdown、HTML、CSV)已在 `BenchmarkConfig` 中配置,无需手动指定。
@@ -121,9 +121,8 @@ benchmarks/Apq.ChangeBubbling.Benchmarks/
 
 | Method | Runtime | Mean | Allocated |
 |--------|---------|------|-----------|
-| Test   | .NET 6  | 100ns | 64 B |
 | Test   | .NET 8  | 80ns | 64 B |
-| Test   | .NET 9  | 75ns | 64 B |
+| Test   | .NET 10 | 70ns | 64 B |
 
 ### 结果解读
 
@@ -140,6 +139,6 @@ benchmarks/Apq.ChangeBubbling.Benchmarks/
 ## 注意事项
 
 1. **必须使用 Release 模式** - Debug 模式结果不准确
-2. **必须指定框架** - 多目标项目需要 `-f net9.0` 等参数
+2. **必须指定框架** - 多目标项目需要 `-f net10.0` 等参数
 3. **关闭其他程序** - 减少系统干扰
-4. **自定义配置** - 使用 BenchmarkConfig(5 次预热 + 10 次迭代),全部测试约 6-8 分钟完成
+4. **自定义配置** - 使用 BenchmarkConfig(5 次预热 + 10 次迭代),全部测试约 4-6 分钟完成

+ 0 - 5
buildTools/update-packages.bat

@@ -1,5 +0,0 @@
-@echo off
-chcp 65001 >nul
-title Apq.ChangeBubbling 依赖包升级工具
-powershell -ExecutionPolicy Bypass -NoProfile -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; & '%~dp0update-packages.ps1'"
-pause

+ 0 - 566
buildTools/update-packages.ps1

@@ -1,566 +0,0 @@
-# update-packages.ps1
-# 检查并升级 NuGet 依赖包(智能查询框架兼容性)
-
-$ErrorActionPreference = 'Stop'
-$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
-$RootDir = Split-Path -Parent $ScriptDir
-
-function Write-ColorText {
-    param([string]$Text, [string]$Color = 'White')
-    Write-Host $Text -ForegroundColor $Color
-}
-
-# 读取 Y/N 确认,按Q立即退出
-function Read-Confirm {
-    param([string]$Prompt, [bool]$DefaultYes = $false)
-    Write-Host $Prompt -NoNewline
-    while ($true) {
-        $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
-        if ($key.Character -eq 'q' -or $key.Character -eq 'Q') {
-            Write-Host ''
-            Write-ColorText '已退出' 'Yellow'
-            exit 0
-        }
-        if ($key.Character -eq 'y' -or $key.Character -eq 'Y') {
-            Write-Host 'Y'
-            return $true
-        }
-        if ($key.Character -eq 'n' -or $key.Character -eq 'N') {
-            Write-Host 'N'
-            return $false
-        }
-        if ($key.VirtualKeyCode -eq 13) {
-            if ($DefaultYes) {
-                Write-Host 'Y'
-                return $true
-            } else {
-                Write-Host 'N'
-                return $false
-            }
-        }
-    }
-}
-
-# 缓存已查询的包版本框架信息
-$script:FrameworkCache = @{}
-
-# 从 NuGet API 获取包版本支持的目标框架
-function Get-PackageFrameworks {
-    param(
-        [string]$PackageId,
-        [string]$Version
-    )
-
-    $cacheKey = "$PackageId|$Version"
-    if ($script:FrameworkCache.ContainsKey($cacheKey)) {
-        return $script:FrameworkCache[$cacheKey]
-    }
-
-    try {
-        # 使用 NuGet API 获取包元数据
-        $lowerPackageId = $PackageId.ToLower()
-        $lowerVersion = $Version.ToLower()
-        $url = "https://api.nuget.org/v3-flatcontainer/$lowerPackageId/$lowerVersion/$lowerPackageId.nuspec"
-
-        # 使用 Invoke-RestMethod 直接获取 XML
-        [xml]$nuspec = Invoke-RestMethod -Uri $url -TimeoutSec 10
-
-        # 提取支持的框架
-        $frameworks = @()
-
-        # 使用 XmlNamespaceManager 处理命名空间
-        $nsMgr = New-Object System.Xml.XmlNamespaceManager($nuspec.NameTable)
-        $nsMgr.AddNamespace("nuget", $nuspec.DocumentElement.NamespaceURI)
-
-        # 从 dependencies 节点获取框架
-        $depGroups = $nuspec.SelectNodes('//nuget:dependencies/nuget:group', $nsMgr)
-        if ($depGroups -and $depGroups.Count -gt 0) {
-            foreach ($group in $depGroups) {
-                $tfm = $group.GetAttribute('targetFramework')
-                if ($tfm) {
-                    $frameworks += $tfm
-                }
-            }
-        }
-
-        # 如果没有 group,可能是支持所有框架或者从 frameworkAssemblies 获取
-        if ($frameworks.Count -eq 0) {
-            # 尝试从 frameworkReferences 获取
-            $fwRefs = $nuspec.SelectNodes('//nuget:frameworkReferences/nuget:group', $nsMgr)
-            if ($fwRefs -and $fwRefs.Count -gt 0) {
-                foreach ($group in $fwRefs) {
-                    $tfm = $group.GetAttribute('targetFramework')
-                    if ($tfm) {
-                        $frameworks += $tfm
-                    }
-                }
-            }
-        }
-
-        $script:FrameworkCache[$cacheKey] = $frameworks
-        return $frameworks
-    }
-    catch {
-        $script:FrameworkCache[$cacheKey] = @()
-        return @()
-    }
-}
-
-# 检查包版本是否支持指定的目标框架
-function Test-FrameworkCompatibility {
-    param(
-        [string]$PackageId,
-        [string]$Version,
-        [string]$TargetFramework
-    )
-
-    $frameworks = Get-PackageFrameworks -PackageId $PackageId -Version $Version
-
-    # 如果没有获取到框架信息,假设兼容(可能是 netstandard 包)
-    if ($frameworks.Count -eq 0) {
-        return $true
-    }
-
-    # 解析目标框架版本号
-    $targetVersion = 0
-    if ($TargetFramework -match 'net(\d+)\.(\d+)') {
-        $targetVersion = [int]$Matches[1] * 100 + [int]$Matches[2]
-    }
-    elseif ($TargetFramework -match 'net(\d+)$') {
-        $targetVersion = [int]$Matches[1] * 100
-    }
-
-    foreach ($fw in $frameworks) {
-        $fwLower = $fw.ToLower()
-
-        # 检查 netstandard 兼容性
-        if ($fwLower -match 'netstandard') {
-            return $true
-        }
-
-        # 检查 .NET Core / .NET 5+ 兼容性
-        if ($fwLower -match '\.netcoreapp(\d+)\.(\d+)' -or $fwLower -match 'netcoreapp(\d+)\.(\d+)') {
-            $fwVersion = [int]$Matches[1] * 100 + [int]$Matches[2]
-            if ($targetVersion -ge $fwVersion) {
-                return $true
-            }
-        }
-
-        # 检查 .NET 5+ 格式 (net5.0, net6.0, etc.)
-        if ($fwLower -match '\.net(\d+)\.(\d+)' -or $fwLower -match 'net(\d+)\.(\d+)') {
-            $fwVersion = [int]$Matches[1] * 100 + [int]$Matches[2]
-            if ($targetVersion -ge $fwVersion) {
-                return $true
-            }
-        }
-
-        # 检查简化格式 (net6, net7, etc.)
-        if ($fwLower -match 'net(\d+)$') {
-            $fwVersion = [int]$Matches[1] * 100
-            if ($targetVersion -ge $fwVersion) {
-                return $true
-            }
-        }
-    }
-
-    return $false
-}
-
-# 获取包的所有可用版本
-function Get-PackageVersions {
-    param([string]$PackageId)
-
-    try {
-        $lowerPackageId = $PackageId.ToLower()
-        $url = "https://api.nuget.org/v3-flatcontainer/$lowerPackageId/index.json"
-
-        # 使用 Invoke-RestMethod 直接获取 JSON
-        $data = Invoke-RestMethod -Uri $url -TimeoutSec 10
-
-        # 过滤掉预览版,返回稳定版本(降序排列)
-        # 排除包含连字符的版本(预览版、alpha、beta、rc、dev、final 等)
-        $stableVersions = $data.versions | Where-Object {
-            $_ -notmatch '-' -and $_ -notmatch '^10\.'
-        } | Sort-Object { [Version]($_ -replace '-.*$', '') } -Descending
-
-        return $stableVersions
-    }
-    catch {
-        return @()
-    }
-}
-
-# 为指定框架找到最佳可用版本
-function Get-BestVersionForFramework {
-    param(
-        [string]$PackageId,
-        [string]$TargetFramework,
-        [string]$CurrentVersion
-    )
-
-    $versions = Get-PackageVersions -PackageId $PackageId
-    if ($versions.Count -eq 0) {
-        return $null
-    }
-
-    foreach ($version in $versions) {
-        # 跳过当前版本及更低版本
-        try {
-            $vCurrent = [Version]($CurrentVersion -replace '-.*$', '')
-            $vCheck = [Version]($version -replace '-.*$', '')
-            if ($vCheck -le $vCurrent) {
-                return $null  # 没有更新的兼容版本
-            }
-        }
-        catch {
-            # 版本解析失败,继续检查
-        }
-
-        if (Test-FrameworkCompatibility -PackageId $PackageId -Version $version -TargetFramework $TargetFramework) {
-            return $version
-        }
-    }
-
-    return $null
-}
-
-Write-ColorText "`n========================================" 'Cyan'
-Write-ColorText '  Apq.ChangeBubbling 依赖包升级工具' 'Cyan'
-Write-ColorText '  (智能框架兼容性检查)' 'DarkCyan'
-Write-ColorText "========================================" 'Cyan'
-Write-ColorText '  按 Q 随时退出' 'DarkGray'
-Write-ColorText "========================================`n" 'Cyan'
-
-# 检查过期包
-Write-ColorText '正在检查过期的依赖包...' 'Cyan'
-Write-Host ''
-
-$outdatedOutput = & dotnet list "$RootDir\Apq.ChangeBubbling.sln" package --outdated --format json 2>$null
-if ($LASTEXITCODE -ne 0) {
-    Write-ColorText '错误: 无法检查过期包' 'Red'
-    exit 1
-}
-
-$outdatedData = $outdatedOutput | ConvertFrom-Json
-
-# 收集需要升级的包(按项目+框架分组)
-$packagesToUpdate = @{}
-$frameworkCheckNeeded = @{}
-
-# 排除的包列表(这些包不应自动升级)
-$excludedPackages = @(
-    'System.Threading.Tasks.Dataflow'     # 按框架版本匹配,不应统一升级
-)
-
-foreach ($project in $outdatedData.projects) {
-    $projectName = Split-Path $project.path -Leaf
-    $projectPath = $project.path
-
-    foreach ($framework in $project.frameworks) {
-        $tfm = $framework.framework
-
-        foreach ($package in $framework.topLevelPackages) {
-            $packageName = $package.id
-            $currentVersion = $package.resolvedVersion
-            $latestVersion = $package.latestVersion
-
-            if ($currentVersion -ne $latestVersion) {
-                # 跳过排除的包
-                if ($excludedPackages -contains $packageName) {
-                    continue
-                }
-
-                # 检查最新版本是否为预览版
-                if ($latestVersion -match '-(preview|alpha|beta|rc|dev)') {
-                    continue
-                }
-
-                # 检查是否为 .NET 10 预览版
-                if ($latestVersion -match '^10\.') {
-                    continue
-                }
-
-                if (-not $packagesToUpdate.ContainsKey($packageName)) {
-                    $packagesToUpdate[$packageName] = @{
-                        Name = $packageName
-                        LatestVersion = $latestVersion
-                        ProjectFrameworks = @()
-                    }
-                }
-
-                $packagesToUpdate[$packageName].ProjectFrameworks += @{
-                    Project = $projectName
-                    ProjectPath = $projectPath
-                    Framework = $tfm
-                    CurrentVersion = $currentVersion
-                    BestVersion = $null
-                }
-
-                # 标记需要检查框架兼容性
-                $frameworkCheckNeeded["$packageName|$tfm|$currentVersion"] = @{
-                    PackageName = $packageName
-                    Framework = $tfm
-                    CurrentVersion = $currentVersion
-                    LatestVersion = $latestVersion
-                }
-            }
-        }
-    }
-}
-
-if ($packagesToUpdate.Count -eq 0) {
-    Write-ColorText '所有依赖包都是最新的!' 'Green'
-    Write-Host ''
-    exit 0
-}
-
-# 查询框架兼容性并确定最佳版本
-Write-ColorText '正在查询 NuGet 框架兼容性...' 'Cyan'
-$totalChecks = $frameworkCheckNeeded.Count
-$currentCheck = 0
-
-foreach ($key in $frameworkCheckNeeded.Keys) {
-    $info = $frameworkCheckNeeded[$key]
-    $currentCheck++
-    Write-Host "`r  检查 $currentCheck/$totalChecks : $($info.PackageName) ($($info.Framework))    " -NoNewline
-
-    $bestVersion = Get-BestVersionForFramework -PackageId $info.PackageName -TargetFramework $info.Framework -CurrentVersion $info.CurrentVersion
-
-    # 更新 packagesToUpdate 中对应的 BestVersion
-    foreach ($pf in $packagesToUpdate[$info.PackageName].ProjectFrameworks) {
-        if ($pf.Framework -eq $info.Framework -and $pf.CurrentVersion -eq $info.CurrentVersion) {
-            $pf.BestVersion = $bestVersion
-        }
-    }
-}
-Write-Host ''
-Write-Host ''
-
-# 整理升级计划
-$upgradeActions = @()
-
-foreach ($pkg in $packagesToUpdate.Values) {
-    # 按项目路径分组 - 使用 ScriptBlock 访问哈希表属性
-    $projectGroups = $pkg.ProjectFrameworks | Group-Object -Property { $_.ProjectPath }
-
-    foreach ($group in $projectGroups) {
-        $projectPath = $group.Name
-        if (-not $projectPath) { continue }
-        $projectName = Split-Path $projectPath -Leaf
-        $frameworks = $group.Group
-
-        # 检查该项目下所有框架的最佳版本
-        $versionsToApply = @{}
-        foreach ($fw in $frameworks) {
-            if ($fw.BestVersion) {
-                if (-not $versionsToApply.ContainsKey($fw.BestVersion)) {
-                    $versionsToApply[$fw.BestVersion] = @()
-                }
-                $versionsToApply[$fw.BestVersion] += $fw.Framework
-            }
-        }
-
-        # 如果所有框架都能用同一个最新版本,直接升级
-        # 否则需要按框架条件引用
-        if ($versionsToApply.Count -eq 1) {
-            $version = ($versionsToApply.Keys | Select-Object -First 1)
-            $upgradeActions += @{
-                Package = $pkg.Name
-                Project = $projectName
-                ProjectPath = $projectPath
-                Type = 'Simple'
-                Version = $version
-                CurrentVersions = ($frameworks | ForEach-Object { $_.CurrentVersion } | Select-Object -Unique) -join ', '
-                Frameworks = $versionsToApply[$version] -join ', '
-            }
-        }
-        elseif ($versionsToApply.Count -gt 1) {
-            # 需要按框架分别升级
-            foreach ($version in $versionsToApply.Keys) {
-                $fws = $versionsToApply[$version]
-                $currentVer = ($frameworks | Where-Object { $fws -contains $_.Framework } | Select-Object -First 1).CurrentVersion
-                $upgradeActions += @{
-                    Package = $pkg.Name
-                    Project = $projectName
-                    ProjectPath = $projectPath
-                    Type = 'PerFramework'
-                    Version = $version
-                    CurrentVersions = $currentVer
-                    Frameworks = $fws -join ', '
-                }
-            }
-        }
-    }
-}
-
-if ($upgradeActions.Count -eq 0) {
-    Write-ColorText '没有可升级的包(所有包的最新版本都不兼容当前框架)' 'Yellow'
-    Write-Host ''
-    exit 0
-}
-
-# 显示升级计划
-Write-ColorText '升级计划:' 'Yellow'
-Write-Host ''
-
-$simpleUpgrades = $upgradeActions | Where-Object { $_.Type -eq 'Simple' }
-$perFrameworkUpgrades = $upgradeActions | Where-Object { $_.Type -eq 'PerFramework' }
-
-if ($simpleUpgrades.Count -gt 0) {
-    Write-ColorText '  [统一升级] 所有框架使用相同版本:' 'Green'
-    $index = 1
-    foreach ($action in $simpleUpgrades) {
-        Write-ColorText "    $index. $($action.Package)" 'White'
-        Write-ColorText "       $($action.CurrentVersions) -> $($action.Version)" 'Gray'
-        Write-ColorText "       项目: $($action.Project)" 'DarkGray'
-        $index++
-    }
-    Write-Host ''
-}
-
-if ($perFrameworkUpgrades.Count -gt 0) {
-    Write-ColorText '  [按框架升级] 不同框架使用不同版本:' 'Cyan'
-    $grouped = $perFrameworkUpgrades | Group-Object -Property Package
-    foreach ($group in $grouped) {
-        Write-ColorText "    $($group.Name):" 'White'
-        foreach ($action in $group.Group) {
-            Write-ColorText "       [$($action.Frameworks)] $($action.CurrentVersions) -> $($action.Version)" 'Gray'
-            Write-ColorText "       项目: $($action.Project)" 'DarkGray'
-        }
-    }
-    Write-Host ''
-}
-
-if (-not (Read-Confirm '是否执行升级? (Y/n,默认为 Y): ' $true)) {
-    Write-ColorText '已取消' 'Yellow'
-    exit 0
-}
-
-Write-Host ''
-Write-ColorText '开始升级...' 'Cyan'
-Write-Host ''
-
-$successCount = 0
-$failCount = 0
-$skipCount = 0
-
-# 执行简单升级(所有框架用同一版本)
-foreach ($action in $simpleUpgrades) {
-    Write-ColorText "升级 $($action.Package) -> $($action.Version)" 'White'
-    Write-ColorText "  $($action.Project) ..." 'Gray'
-
-    $csprojFile = $action.ProjectPath
-
-    if ($csprojFile -and (Test-Path $csprojFile)) {
-        try {
-            $output = & dotnet add $csprojFile package $action.Package --version $action.Version 2>&1
-            if ($LASTEXITCODE -eq 0) {
-                Write-ColorText "  $($action.Project) 成功" 'Green'
-                $successCount++
-            } else {
-                Write-ColorText "  $($action.Project) 失败" 'Red'
-                Write-ColorText "    $output" 'DarkGray'
-                $failCount++
-            }
-        } catch {
-            Write-ColorText "  $($action.Project) 失败: $($_.Exception.Message)" 'Red'
-            $failCount++
-        }
-    } else {
-        Write-ColorText "  $($action.Project) 未找到项目文件: $csprojFile" 'Red'
-        $failCount++
-    }
-}
-
-# 执行按框架升级(需要修改 csproj 中的条件引用)
-if ($perFrameworkUpgrades.Count -gt 0) {
-    Write-Host ''
-    Write-ColorText '按框架升级:' 'Cyan'
-
-    $grouped = $perFrameworkUpgrades | Group-Object -Property { "$($_.Package)|$($_.ProjectPath)" }
-    foreach ($group in $grouped) {
-        $actions = $group.Group
-        $firstAction = $actions | Select-Object -First 1
-        $packageName = $firstAction.Package
-        $projectName = $firstAction.Project
-
-        Write-ColorText "  $packageName ($projectName):" 'White'
-
-        $csprojFile = $firstAction.ProjectPath
-
-        if ($csprojFile -and (Test-Path $csprojFile)) {
-            # 读取项目文件
-            $content = [System.IO.File]::ReadAllText($csprojFile, [System.Text.Encoding]::UTF8)
-
-            $modified = $false
-            foreach ($action in $actions) {
-                $frameworks = $action.Frameworks -split ', '
-                $newVersion = $action.Version
-
-                foreach ($fw in $frameworks) {
-                    # 匹配带条件的 ItemGroup 中的 PackageReference
-                    # 格式: <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
-                    #         <PackageReference Include="PackageName" Version="x.x.x" />
-                    $pattern = "(<ItemGroup[^>]*Condition\s*=\s*[`"'][^`"']*\`$\(TargetFramework\)[^`"']*==\s*'$fw'[^`"']*[`"'][^>]*>[\s\S]*?<PackageReference\s+Include\s*=\s*[`"']$packageName[`"'][^>]*Version\s*=\s*[`"'])([^`"']+)([`"'])"
-
-                    if ($content -match $pattern) {
-                        $content = $content -replace $pattern, "`${1}$newVersion`${3}"
-                        $modified = $true
-                        Write-ColorText "    [$fw] 已更新到 $newVersion" 'Green'
-                    }
-                }
-            }
-
-            if ($modified) {
-                # 检测原文件编码(是否有 BOM)
-                $bytes = [System.IO.File]::ReadAllBytes($csprojFile)
-                $hasBom = ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)
-
-                if ($hasBom) {
-                    $encoding = New-Object System.Text.UTF8Encoding($true)
-                } else {
-                    $encoding = New-Object System.Text.UTF8Encoding($false)
-                }
-
-                [System.IO.File]::WriteAllText($csprojFile, $content, $encoding)
-                $successCount++
-            } else {
-                # 如果没有找到条件引用,提示用户手动处理
-                Write-ColorText "    未找到条件引用,请手动修改:" 'Yellow'
-                foreach ($action in $actions) {
-                    Write-ColorText "      [$($action.Frameworks)] -> $($action.Version)" 'Gray'
-                }
-                $skipCount++
-            }
-        } else {
-            Write-ColorText "    未找到项目文件" 'Red'
-            $failCount++
-        }
-    }
-}
-
-Write-Host ''
-Write-ColorText "========================================" 'Cyan'
-$resultColor = if ($failCount -eq 0 -and $skipCount -eq 0) { 'Green' } else { 'Yellow' }
-Write-ColorText "升级完成: 成功 $successCount, 失败 $failCount, 跳过 $skipCount" $resultColor
-Write-ColorText "========================================" 'Cyan'
-Write-Host ''
-
-# 验证构建
-if (Read-Confirm '是否验证构建? (Y/n,默认为 Y): ' $true) {
-    Write-Host ''
-    Write-ColorText '正在构建解决方案...' 'Cyan'
-    Write-Host ''
-
-    & dotnet build "$RootDir\Apq.ChangeBubbling.sln" -c Release --verbosity minimal
-
-    if ($LASTEXITCODE -eq 0) {
-        Write-Host ''
-        Write-ColorText '构建成功!' 'Green'
-    } else {
-        Write-Host ''
-        Write-ColorText '构建失败,请检查错误信息' 'Red'
-    }
-}
-
-Write-Host ''

+ 7 - 0
docs/site/.gitignore

@@ -0,0 +1,7 @@
+node_modules/
+.vitepress/cache/
+.vitepress/dist/
+*.local
+.env*
+.DS_Store
+Thumbs.db

+ 268 - 0
docs/site/.vitepress/config.ts

@@ -0,0 +1,268 @@
+import { defineConfig } from 'vitepress'
+
+// 中文侧边栏配置
+const zhSidebar = {
+  '/guide/': [
+    {
+      text: '入门',
+      items: [
+        { text: '简介', link: '/guide/' },
+        { text: '快速开始', link: '/guide/quick-start' },
+        { text: '安装', link: '/guide/installation' }
+      ]
+    },
+    {
+      text: '基础',
+      items: [
+        { text: '节点类型', link: '/guide/node-types' },
+        { text: '事件冒泡', link: '/guide/event-bubbling' },
+        { text: '消息系统', link: '/guide/messaging' }
+      ]
+    },
+    {
+      text: '进阶',
+      items: [
+        { text: 'Rx 响应式流', link: '/guide/reactive-streams' },
+        { text: '事件过滤', link: '/guide/event-filtering' },
+        { text: '批量操作', link: '/guide/batch-operations' },
+        { text: '背压管线', link: '/guide/dataflow' },
+        { text: '快照服务', link: '/guide/snapshot' }
+      ]
+    },
+    {
+      text: '深入',
+      items: [
+        { text: '架构设计', link: '/guide/architecture' },
+        { text: '性能优化', link: '/guide/performance' },
+        { text: '最佳实践', link: '/guide/best-practices' }
+      ]
+    }
+  ],
+  '/api/': [
+    {
+      text: 'API 参考',
+      items: [
+        { text: '概述', link: '/api/' },
+        { text: '节点类型', link: '/api/nodes' },
+        { text: '消息系统', link: '/api/messaging' },
+        { text: '事件过滤', link: '/api/filtering' },
+        { text: '快照服务', link: '/api/snapshot' }
+      ]
+    }
+  ],
+  '/examples/': [
+    {
+      text: '示例',
+      items: [
+        { text: '概述', link: '/examples/' },
+        { text: '基础示例', link: '/examples/basic' },
+        { text: 'Rx 响应式', link: '/examples/reactive' },
+        { text: '事件过滤', link: '/examples/filtering' },
+        { text: '快照导出', link: '/examples/snapshot' }
+      ]
+    }
+  ]
+}
+
+// 英文侧边栏配置
+const enSidebar = {
+  '/en/guide/': [
+    {
+      text: 'Getting Started',
+      items: [
+        { text: 'Introduction', link: '/en/guide/' },
+        { text: 'Quick Start', link: '/en/guide/quick-start' },
+        { text: 'Installation', link: '/en/guide/installation' }
+      ]
+    },
+    {
+      text: 'Basics',
+      items: [
+        { text: 'Node Types', link: '/en/guide/node-types' },
+        { text: 'Event Bubbling', link: '/en/guide/event-bubbling' },
+        { text: 'Messaging', link: '/en/guide/messaging' }
+      ]
+    },
+    {
+      text: 'Advanced',
+      items: [
+        { text: 'Reactive Streams', link: '/en/guide/reactive-streams' },
+        { text: 'Event Filtering', link: '/en/guide/event-filtering' },
+        { text: 'Batch Operations', link: '/en/guide/batch-operations' },
+        { text: 'Dataflow Pipeline', link: '/en/guide/dataflow' },
+        { text: 'Snapshot Service', link: '/en/guide/snapshot' }
+      ]
+    },
+    {
+      text: 'Deep Dive',
+      items: [
+        { text: 'Architecture', link: '/en/guide/architecture' },
+        { text: 'Performance', link: '/en/guide/performance' },
+        { text: 'Best Practices', link: '/en/guide/best-practices' }
+      ]
+    }
+  ],
+  '/en/api/': [
+    {
+      text: 'API Reference',
+      items: [
+        { text: 'Overview', link: '/en/api/' },
+        { text: 'Node Types', link: '/en/api/nodes' },
+        { text: 'Messaging', link: '/en/api/messaging' },
+        { text: 'Filtering', link: '/en/api/filtering' },
+        { text: 'Snapshot', link: '/en/api/snapshot' }
+      ]
+    }
+  ],
+  '/en/examples/': [
+    {
+      text: 'Examples',
+      items: [
+        { text: 'Overview', link: '/en/examples/' },
+        { text: 'Basic Examples', link: '/en/examples/basic' },
+        { text: 'Reactive', link: '/en/examples/reactive' },
+        { text: 'Filtering', link: '/en/examples/filtering' },
+        { text: 'Snapshot', link: '/en/examples/snapshot' }
+      ]
+    }
+  ]
+}
+
+// Gitee 图标 SVG
+const giteeSvg = '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M512 1024C229.222 1024 0 794.778 0 512S229.222 0 512 0s512 229.222 512 512-229.222 512-512 512z m259.149-568.883h-290.74a25.293 25.293 0 0 0-25.292 25.293l-0.026 63.206c0 13.952 11.315 25.293 25.267 25.293h177.024c13.978 0 25.293 11.315 25.293 25.267v12.646a75.853 75.853 0 0 1-75.853 75.853h-240.23a25.293 25.293 0 0 1-25.267-25.293V417.203a75.853 75.853 0 0 1 75.827-75.853h353.946a25.293 25.293 0 0 0 25.267-25.292l0.077-63.207a25.293 25.293 0 0 0-25.268-25.293H417.152a189.62 189.62 0 0 0-189.62 189.645V771.15c0 13.977 11.316 25.293 25.294 25.293h372.94a170.65 170.65 0 0 0 170.65-170.65V480.384a25.293 25.293 0 0 0-25.293-25.267z" fill="currentColor"/></svg>'
+
+export default defineConfig({
+  title: 'Apq.ChangeBubbling',
+
+  head: [
+    ['link', { rel: 'icon', href: '/logo.svg' }],
+    // SEO meta 标签
+    ['meta', { name: 'keywords', content: 'Apq.ChangeBubbling, .NET, 变更冒泡, Change Bubbling, Reactive, Rx, MVVM, C#, 事件, Observable' }],
+    ['meta', { name: 'author', content: 'Apq' }],
+    // Open Graph 标签(社交媒体分享)
+    ['meta', { property: 'og:type', content: 'website' }],
+    ['meta', { property: 'og:title', content: 'Apq.ChangeBubbling - .NET Change Bubbling Library' }],
+    ['meta', { property: 'og:description', content: 'A .NET library for tree-structured change event bubbling with Rx streams, weak messaging, and pluggable scheduling' }],
+    ['meta', { property: 'og:image', content: '/logo.svg' }],
+    // Twitter Card 标签
+    ['meta', { name: 'twitter:card', content: 'summary' }],
+    ['meta', { name: 'twitter:title', content: 'Apq.ChangeBubbling - .NET Change Bubbling Library' }],
+    ['meta', { name: 'twitter:description', content: 'A .NET library for tree-structured change event bubbling with Rx streams, weak messaging, and pluggable scheduling' }],
+    // Sitemap 链接
+    ['link', { rel: 'sitemap', type: 'application/xml', href: '/sitemap.xml' }]
+  ],
+
+  // 多语言配置
+  locales: {
+    root: {
+      label: '简体中文',
+      lang: 'zh-CN',
+      description: '变更冒泡事件库,支持 Rx 响应式流、弱引用消息和可插拔调度环境',
+      themeConfig: {
+        nav: [
+          { text: '指南', link: '/guide/', activeMatch: '/guide/' },
+          { text: 'API', link: '/api/', activeMatch: '/api/' },
+          { text: '示例', link: '/examples/', activeMatch: '/examples/' },
+          { text: '更新日志', link: '/changelog' }
+        ],
+        sidebar: zhSidebar,
+        outline: {
+          label: '页面导航',
+          level: [2, 3]
+        },
+        darkModeSwitchLabel: '外观',
+        darkModeSwitchTitle: '切换到深色模式',
+        lightModeSwitchTitle: '切换到浅色模式',
+        docFooter: {
+          prev: '上一页',
+          next: '下一页'
+        },
+        editLink: {
+          pattern: 'https://gitee.com/apq/Apq.ChangeBubbling/edit/master/docs/site/:path',
+          text: '在 Gitee 上编辑此页'
+        },
+        footer: {
+          message: '基于 MIT 许可发布',
+          copyright: `Copyright © ${new Date().getFullYear()} Apq.ChangeBubbling`
+        },
+        search: {
+          provider: 'local',
+          options: {
+            translations: {
+              button: {
+                buttonText: '搜索文档',
+                buttonAriaLabel: '搜索文档'
+              },
+              modal: {
+                noResultsText: '无法找到相关结果',
+                resetButtonTitle: '清除查询条件',
+                footer: {
+                  selectText: '选择',
+                  navigateText: '切换'
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    en: {
+      label: 'English',
+      lang: 'en-US',
+      link: '/en/',
+      description: 'Change bubbling event library with Rx streams, weak messaging, and pluggable scheduling',
+      themeConfig: {
+        nav: [
+          { text: 'Guide', link: '/en/guide/', activeMatch: '/en/guide/' },
+          { text: 'API', link: '/en/api/', activeMatch: '/en/api/' },
+          { text: 'Examples', link: '/en/examples/', activeMatch: '/en/examples/' },
+          { text: 'Changelog', link: '/en/changelog' }
+        ],
+        sidebar: enSidebar,
+        outline: {
+          label: 'On this page',
+          level: [2, 3]
+        },
+        docFooter: {
+          prev: 'Previous',
+          next: 'Next'
+        },
+        editLink: {
+          pattern: 'https://gitee.com/apq/Apq.ChangeBubbling/edit/master/docs/site/:path',
+          text: 'Edit this page on Gitee'
+        },
+        footer: {
+          message: 'Released under the MIT License',
+          copyright: `Copyright © ${new Date().getFullYear()} Apq.ChangeBubbling`
+        }
+      }
+    }
+  },
+
+  themeConfig: {
+    logo: '/logo.svg',
+
+    socialLinks: [
+      {
+        icon: { svg: giteeSvg },
+        link: 'https://gitee.com/apq/Apq.ChangeBubbling'
+      }
+    ]
+  },
+
+  markdown: {
+    lineNumbers: true
+  },
+
+  lastUpdated: false,
+
+  // 忽略 localhost 链接
+  ignoreDeadLinks: [
+    /^http:\/\/localhost/
+  ],
+
+  // Sitemap 配置(SEO 优化)
+  sitemap: {
+    hostname: 'https://apq-changebubbling.gitee.io'
+  }
+})

+ 59 - 0
docs/site/.vitepress/theme/custom.css

@@ -0,0 +1,59 @@
+/* 自定义样式 */
+
+:root {
+  --vp-c-brand-1: #6366f1;
+  --vp-c-brand-2: #8b5cf6;
+  --vp-c-brand-3: #5558e3;
+  --vp-c-brand-soft: rgba(99, 102, 241, 0.14);
+}
+
+.dark {
+  --vp-c-brand-1: #8b5cf6;
+  --vp-c-brand-2: #6366f1;
+  --vp-c-brand-3: #a78bfa;
+}
+
+/* 首页样式 */
+.VPHero .name {
+  background: linear-gradient(120deg, var(--vp-c-brand-1) 30%, var(--vp-c-brand-2));
+  -webkit-background-clip: text;
+  background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+/* 代码块样式 */
+.vp-doc div[class*='language-'] {
+  border-radius: 8px;
+}
+
+/* 表格样式 */
+.vp-doc table {
+  display: table;
+  width: 100%;
+}
+
+.vp-doc th,
+.vp-doc td {
+  padding: 12px 16px;
+}
+
+/* 特性卡片样式 */
+.VPFeature {
+  border-radius: 12px;
+  transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.VPFeature:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+/* 侧边栏样式 */
+.VPSidebar {
+  scrollbar-width: thin;
+}
+
+/* 搜索框样式 */
+.DocSearch-Button {
+  border-radius: 8px !important;
+}

+ 7 - 0
docs/site/.vitepress/theme/index.ts

@@ -0,0 +1,7 @@
+import DefaultTheme from 'vitepress/theme'
+import type { Theme } from 'vitepress'
+import './custom.css'
+
+export default {
+  extends: DefaultTheme
+} satisfies Theme

+ 69 - 0
docs/site/api/index.md

@@ -0,0 +1,69 @@
+# API 概述
+
+本节提供 Apq.ChangeBubbling 的完整 API 参考文档。
+
+## 命名空间
+
+| 命名空间 | 说明 |
+|----------|------|
+| `Apq.ChangeBubbling.Abstractions` | 抽象定义、接口、枚举 |
+| `Apq.ChangeBubbling.Core` | 核心实现、节点基类 |
+| `Apq.ChangeBubbling.Nodes` | 节点实现类 |
+| `Apq.ChangeBubbling.Messaging` | 消息中心、Rx 流 |
+| `Apq.ChangeBubbling.Infrastructure.EventFiltering` | 事件过滤器 |
+| `Apq.ChangeBubbling.Infrastructure.Dataflow` | TPL Dataflow 管线 |
+| `Apq.ChangeBubbling.Snapshot` | 快照服务 |
+
+## 核心类型
+
+### 节点类型
+
+- [ListBubblingNode&lt;T&gt;](/api/nodes#listbubblingnode) - 列表节点
+- [DictionaryBubblingNode&lt;TKey, TValue&gt;](/api/nodes#dictionarybubblingnode) - 字典节点
+- [ConcurrentBagBubblingNode&lt;T&gt;](/api/nodes#concurrentbagbubblingnode) - 线程安全列表节点
+- [ConcurrentDictionaryBubblingNode&lt;TKey, TValue&gt;](/api/nodes#concurrentdictionarybubblingnode) - 线程安全字典节点
+
+### 消息系统
+
+- [ChangeMessenger](/api/messaging#changemessenger) - 消息发布中心
+- [BubblingChangeMessage](/api/messaging#bubblingchangemessage) - 消息包装类
+
+### 事件过滤
+
+- [PropertyBasedEventFilter](/api/filtering#propertybasedeventfilter) - 属性过滤器
+- [PathBasedEventFilter](/api/filtering#pathbasedeventfilter) - 路径过滤器
+- [FrequencyBasedEventFilter](/api/filtering#frequencybasedeventfilter) - 频率过滤器
+- [CompositeEventFilter](/api/filtering#compositeeventfilter) - 组合过滤器
+
+### 快照服务
+
+- [TreeSnapshotService](/api/snapshot#treesnapshotservice) - 树形快照服务
+- [SnapshotSerializer](/api/snapshot#snapshotserializer) - 快照序列化器
+
+## 变更类型枚举
+
+```csharp
+public enum NodeChangeKind
+{
+    PropertyUpdate,      // 属性值更新
+    CollectionAdd,       // 集合添加元素
+    CollectionRemove,    // 集合移除元素
+    CollectionReplace,   // 集合替换元素
+    CollectionMove,      // 集合移动元素
+    CollectionReset      // 集合重置
+}
+```
+
+## BubblingChange 结构体
+
+```csharp
+public readonly struct BubblingChange
+{
+    public string NodeName { get; }
+    public string PropertyName { get; }
+    public NodeChangeKind Kind { get; }
+    public object? OldValue { get; }
+    public object? NewValue { get; }
+    public IReadOnlyList<string> PathSegments { get; }
+}
+```

+ 49 - 0
docs/site/changelog.md

@@ -0,0 +1,49 @@
+# 更新日志
+
+## v1.0.0
+
+### 新功能
+
+- 变更冒泡机制
+  - 子节点变更自动冒泡到父节点
+  - 携带完整路径信息
+  - 支持多级嵌套
+
+- 节点类型
+  - `ListBubblingNode<T>` - 列表节点
+  - `DictionaryBubblingNode<TKey, TValue>` - 字典节点
+  - `ConcurrentBagBubblingNode<T>` - 线程安全列表节点
+  - `ConcurrentDictionaryBubblingNode<TKey, TValue>` - 线程安全字典节点
+
+- Rx 响应式流
+  - `AsObservable()` - 原始事件流
+  - `AsThrottledObservable()` - 节流事件流
+  - `AsBufferedObservable()` - 缓冲批量事件流
+
+- 消息系统
+  - 弱引用消息,避免内存泄漏
+  - 对象池,零 GC 分配
+  - 可插拔调度环境
+
+- 事件过滤
+  - `PropertyBasedEventFilter` - 属性过滤器
+  - `PathBasedEventFilter` - 路径过滤器
+  - `FrequencyBasedEventFilter` - 频率过滤器
+  - `CompositeEventFilter` - 组合过滤器
+
+- 批量操作
+  - `BeginBatch()`/`EndBatch()` - 批量收集事件
+  - `BeginCoalesce()`/`EndCoalesce()` - 事件合并
+
+- 背压管线
+  - 基于 TPL Dataflow
+  - 可配置容量和并行度
+
+- 快照服务
+  - 树形快照导出与导入
+  - JSON 序列化支持
+
+### 支持的框架
+
+- .NET 8.0
+- .NET 10.0

+ 50 - 0
docs/site/en/api/index.md

@@ -0,0 +1,50 @@
+# API Overview
+
+This section provides the complete API reference for Apq.ChangeBubbling.
+
+## Namespaces
+
+| Namespace | Description |
+|-----------|-------------|
+| `Apq.ChangeBubbling.Abstractions` | Abstract definitions, interfaces, enums |
+| `Apq.ChangeBubbling.Core` | Core implementation, node base class |
+| `Apq.ChangeBubbling.Nodes` | Node implementation classes |
+| `Apq.ChangeBubbling.Messaging` | Message center, Rx streams |
+| `Apq.ChangeBubbling.Infrastructure.EventFiltering` | Event filters |
+| `Apq.ChangeBubbling.Infrastructure.Dataflow` | TPL Dataflow pipeline |
+| `Apq.ChangeBubbling.Snapshot` | Snapshot service |
+
+## Core Types
+
+### Node Types
+
+- [ListBubblingNode&lt;T&gt;](/en/api/nodes#listbubblingnode) - List node
+- [DictionaryBubblingNode&lt;TKey, TValue&gt;](/en/api/nodes#dictionarybubblingnode) - Dictionary node
+- [ConcurrentBagBubblingNode&lt;T&gt;](/en/api/nodes#concurrentbagbubblingnode) - Thread-safe list node
+- [ConcurrentDictionaryBubblingNode&lt;TKey, TValue&gt;](/en/api/nodes#concurrentdictionarybubblingnode) - Thread-safe dictionary node
+
+### Messaging
+
+- [ChangeMessenger](/en/api/messaging#changemessenger) - Message publishing center
+- [BubblingChangeMessage](/en/api/messaging#bubblingchangemessage) - Message wrapper class
+
+### Event Filtering
+
+- [PropertyBasedEventFilter](/en/api/filtering#propertybasedeventfilter) - Property filter
+- [PathBasedEventFilter](/en/api/filtering#pathbasedeventfilter) - Path filter
+- [FrequencyBasedEventFilter](/en/api/filtering#frequencybasedeventfilter) - Frequency filter
+- [CompositeEventFilter](/en/api/filtering#compositeeventfilter) - Composite filter
+
+## Change Kind Enum
+
+```csharp
+public enum NodeChangeKind
+{
+    PropertyUpdate,      // Property value update
+    CollectionAdd,       // Collection add element
+    CollectionRemove,    // Collection remove element
+    CollectionReplace,   // Collection replace element
+    CollectionMove,      // Collection move element
+    CollectionReset      // Collection reset
+}
+```

+ 57 - 0
docs/site/en/examples/index.md

@@ -0,0 +1,57 @@
+# Examples Overview
+
+This section provides usage examples for Apq.ChangeBubbling.
+
+## Example List
+
+| Example | Description |
+|---------|-------------|
+| [Basic Examples](/en/examples/basic) | Node creation, event subscription, basic operations |
+| [Reactive](/en/examples/reactive) | Reactive streams, throttling, buffering |
+| [Filtering](/en/examples/filtering) | Property, path, and frequency filtering |
+| [Snapshot](/en/examples/snapshot) | Snapshot export and import |
+
+## Quick Example
+
+### Create Node Tree
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+var root = new ListBubblingNode<string>("Root");
+var users = new ListBubblingNode<User>("Users");
+var settings = new DictionaryBubblingNode<string, object>("Settings");
+
+root.AttachChild(users);
+root.AttachChild(settings);
+```
+
+### Subscribe to Changes
+
+```csharp
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"[{change.Kind}] {change.NodeName}.{change.PropertyName}");
+};
+```
+
+### Use Rx Streams
+
+```csharp
+using Apq.ChangeBubbling.Messaging;
+using System.Reactive.Linq;
+
+ChangeMessenger.AsObservable()
+    .Where(c => c.Kind == NodeChangeKind.CollectionAdd)
+    .Throttle(TimeSpan.FromMilliseconds(100))
+    .Subscribe(c => Console.WriteLine($"Added: {c.PropertyName}"));
+```
+
+## Run Examples
+
+The sample project is located in `Samples/Apq.ChangeBubbling.Samples`:
+
+```bash
+cd Samples/Apq.ChangeBubbling.Samples
+dotnet run
+```

+ 97 - 0
docs/site/en/guide/index.md

@@ -0,0 +1,97 @@
+# Introduction
+
+Apq.ChangeBubbling is a change bubbling event library that provides automatic change event bubbling for tree-structured data, Rx reactive streams, weak reference messaging, and pluggable scheduling environments.
+
+## Why Apq.ChangeBubbling?
+
+### 🌳 Change Event Bubbling
+
+Child node changes automatically bubble up to parent nodes with full path information:
+
+```csharp
+var root = new ListBubblingNode<string>("Root");
+var child = new ListBubblingNode<int>("Child");
+
+root.AttachChild(child);
+
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"Path: {string.Join(".", change.PathSegments)}");
+};
+
+child.Add(42);  // Output: Path: Child.0
+```
+
+### 📡 Rx Reactive Streams
+
+System.Reactive based reactive programming support:
+
+```csharp
+// Subscribe to raw event stream
+ChangeMessenger.AsObservable()
+    .Subscribe(change => Console.WriteLine($"Received: {change.PropertyName}"));
+
+// Throttled event stream
+ChangeMessenger.AsThrottledObservable(TimeSpan.FromMilliseconds(100))
+    .Subscribe(change => Console.WriteLine($"Throttled: {change.PropertyName}"));
+```
+
+### 💬 Weak Reference Messaging
+
+CommunityToolkit.Mvvm integration for memory-safe messaging:
+
+- Automatic cleanup of invalid subscriptions
+- Cross-component communication
+- No manual unsubscription needed
+
+### ⚡ Pluggable Scheduling
+
+Multiple scheduling modes:
+
+```csharp
+// Thread pool (default)
+ChangeMessenger.RegisterThreadPool("default");
+
+// UI thread
+ChangeMessenger.RegisterDispatcher("ui");
+
+// Dedicated thread
+var disposable = ChangeMessenger.RegisterDedicatedThread("worker", "WorkerThread");
+```
+
+## Quick Start
+
+### Installation
+
+```bash
+dotnet add package Apq.ChangeBubbling
+```
+
+### Basic Usage
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+var root = new ListBubblingNode<string>("Root");
+
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"Change type: {change.Kind}");
+};
+
+root.Add("Hello");
+root.Add("World");
+```
+
+## Compatibility
+
+| Framework | Version |
+|-----------|---------|
+| .NET | 8.0, 10.0 (LTS) |
+
+## Next Steps
+
+- [Quick Start](/en/guide/quick-start) - 5-minute tutorial
+- [Node Types](/en/guide/node-types) - Learn about node types
+- [Event Bubbling](/en/guide/event-bubbling) - Understand bubbling mechanism
+- [API Reference](/en/api/) - Complete API documentation

+ 84 - 0
docs/site/en/index.md

@@ -0,0 +1,84 @@
+---
+layout: home
+
+hero:
+  name: Apq.ChangeBubbling
+  text: Change Bubbling Event Library
+  tagline: Rx reactive streams, weak messaging, and pluggable scheduling
+  image:
+    src: /logo.svg
+    alt: Apq.ChangeBubbling
+  actions:
+    - theme: brand
+      text: Get Started
+      link: /en/guide/
+    - theme: alt
+      text: View on Gitee
+      link: https://gitee.com/apq/Apq.ChangeBubbling
+
+features:
+  - icon: 🌳
+    title: Change Event Bubbling
+    details: Child node changes automatically bubble up to parent nodes with full path information
+  - icon: 📡
+    title: Rx Reactive Streams
+    details: System.Reactive based reactive programming with throttling, buffering, and filtering
+  - icon: 💬
+    title: Weak Reference Messaging
+    details: CommunityToolkit.Mvvm integration for memory-safe cross-component communication
+  - icon: ⚡
+    title: Pluggable Scheduling
+    details: Support for thread pool, UI thread, dedicated threads, and Nito.AsyncEx
+  - icon: 🎯
+    title: Event Filtering
+    details: Built-in property, path, and frequency filters for precise event control
+  - icon: 📸
+    title: Snapshot Service
+    details: Export and import node tree snapshots for state persistence
+---
+
+<div class="vp-doc" style="padding: 2rem;">
+
+## Quick Install
+
+::: code-group
+
+```bash [.NET CLI]
+dotnet add package Apq.ChangeBubbling
+```
+
+```xml [PackageReference]
+<PackageReference Include="Apq.ChangeBubbling" Version="1.0.*" />
+```
+
+:::
+
+## Simple Example
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+// Create node tree
+var root = new ListBubblingNode<string>("Root");
+var child = new ListBubblingNode<int>("Child");
+
+// Establish parent-child relationship
+root.AttachChild(child);
+
+// Subscribe to change events
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"Change: {change.PropertyName}, Path: {string.Join(".", change.PathSegments)}");
+};
+
+// Child changes bubble up to parent
+child.Add(42);  // Output: Change: 0, Path: Child.0
+```
+
+## Supported Frameworks
+
+| Framework | Version |
+|-----------|---------|
+| .NET | 8.0, 10.0 (LTS) |
+
+</div>

+ 70 - 0
docs/site/examples/index.md

@@ -0,0 +1,70 @@
+# 示例概述
+
+本节提供 Apq.ChangeBubbling 的使用示例。
+
+## 示例列表
+
+| 示例 | 说明 |
+|------|------|
+| [基础示例](/examples/basic) | 节点创建、事件订阅、基本操作 |
+| [Rx 响应式](/examples/reactive) | 响应式流、节流、缓冲 |
+| [事件过滤](/examples/filtering) | 属性过滤、路径过滤、频率过滤 |
+| [快照导出](/examples/snapshot) | 快照导出与导入 |
+
+## 快速示例
+
+### 创建节点树
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+var root = new ListBubblingNode<string>("Root");
+var users = new ListBubblingNode<User>("Users");
+var settings = new DictionaryBubblingNode<string, object>("Settings");
+
+root.AttachChild(users);
+root.AttachChild(settings);
+```
+
+### 订阅变更
+
+```csharp
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"[{change.Kind}] {change.NodeName}.{change.PropertyName}");
+};
+```
+
+### 使用 Rx 流
+
+```csharp
+using Apq.ChangeBubbling.Messaging;
+using System.Reactive.Linq;
+
+ChangeMessenger.AsObservable()
+    .Where(c => c.Kind == NodeChangeKind.CollectionAdd)
+    .Throttle(TimeSpan.FromMilliseconds(100))
+    .Subscribe(c => Console.WriteLine($"新增: {c.PropertyName}"));
+```
+
+### 批量操作
+
+```csharp
+var node = new ListBubblingNode<int>("Numbers");
+
+node.BeginBatch();
+for (int i = 0; i < 100; i++)
+{
+    node.Add(i);
+}
+node.EndBatch();  // 一次性触发所有事件
+```
+
+## 运行示例
+
+示例项目位于 `Samples/Apq.ChangeBubbling.Samples` 目录:
+
+```bash
+cd Samples/Apq.ChangeBubbling.Samples
+dotnet run
+```

+ 145 - 0
docs/site/guide/index.md

@@ -0,0 +1,145 @@
+# 简介
+
+Apq.ChangeBubbling 是一个变更冒泡事件库,提供树形数据结构的变更事件自动冒泡、Rx 响应式流、弱引用消息和可插拔调度环境。
+
+## 为什么选择 Apq.ChangeBubbling?
+
+### 🌳 变更事件冒泡
+
+子节点的变更事件自动向上冒泡到父节点,携带完整路径信息:
+
+```csharp
+var root = new ListBubblingNode<string>("Root");
+var child = new ListBubblingNode<int>("Child");
+
+root.AttachChild(child);
+
+root.NodeChanged += (sender, change) =>
+{
+    // 可以追踪变更来源的完整路径
+    Console.WriteLine($"路径: {string.Join(".", change.PathSegments)}");
+};
+
+child.Add(42);  // 输出: 路径: Child.0
+```
+
+### 📡 Rx 响应式流
+
+基于 System.Reactive 的响应式编程支持:
+
+```csharp
+// 订阅原始事件流
+ChangeMessenger.AsObservable()
+    .Subscribe(change => Console.WriteLine($"收到变更: {change.PropertyName}"));
+
+// 节流事件流
+ChangeMessenger.AsThrottledObservable(TimeSpan.FromMilliseconds(100))
+    .Subscribe(change => Console.WriteLine($"节流后: {change.PropertyName}"));
+
+// 缓冲批量事件流
+ChangeMessenger.AsBufferedObservable(TimeSpan.FromSeconds(1), 100)
+    .Subscribe(changes => Console.WriteLine($"批量收到 {changes.Count} 个变更"));
+```
+
+### 💬 弱引用消息
+
+集成 CommunityToolkit.Mvvm 弱引用消息,避免内存泄漏:
+
+- 自动清理失效订阅
+- 支持跨组件通信
+- 无需手动取消订阅
+
+### ⚡ 可插拔调度环境
+
+支持多种调度模式:
+
+```csharp
+// 使用线程池(默认)
+ChangeMessenger.RegisterThreadPool("default");
+
+// 使用 UI 线程
+ChangeMessenger.RegisterDispatcher("ui");
+
+// 使用专用线程
+var disposable = ChangeMessenger.RegisterDedicatedThread("worker", "WorkerThread");
+
+// 发布到指定环境
+ChangeMessenger.Publish(change, "ui");
+```
+
+### 🎯 事件过滤
+
+内置多种过滤器:
+
+```csharp
+// 基于属性的过滤器
+var propertyFilter = new PropertyBasedEventFilter(
+    allowedProperties: new[] { "Name", "Value" },
+    excludedKinds: new[] { NodeChangeKind.CollectionReset }
+);
+
+// 基于路径的过滤器
+var pathFilter = new PathBasedEventFilter(
+    allowedPaths: new[] { "Root.Settings" },
+    maxDepth: 3
+);
+
+// 基于频率的过滤器(节流)
+var frequencyFilter = new FrequencyBasedEventFilter(
+    throttleInterval: TimeSpan.FromMilliseconds(100)
+);
+```
+
+### 📸 快照服务
+
+支持节点树的快照导出与导入:
+
+```csharp
+// 导出快照
+var snapshot = TreeSnapshotService.Export(rootNode);
+var json = SnapshotSerializer.ToJson(snapshot);
+
+// 导入快照
+var loadedSnapshot = SnapshotSerializer.FromJson(json);
+var restoredNode = TreeSnapshotService.Import(loadedSnapshot);
+```
+
+## 快速开始
+
+### 安装
+
+```bash
+dotnet add package Apq.ChangeBubbling
+```
+
+### 基本用法
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+// 创建节点
+var root = new ListBubblingNode<string>("Root");
+
+// 订阅变更
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"变更类型: {change.Kind}");
+};
+
+// 操作节点
+root.Add("Hello");
+root.Add("World");
+```
+
+## 兼容性
+
+| 框架 | 版本 |
+|------|------|
+| .NET | 8.0, 10.0 (LTS) |
+
+## 下一步
+
+- [快速开始](/guide/quick-start) - 5 分钟上手教程
+- [节点类型](/guide/node-types) - 了解所有节点类型
+- [事件冒泡](/guide/event-bubbling) - 深入理解冒泡机制
+- [API 参考](/api/) - 完整的 API 文档

+ 61 - 0
docs/site/guide/installation.md

@@ -0,0 +1,61 @@
+# 安装
+
+## 系统要求
+
+- .NET 8.0 或 .NET 10.0
+
+## 使用 .NET CLI
+
+```bash
+dotnet add package Apq.ChangeBubbling
+```
+
+## 使用 PackageReference
+
+在项目文件 (`.csproj`) 中添加:
+
+```xml
+<PackageReference Include="Apq.ChangeBubbling" Version="1.0.*" />
+```
+
+## 使用 NuGet 包管理器
+
+在 Visual Studio 中:
+
+1. 右键点击项目 → 管理 NuGet 包
+2. 搜索 `Apq.ChangeBubbling`
+3. 点击安装
+
+## 依赖项
+
+Apq.ChangeBubbling 依赖以下包(会自动安装):
+
+| 包名 | 用途 |
+|------|------|
+| System.Reactive | Rx 响应式编程 |
+| System.Threading.Tasks.Dataflow | TPL Dataflow 背压管线 |
+| CommunityToolkit.Mvvm | 弱引用消息 |
+| Castle.Core | 动态代理 |
+| Nito.AsyncEx | 异步上下文 |
+| PropertyChanged.Fody | 自动织入 INotifyPropertyChanged |
+
+## 验证安装
+
+创建一个简单的测试程序:
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+var node = new ListBubblingNode<string>("Test");
+node.NodeChanged += (s, e) => Console.WriteLine($"变更: {e.PropertyName}");
+node.Add("Hello");
+
+Console.WriteLine("安装成功!");
+```
+
+运行程序,如果看到输出则表示安装成功。
+
+## 下一步
+
+- [快速开始](/guide/quick-start) - 5 分钟上手教程
+- [节点类型](/guide/node-types) - 了解所有节点类型

+ 134 - 0
docs/site/guide/quick-start.md

@@ -0,0 +1,134 @@
+# 快速开始
+
+本指南将帮助你在 5 分钟内上手 Apq.ChangeBubbling。
+
+## 安装
+
+使用 .NET CLI 安装:
+
+```bash
+dotnet add package Apq.ChangeBubbling
+```
+
+或在项目文件中添加:
+
+```xml
+<PackageReference Include="Apq.ChangeBubbling" Version="1.0.*" />
+```
+
+## 创建第一个节点树
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+// 创建根节点
+var root = new ListBubblingNode<string>("Root");
+
+// 创建子节点
+var users = new ListBubblingNode<string>("Users");
+var settings = new DictionaryBubblingNode<string, int>("Settings");
+
+// 建立父子关系
+root.AttachChild(users);
+root.AttachChild(settings);
+```
+
+## 订阅变更事件
+
+```csharp
+// 在根节点订阅,可以接收所有子节点的变更
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"节点: {change.NodeName}");
+    Console.WriteLine($"属性: {change.PropertyName}");
+    Console.WriteLine($"类型: {change.Kind}");
+    Console.WriteLine($"路径: {string.Join(".", change.PathSegments)}");
+    Console.WriteLine("---");
+};
+```
+
+## 触发变更
+
+```csharp
+// 添加用户
+users.Add("Alice");
+users.Add("Bob");
+
+// 设置配置
+settings["MaxRetries"] = 3;
+settings["Timeout"] = 5000;
+```
+
+输出:
+
+```
+节点: Users
+属性: 0
+类型: CollectionAdd
+路径: Users.0
+---
+节点: Users
+属性: 1
+类型: CollectionAdd
+路径: Users.1
+---
+节点: Settings
+属性: MaxRetries
+类型: CollectionAdd
+路径: Settings.MaxRetries
+---
+节点: Settings
+属性: Timeout
+类型: CollectionAdd
+路径: Settings.Timeout
+---
+```
+
+## 使用 Rx 响应式流
+
+```csharp
+using Apq.ChangeBubbling.Messaging;
+using System.Reactive.Linq;
+
+// 订阅所有变更
+ChangeMessenger.AsObservable()
+    .Where(c => c.Kind == NodeChangeKind.CollectionAdd)
+    .Subscribe(change =>
+    {
+        Console.WriteLine($"新增: {change.PropertyName}");
+    });
+
+// 节流订阅(100ms 内只处理一次)
+ChangeMessenger.AsThrottledObservable(TimeSpan.FromMilliseconds(100))
+    .Subscribe(change =>
+    {
+        Console.WriteLine($"节流后: {change.PropertyName}");
+    });
+```
+
+## 批量操作
+
+```csharp
+var node = new ListBubblingNode<int>("Numbers");
+
+// 开始批量操作
+node.BeginBatch();
+try
+{
+    for (int i = 0; i < 1000; i++)
+    {
+        node.Add(i);
+    }
+}
+finally
+{
+    // 结束批量操作,一次性触发所有事件
+    node.EndBatch();
+}
+```
+
+## 下一步
+
+- [节点类型](/guide/node-types) - 了解所有可用的节点类型
+- [事件冒泡](/guide/event-bubbling) - 深入理解冒泡机制
+- [Rx 响应式流](/guide/reactive-streams) - 高级响应式编程

+ 106 - 0
docs/site/index.md

@@ -0,0 +1,106 @@
+---
+layout: home
+
+hero:
+  name: Apq.ChangeBubbling
+  text: 变更冒泡事件库
+  tagline: 支持 Rx 响应式流、弱引用消息和可插拔调度环境
+  image:
+    src: /logo.svg
+    alt: Apq.ChangeBubbling
+  actions:
+    - theme: brand
+      text: 快速开始
+      link: /guide/
+    - theme: alt
+      text: 在 Gitee 上查看
+      link: https://gitee.com/apq/Apq.ChangeBubbling
+
+features:
+  - icon: 🌳
+    title: 变更事件冒泡
+    details: 子节点的变更事件自动向上冒泡到父节点,携带完整路径信息,轻松追踪数据变化来源
+  - icon: 📡
+    title: Rx 响应式流
+    details: 基于 System.Reactive 的响应式编程支持,提供节流、缓冲、过滤等丰富的流操作
+  - icon: 💬
+    title: 弱引用消息
+    details: 集成 CommunityToolkit.Mvvm 弱引用消息,避免内存泄漏,支持跨组件通信
+  - icon: ⚡
+    title: 可插拔调度
+    details: 支持线程池、UI 线程、专用线程、Nito.AsyncEx 等多种调度环境
+  - icon: 🎯
+    title: 事件过滤
+    details: 内置属性过滤、路径过滤、频率节流等多种过滤器,精确控制事件传播
+  - icon: 📸
+    title: 快照服务
+    details: 支持节点树的快照导出与导入,方便状态持久化和恢复
+---
+
+<div class="vp-doc" style="padding: 2rem;">
+
+## 快速安装
+
+::: code-group
+
+```bash [.NET CLI]
+dotnet add package Apq.ChangeBubbling
+```
+
+```xml [PackageReference]
+<PackageReference Include="Apq.ChangeBubbling" Version="1.0.*" />
+```
+
+:::
+
+## 简单示例
+
+```csharp
+using Apq.ChangeBubbling.Nodes;
+
+// 创建节点树
+var root = new ListBubblingNode<string>("Root");
+var child = new ListBubblingNode<int>("Child");
+
+// 建立父子关系
+root.AttachChild(child);
+
+// 订阅变更事件
+root.NodeChanged += (sender, change) =>
+{
+    Console.WriteLine($"变更: {change.PropertyName}, 路径: {string.Join(".", change.PathSegments)}");
+};
+
+// 子节点的变更会自动冒泡到父节点
+child.Add(42);  // 输出: 变更: 0, 路径: Child.0
+```
+
+## 核心特性
+
+| 特性 | 说明 |
+|------|------|
+| 变更冒泡 | 子节点变更自动冒泡到父节点,携带完整路径 |
+| Rx 响应式 | 支持 Observable 流、节流、缓冲、过滤 |
+| 弱引用消息 | 避免内存泄漏,自动清理失效订阅 |
+| 批量操作 | BeginBatch/EndBatch 收集并批量触发事件 |
+| 事件合并 | BeginCoalesce/EndCoalesce 合并同属性多次变更 |
+| 背压管线 | 基于 TPL Dataflow 的背压处理管线 |
+| 快照服务 | 节点树快照导出与导入 |
+| 线程安全 | 提供 ConcurrentBag/ConcurrentDictionary 节点 |
+
+## 节点类型
+
+| 类型 | 描述 |
+|------|------|
+| `ListBubblingNode<T>` | 基于列表的冒泡节点 |
+| `DictionaryBubblingNode<TKey, TValue>` | 基于字典的冒泡节点 |
+| `ConcurrentBagBubblingNode<T>` | 线程安全的列表冒泡节点 |
+| `ConcurrentDictionaryBubblingNode<TKey, TValue>` | 线程安全的字典冒泡节点 |
+
+## 支持的框架
+
+| 框架 | 版本 |
+|------|------|
+| .NET | 8.0, 10.0 (LTS) |
+
+</div>

+ 26 - 0
docs/site/package.json

@@ -0,0 +1,26 @@
+{
+  "name": "apq-changebubbling-docs",
+  "version": "1.0.0",
+  "description": "Apq.ChangeBubbling 文档站点 - 变更冒泡事件库",
+  "type": "module",
+  "scripts": {
+    "dev": "vitepress dev",
+    "build": "vitepress build",
+    "preview": "vitepress preview"
+  },
+  "keywords": [
+    "apq.changebubbling",
+    "change-bubbling",
+    "reactive",
+    "rx",
+    "mvvm",
+    "dotnet",
+    "documentation",
+    "vitepress"
+  ],
+  "author": "Apq",
+  "license": "MIT",
+  "devDependencies": {
+    "vitepress": "^1.5.0"
+  }
+}

+ 12 - 0
docs/site/public/logo.svg

@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
+  <defs>
+    <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
+      <stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
+      <stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
+    </linearGradient>
+  </defs>
+  <rect x="8" y="8" width="112" height="112" rx="16" fill="url(#grad1)"/>
+  <text x="64" y="85" font-family="Arial, sans-serif" font-size="64" font-weight="bold" fill="white" text-anchor="middle">B</text>
+  <circle cx="96" cy="32" r="12" fill="white" opacity="0.9"/>
+  <path d="M90 32 L96 26 L96 38 Z" fill="#6366f1"/>
+</svg>

+ 98 - 0
docs/文档站点部署指南.md

@@ -0,0 +1,98 @@
+# 文档站点部署指南
+
+本文档介绍如何部署 Apq.ChangeBubbling 文档站点。
+
+## 技术栈
+
+- **VitePress** - Vue 3 + Vite 驱动的静态站点生成器
+- **Node.js 20+** - 运行时环境
+
+## 本地开发
+
+### 安装依赖
+
+```bash
+cd docs/site
+npm install
+```
+
+### 启动开发服务器
+
+```bash
+npm run dev
+```
+
+访问 http://localhost:5173 查看文档。
+
+### 构建生产版本
+
+```bash
+npm run build
+```
+
+构建产物位于 `.vitepress/dist` 目录。
+
+### 本地预览
+
+```bash
+npm run preview
+```
+
+## 部署方案
+
+### 方案一:Gitee Pages(推荐)
+
+国内访问速度最快。
+
+1. 在 Gitee 仓库设置中启用 Gitee Pages
+2. 选择部署分支和目录
+3. 手动或自动部署
+
+### 方案二:Vercel
+
+个人项目永久免费,全球 CDN。
+
+1. 在 [Vercel](https://vercel.com) 导入 Git 仓库
+2. 配置构建命令:
+   - Build Command: `cd docs/site && npm install && npm run build`
+   - Output Directory: `docs/site/.vitepress/dist`
+3. 部署
+
+## 目录结构
+
+```
+docs/site/
+├── .vitepress/
+│   ├── config.ts          # VitePress 配置
+│   ├── theme/
+│   │   ├── index.ts       # 主题配置
+│   │   └── custom.css     # 自定义样式
+│   ├── cache/             # 构建缓存
+│   └── dist/              # 构建产物
+├── public/
+│   └── logo.svg           # 站点 Logo
+├── guide/                 # 指南文档
+├── api/                   # API 文档
+├── examples/              # 示例文档
+├── en/                    # 英文版本
+├── index.md               # 首页
+├── changelog.md           # 更新日志
+└── package.json           # npm 配置
+```
+
+## 添加新文档
+
+1. 在对应目录创建 `.md` 文件
+2. 在 `.vitepress/config.ts` 的侧边栏配置中添加链接
+3. 如需多语言,在 `en/` 目录创建对应文件
+
+## 自定义主题
+
+编辑 `.vitepress/theme/custom.css` 修改样式:
+
+```css
+:root {
+  --vp-c-brand-1: #6366f1;  /* 主色调 */
+  --vp-c-brand-2: #8b5cf6;
+}
+```

+ 1 - 1
tests/Apq.ChangeBubbling.Tests.Net9/Apq.ChangeBubbling.Tests.Net9.csproj → tests/Apq.ChangeBubbling.Tests.Net10/Apq.ChangeBubbling.Tests.Net10.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <TargetFramework>net10.0</TargetFramework>
     <TargetFrameworks></TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>

+ 0 - 39
tests/Apq.ChangeBubbling.Tests.Net6/Apq.ChangeBubbling.Tests.Net6.csproj

@@ -1,39 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
-    <TargetFrameworks></TargetFrameworks>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <Nullable>enable</Nullable>
-    <IsPackable>false</IsPackable>
-    <IsTestProject>true</IsTestProject>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
-    <PackageReference Include="xunit" Version="2.9.3" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-    <PackageReference Include="coverlet.collector" Version="6.0.4">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-    <PackageReference Include="Xunit.SkippableFact" Version="1.5.23" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Using Include="Xunit" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\..\Apq.ChangeBubbling\Apq.ChangeBubbling.csproj" />
-  </ItemGroup>
-
-  <!-- 引用共享测试代码 -->
-  <ItemGroup>
-    <Compile Include="..\Apq.ChangeBubbling.Tests.Shared\**\*.cs" Link="Shared\%(RecursiveDir)%(Filename)%(Extension)" />
-  </ItemGroup>
-
-</Project>