入门篇:ASP.NET Core Web API 实战.md
Alpine.js 入门教程:给你的 HTML 添加超能力
一、Alpine.js 是什么?
Alpine.js 的口号是"像写 Tailwind CSS 一样写 JavaScript"。它让你用简单的 HTML 属性为页面添加交互,无需构建工具、无需虚拟 DOM,只有 15 个属性 + 6 个魔法方法 + 1 个全局状态。非常适合后端开发者给模板引擎(Laravel Blade、Django Templates、Rails ERB 等)添加交互,或为静态网站注入轻量级动态行为。
二、核心指令速览
| 指令 |
作用 |
示例 |
x-data |
定义组件作用域和数据 |
x-data="{ open: false }" |
x-model |
双向数据绑定 |
<input x-model="name"> |
@click / x-on: |
事件绑定 |
<button @click="open = !open"> |
x-show |
条件显示(display:none) |
<div x-show="open"> |
x-if |
条件渲染(移除DOM) |
<template x-if="open"> |
x-for |
循环渲染 |
<template x-for="item in items"> |
x-text |
输出文本(防XSS) |
<span x-text="name"> |
x-html |
输出HTML(慎用) |
<div x-html="raw"> |
x-bind: |
属性绑定 |
<img :src="url"> |
x-effect |
监听变化执行回调 |
<div x-effect="fetch(url)"> |
x-ref |
组件内部引用 |
<div x-ref="myDiv"> |
x-init |
组件初始化钩子 |
<div x-init="fetchData()"> |
三、魔法方法
<!-- $store - 全局状态 -->
<span x-text="$store.theme.darkMode"></span>
<!-- $dispatch - 事件分发 -->
<button @click="$dispatch('custom-event', { data: 42 })">
<!-- $watch - 监听数据变化 -->
<div x-init="$watch('count', val => console.log(val))">
<!-- $refs - 访问 DOM -->
<div x-ref="input" x-init="$refs.input.focus()">
<!-- $nextTick - 下一个 tick -->
<div x-init="$nextTick(() => console.log('DOM 已更新'))">
<!-- $el - 当前 DOM 元素 -->
<div x-init="console.log($el.tagName)">
四、分步实战:Todo 应用
第一步:基础结构
<div x-data="{
todos: [],
newTodo: '',
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({ id: Date.now(), text: this.newTodo, done: false });
this.newTodo = '';
}
},
removeTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
}
}">
第二步:表单与列表
<form @submit.prevent="addTodo">
<input x-model="newTodo" placeholder="输入任务..." />
<button type="submit">添加</button>
</form>
<template x-for="todo in todos" :key="todo.id">
<li>
<span :class="{ 'line-through': todo.done }" x-text="todo.text"></span>
<button @click="todo.done = !todo.done">✓</button>
<button @click="removeTodo(todo.id)">✕</button>
</li>
</template>
第三步:持久化到 localStorage
<div x-data="{ todos: [] }"
x-init="todos = JSON.parse(localStorage.getItem('todos') || '[]');
$watch('todos', val => localStorage.setItem('todos', JSON.stringify(val)))">
五、全局状态(Alpine.store)
<script defer src="...alpine.min.js"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('theme', {
dark: false,
toggle() { this.dark = !this.dark; }
});
});
</script>
<!-- 任意组件中使用 -->
<div :class="$store.theme.dark ? 'bg-black' : 'bg-white'">
<button @click="$store.theme.toggle()">切换主题</button>
</div>
六、最佳实践
- x-data 对象保持简单——复杂逻辑抽到外部 JS 文件
- x-if 用 template 标签——避免残留 DOM 节点
- 列表必加 :key——性能关键
- $dispatch 实现父子组件通信——松耦合
七、思考题
- Alpine.js 和 Vue.js 的
v-model 有什么区别?
- 如何在 Alpine 组件间共享状态?(提示:$store / $dispatch / 自定义事件)
- 为什么 Alpine.js 适合"渐进增强"而 React 不太适合?
进阶篇:依赖注入与中间件深度解析.md
Alpine.js 入门教程:给你的 HTML 添加超能力
一、Alpine.js 是什么?
Alpine.js 的口号是"像写 Tailwind CSS 一样写 JavaScript"。它让你用简单的 HTML 属性为页面添加交互,无需构建工具、无需虚拟 DOM,只有 15 个属性 + 6 个魔法方法 + 1 个全局状态。非常适合后端开发者给模板引擎(Laravel Blade、Django Templates、Rails ERB 等)添加交互,或为静态网站注入轻量级动态行为。
二、核心指令速览
| 指令 |
作用 |
示例 |
x-data |
定义组件作用域和数据 |
x-data="{ open: false }" |
x-model |
双向数据绑定 |
<input x-model="name"> |
@click / x-on: |
事件绑定 |
<button @click="open = !open"> |
x-show |
条件显示(display:none) |
<div x-show="open"> |
x-if |
条件渲染(移除DOM) |
<template x-if="open"> |
x-for |
循环渲染 |
<template x-for="item in items"> |
x-text |
输出文本(防XSS) |
<span x-text="name"> |
x-html |
输出HTML(慎用) |
<div x-html="raw"> |
x-bind: |
属性绑定 |
<img :src="url"> |
x-effect |
监听变化执行回调 |
<div x-effect="fetch(url)"> |
x-ref |
组件内部引用 |
<div x-ref="myDiv"> |
x-init |
组件初始化钩子 |
<div x-init="fetchData()"> |
三、魔法方法
<!-- $store - 全局状态 -->
<span x-text="$store.theme.darkMode"></span>
<!-- $dispatch - 事件分发 -->
<button @click="$dispatch('custom-event', { data: 42 })">
<!-- $watch - 监听数据变化 -->
<div x-init="$watch('count', val => console.log(val))">
<!-- $refs - 访问 DOM -->
<div x-ref="input" x-init="$refs.input.focus()">
<!-- $nextTick - 下一个 tick -->
<div x-init="$nextTick(() => console.log('DOM 已更新'))">
<!-- $el - 当前 DOM 元素 -->
<div x-init="console.log($el.tagName)">
四、分步实战:Todo 应用
第一步:基础结构
<div x-data="{
todos: [],
newTodo: '',
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({ id: Date.now(), text: this.newTodo, done: false });
this.newTodo = '';
}
},
removeTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
}
}">
第二步:表单与列表
<form @submit.prevent="addTodo">
<input x-model="newTodo" placeholder="输入任务..." />
<button type="submit">添加</button>
</form>
<template x-for="todo in todos" :key="todo.id">
<li>
<span :class="{ 'line-through': todo.done }" x-text="todo.text"></span>
<button @click="todo.done = !todo.done">✓</button>
<button @click="removeTodo(todo.id)">✕</button>
</li>
</template>
第三步:持久化到 localStorage
<div x-data="{ todos: [] }"
x-init="todos = JSON.parse(localStorage.getItem('todos') || '[]');
$watch('todos', val => localStorage.setItem('todos', JSON.stringify(val)))">
五、全局状态(Alpine.store)
<script defer src="...alpine.min.js"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('theme', {
dark: false,
toggle() { this.dark = !this.dark; }
});
});
</script>
<!-- 任意组件中使用 -->
<div :class="$store.theme.dark ? 'bg-black' : 'bg-white'">
<button @click="$store.theme.toggle()">切换主题</button>
</div>
六、最佳实践
- x-data 对象保持简单——复杂逻辑抽到外部 JS 文件
- x-if 用 template 标签——避免残留 DOM 节点
- 列表必加 :key——性能关键
- $dispatch 实现父子组件通信——松耦合
七、思考题
- Alpine.js 和 Vue.js 的
v-model 有什么区别?
- 如何在 Alpine 组件间共享状态?(提示:$store / $dispatch / 自定义事件)
- 为什么 Alpine.js 适合"渐进增强"而 React 不太适合?
01-aspnet-core-web-api入门.md
C# 入门篇:ASP.NET Core Web API 实战
背景
ASP.NET Core 是微软开源的跨平台 Web 框架,在 TechEmpower 基准测试中常年 Top 10。本教程带你构建一个完整的图书管理 Web API。
核心概念
1. 中间件管道
请求依次经过每个中间件,构成管道(Pipeline)。内置中间件:异常处理、静态文件、路由、认证、CORS。
var app = builder.Build();
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
2. 依赖注入(DI)
.NET 内置 IoC 容器。注册服务后,通过构造函数自动注入。
builder.Services.AddScoped<IBookService, BookService>();
builder.Services.AddDbContext<AppDbContext>(...);
public class BooksController : ControllerBase
{
private readonly IBookService _service;
public BooksController(IBookService service) => _service = service;
}
3. 配置系统
支持 appsettings.json、环境变量、命令行参数,自动合并。
{
"ConnectionStrings": {
"Default": "Data Source=app.db"
}
}
var connStr = builder.Configuration.GetConnectionString("Default");
分步操作
第一步:创建项目
dotnet new webapi -n BookApi
cd BookApi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
第二步:定义模型与 DbContext
public class Book
{
public int Id { get; set; }
public string Title { get; set; } = "";
public string Author { get; set; } = "";
public decimal Price { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Book> Books => Set<Book>();
public AppDbContext(DbContextOptions<AppDbContext> opts) : base(opts) {}
}
第三步:创建 Service 层
public interface IBookService
{
Task<List<Book>> GetAllAsync();
Task<Book?> GetByIdAsync(int id);
Task<Book> CreateAsync(Book book);
Task<bool> UpdateAsync(int id, Book book);
Task<bool> DeleteAsync(int id);
}
第四步:编写 Controller
利用 [ApiController] 属性自动处理模型验证错误。
第五步:数据库迁移
dotnet ef migrations add InitialCreate
dotnet ef database update
思考题
AddScoped、AddSingleton、AddTransient 三种生命周期有什么区别?
- 中间件顺序为什么重要?如果
UseAuthentication 放在 UseAuthorization 之后会怎样?
- Entity Framework Core 的
AsNoTracking() 什么场景使用?
02-efcore-异步编程与性能.md
C# 进阶篇:EF Core、异步编程与性能优化
背景
Entity Framework Core 是 .NET 最流行的 ORM。掌握其高级用法与异步编程模式,是构建高性能 .NET 应用的基础。
核心概念
1. Task-based 异步模式
public IActionResult GetBooks()
{
var books = _context.Books.ToList(); // 阻塞线程
return Ok(books);
}
public async Task<IActionResult> GetBooksAsync()
{
var books = await _context.Books.ToListAsync(); // 释放线程
return Ok(books);
}
2. EF Core 查询优化
var books = await _context.Books.ToListAsync();
foreach (var b in books) Console.WriteLine(b.Author.Name);
var books = await _context.Books
.Include(b => b.Author)
.ToListAsync();
var result = await _context.Books
.Select(b => new { b.Title, AuthorName = b.Author.Name })
.ToListAsync();
3. 并发处理
public class Book
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
}
4. 原生 SQL 和存储过程
var books = await _context.Books
.FromSqlRaw("SELECT * FROM Books WHERE Price > {0}", minPrice)
.ToListAsync();
var result = await _context.Database
.ExecuteSqlRawAsync("EXEC UpdateBookPrice @id, @price",
new SqliteParameter("@id", id),
new SqliteParameter("@price", price));
5. CancellationToken
[HttpGet]
public async Task<IActionResult> GetBooks(CancellationToken ct)
{
var books = await _context.Books.ToListAsync(ct);
return Ok(books);
}
性能检查清单
| 优化项 |
方法 |
| 查询性能 |
投影(Select)、分页、索引、无跟踪查询 |
| 内存 |
AsNoTracking() 只读查询、流式枚举 |
| 并发 |
连接池(AddDbContextPool)、限流中间件 |
| 序列化 |
System.Text.Json 源码生成(AOT) |
| 启动 |
ReadyToRun、Native AOT 编译 |
思考题
ConfigureAwait(false) 在 ASP.NET Core 中还需要吗?为什么?
- EF Core 的
IQueryable 和 IEnumerable 有什么区别?为什么要把 IQueryable 传给 OData?
- Native AOT 有什么限制?哪些场景不适合 AOT 编译?
C# ASP.NET Core 毕设入门教程.md
C# / ASP.NET Core 毕设入门教程
前言
C# 在毕设中的三大主战场:ASP.NET Core Web API 后端、WPF/WinForms 桌面应用、Unity 游戏开发。本教程聚焦毕设最常用的 ASP.NET Core + EF Core 后端开发。
第一章:快速搭建 Web API 项目
三板斧创建项目
dotnet new webapi -n MyProject
cd MyProject
dotnet run
项目结构
MyProject/
├── Controllers/ # API 控制器
├── Models/ # 数据模型
├── Services/ # 业务逻辑
├── Data/ # DbContext
├── appsettings.json # 配置文件
└── Program.cs # 入口
第二章:构建一个完整的 CRUD API
Model
public class TodoItem
{
public int Id { get; set; }
public string Title { get; set; } = "";
public bool IsCompleted { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
DbContext
public class AppDbContext : DbContext
{
public DbSet<TodoItem> Todos => Set<TodoItem>();
public AppDbContext(DbContextOptions<AppDbContext> o) : base(o) {}
}
Controller(最小 API 风格)
app.MapGet("/todos", async (AppDbContext db) => await db.Todos.ToListAsync());
app.MapPost("/todos", async (TodoItem todo, AppDbContext db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todos/{todo.Id}", todo);
});
app.MapDelete("/todos/{id}", async (int id, AppDbContext db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
});
第三章:毕设常用模式
三层架构
Controller → Service → Repository → DbContext
JWT 认证(毕设加分项)
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new() {
ValidateIssuer = true,
ValidateAudience = true,
};
});
全局异常处理
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{\"error\":\"服务器内部错误\"}");
});
});
第四章:毕设答辩要点
演示流程建议
- 启动项目
dotnet run
- 打开 Swagger 页面展示 API 列表
- 用 Swagger 调几个接口展示 CRUD
- 展示数据库中的数据(SQLite 用 DB Browser 打开)
常见答辩问题
- "为什么选 ASP.NET Core 而不是 Spring Boot?" → 强调 .NET 生态、LINQ 开发效率、学习曲线
- "数据库用的什么?" → SQLite 零部署,适合毕设
- "安全性怎么保证?" → JWT 认证、参数校验、CORS 配置
思考题
- RESTful API 的设计原则有哪些?你的接口是否符合?
async/await 在 ASP.NET Core 中的作用是什么?
- EF Core 的
Include() 和 ThenInclude() 解决了什么问题?