普通视图

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

北京宅地上新,海淀再出“王炸”

2025年11月22日 23:36

11月21日晚间,北京规自委官网发布了2025年第九轮拟供应商品住宅用地清单,共涉及6宗地,土地面积约20公顷,建筑规模约36万平方米,上述用地拟于近期供应。

从区域分布来看,中心城区4宗(朝阳区1宗、海淀区1宗、石景山区2宗),通州区和密云区各1宗。

数据来自北京规自委官网

6宗地中,最引人注目的当属海淀朱房地块。

朱房二期城中村改造项目HD00-0907-0001、0002地块,位于海淀区东升镇。地块性质为纯住宅用地,用地规模5.19公顷,规划地上建筑面积7.8万平方米。

该地块临近北五环,西侧为京藏高速,最近地铁站为8号线永泰庄站,距离大约1.5公里左右。地块西侧3公里左右有海淀今年两大热盘:臻澐和建发海晏,两项目网签价在13万/㎡左右。其所属的清河板块,二手价在6.7万/㎡左右。

朱房地块位置

周边生活配套较为丰富,距离清河万象汇、西三旗万象汇等大型商业配套在3公里左右,奥林匹克森林公园北园、东小口城市公园等休闲配套也环伺其左右。

虽不如西二旗的产业优势,但该地块北侧1公里外的中关村东升科技园,也聚集着数百家高新技术企业。并且据近日消息报道,字节跳动已租下东升科技园二期1号楼,字节旗下包括抖音内容运营、研判治理约2000名员工预计在明年3月搬迁至此。这也或将为未来朱房项目去化提供购买力支撑。

另一亮点地块则是朝阳区东坝1105-01地块,地块性质为R2二类居住用地,用地规模2.55公顷,规划地上建筑面积5.11万平方米。

东坝地块位置

地块邻近地铁3号线东坝站、东坝南站、姚家园站。生活氛围浓厚,万达广场、金隅嘉品Mall、北京市第一中西医结合医院(东坝院区)、京城体育休闲公园等生活配套设施分布左右。

东坝二手房价格在4.5万/㎡,地块周边二手房包括金隅汇景苑4.7万/㎡左右,首开·常青藤6.6万/㎡左右,保利首开天誉稍微,约为9.4万/㎡等。

石景山区涉及2宗宅地,都在古城街道。一宗是1609街区地块,邻近地铁11号线新首钢站,周边有六工汇购物广场、金安环宇荟购物中心、首钢园、北京冬奥公园等生活配套设施。

1609街区地块位置

 一宗是1610街区地块,邻近地铁1号线、6号线、S1线换乘站苹果园站、11号线北辛安站。周边有喜隆多购物中心、京西大悦城、古城公园、首钢园等生活配套设施。

1610街区地块位置

此外,通州区新上宅地处在六环外的宋庄镇,北侧是宋庄画家村,生活配套还算可以,西北侧不远是宋庄艺术小镇和安贞医院,北侧也有宋庄首开LONG街等。

通州宋庄地块位置

密云新地块,则位于密云区0104街区,周边有北京密云万象汇、燕赛奥特莱斯购物中心、国泰百货(密云店)、北京大学第一医院密云院区、奥林公园等生活配套设施。

密云地块位置

CSS 属性值 initial、unset 和 revert 的解析

2025年11月22日 22:17

大家好,我是前端架构师,关注微信公众号【程序员大卫】,免费领取前端最新面试资料。

背景

在 CSS 中,管理属性值和继承行为是创建健壮、可维护样式的基础。除了常见的具体数值(如 16px#333)或通用值(如 inherit)之外,CSS 还提供了一些特殊的关键字,它们能重置或恢复属性值到其“默认”或前一个状态。

其中,initialunsetrevert 是三个功能强大且容易混淆的关键字。理解它们的区别,能帮助您更好地控制样式,尤其是在处理第三方组件或大型应用中的样式冲突时。

核心区别一览

关键字 作用 行为细节 适用场景
initial 重置为初始值 将属性重置为其在 CSS 规范中定义的初始值(就像该属性从未被设置过一样)。 确保属性值是浏览器默认的“出厂设置”,不考虑继承。
unset 重置为未设置值 如果该属性是可继承的,则表现为 inherit(继承父元素的值)。
如果该属性是不可继承的,则表现为 initial
恢复属性到其自然的(默认的)继承行为。
revert 重置为上一个级联值 将属性重置为用户代理样式表(浏览器默认样式)、用户样式表作者样式表中应用过的上一个值。它会撤销当前样式块的更改,恢复到级联链中更早的值。 撤销对属性的特定覆盖,恢复到浏览器或用户定义的样式,常用于恢复浏览器默认样式。

关键字详解与示例

为了更好地理解,我们主要关注 color(可继承)和 border(不可继承)这两个属性。

1. initial:回到起点

initial 总是将属性值设置为 CSS 规范中定义的初始值。这个值是固定的,并且与该元素是否继承了父元素的值无关

示例:

/* 初始设置 */
div {
  color: blue;
  border: 1px solid black;
}

/* 应用 initial */
.initial-demo {
  /* color 是可继承属性,但 initial 强制设为初始值 (通常是 black) */
  color: initial;
  /* border 是不可继承属性,initial 强制设为初始值 (none) */
  border: initial;
}
元素 父元素 div 的样式 .initial-demo 的样式 最终值
color blue initial black (初始值)
border 1px solid black initial none (初始值)

2. unset:恢复自然状态

unset 是一个“智能”的重置。它根据属性的可继承性来决定行为:

  • 可继承属性: 表现为 inherit
  • 不可继承属性: 表现为 initial

示例:

/* 初始设置 */
div {
  color: blue;
  border: 1px solid black;
}

/* 应用 unset */
.unset-demo {
  /* color 是可继承属性,表现为 inherit */
  color: unset;
  /* border 是不可继承属性,表现为 initial */
  border: unset;
}
元素 父元素 div 的样式 .unset-demo 的样式 最终值
color blue unset \rightarrow inherit blue (继承自父元素)
border 1px solid black unset \rightarrow initial none (初始值)

3. revert:撤销作者样式

revert 是最独特的。它沿着 CSS 级联(Cascading)链向上查找,恢复到上一个被应用的值。它本质上是撤销了当前样式表中的属性定义,恢复到:

  1. 用户样式(如果用户设置了)。
  2. 浏览器默认样式(用户代理样式表)。
  3. 如果以上都没有,则恢复到 unset 的行为。

它主要用于恢复浏览器默认样式,例如,您不想让某个 <h1> 标签应用您的全局字体样式,希望它用回浏览器默认的粗体大字。

示例:

假设浏览器默认样式是:h1 { font-weight: bold; }

/* 作者样式表 (您的代码) */
h1 {
  /* 覆盖了浏览器默认样式 */
  font-weight: normal; 
}

.revert-demo {
  /* 撤销了上方的 font-weight: normal; */
  font-weight: revert;
}

在这个例子中,.revert-demo 元素会将 font-weight 恢复到 浏览器默认值 (bold),因为它撤销了作者样式表中的 normal 声明。

✅ 总结与应用建议

关键字 记忆点 应用场景
initial “绝对初始” 确保属性值是其 CSS 规范定义的原始默认值,不考虑上下文。
unset “智能重置” 恢复属性的自然继承行为(可继承则 inherit,不可继承则 initial)。
revert “撤销” 撤销作者样式,恢复到浏览器默认或用户样式。常用于希望保持浏览器默认外观的元素。

什么时候用哪个?

  • 如果您想确保一个属性值是固定的、明确的 CSS 初始值,使用 initial
  • 如果您想让一个属性遵循其固有的继承规则(即,可继承的就继承,不可继承的就使用初始值),使用 unset
  • 如果您想撤销您的样式对某个元素默认外观的覆盖,恢复到浏览器提供的默认样式,使用 revert
昨天 — 2025年11月22日首页

鸭绒价格近期仍在高位 中下游追单采购意愿活跃

2025年11月22日 19:00
财联社记者从业内了解到,羽绒服目前进入消费旺季,“(白鸭绒价)10月份几乎是一天一个价,这段时间没有在涨,维持在高位。”尽管多数企业认为当前的原料价格偏高,但由于前期备货不足,促使中下游的追单采购意愿保持活跃。上市公司方面,记者采访获悉华英农业羽绒加工产能满产;古麒绒材当前整体羽绒产能2288吨,公司具备一定数量的鸭绒和鹅绒库存,随着羽绒价格上涨,公司库存相应增值。(财联社)

李斌:乐道也将有右舵车型

2025年11月22日 18:57
36氪获悉,11月21日,蔚来创始人李斌表示:乐道接下来也会有右舵车型,它需要时间。 今年11月18日,蔚来旗下高端小车品牌萤火虫的右舵车型正式量产,首批将发运至新加坡市场。未来,乐道也将推出右舵车型。

北京“十四五”期间企业直接融资超5.6万亿元

2025年11月22日 18:45
据新华社,记者从日前举行的首都“十四五”规划高质量收官系列主题新闻发布会金融业发展成就专场上获悉,“十四五”期间,北京地区多元化融资渠道进一步拓宽,企业实现直接融资超5.6万亿元。金融“五篇大文章”合计贷款余额6.8万亿元,高于人民币各项贷款增速2.1个百分点,为首都经济持续向好和高质量发展营造良好的金融环境。(新华网)

宁德时代与上汽商用车达成深度战略合作

2025年11月22日 18:30
36氪获悉,11月22日,据宁德时代微信公众号,在第21届广州国际车展上,宁德时代与上汽商用车正式宣布达成深度战略合作。双方将围绕“技术共研、生态共建、全球共进”三大方向,在商用车新能源化、智能化领域全面深化合作,共同构建“车、电、站、云”一体化的智慧换电生态与绿色运力体系。

Ultracite:为 AI 时代打造的零配置代码规范工具

作者 JinSo
2025年11月22日 18:26

Ultracite 是什么?

Ultracite 是一个高度固定化、零配置的代码检查和格式化工具。它基于高性能的 Biome 构建,适用于所有前端项目,无论你使用 React、Vue、Angular 还是原生 JavaScript,都能让你和 AI 编程助手编写出一致且高质量的代码。

核心理念:约定优于配置

不同于 ESLint + Prettier 需要大量配置,Ultracite 采用了极简主义设计理念:

  • 零配置:开箱即用,无需编写任何配置文件
  • 固定规则:精心挑选的规则集,避免无谓的选择困扰
  • 统一标准:整个团队和 AI 助手都遵循相同的代码规范

快速开始

初始化

# 在项目中初始化 Ultracite
pnpm dlx ultracite init

Ultracite 提供了交互式的安装体验,让你根据项目需求选择功能:

❯ pnpm dlx ultracite init
┌
888     888 888    88888888888 8888888b.         d8888  .d8888b. 8888888 88888888888 8888888888
888     888 888        888     888   Y88b       d88888 d88P  Y88b  888       888     888
888     888 888        888     888    888      d88P888 888    888  888       888     888
888     888 888        888     888   d88P     d88P 888 888         888       888     8888888
888     888 888        888     8888888P"     d88P  888 888         888       888     888
888     888 888        888     888 T88b     d88P   888 888    888  888       888     888
Y88b. .d88P 888        888     888  T88b   d8888888888 Y88b  d88P  888       888     888
 "Y88888P"  88888888   888     888   T88b d88P     888  "Y8888P" 8888888     888     8888888888

│
●  Detected lockfile, using pnpm
│
◇  Remove existing formatters/linters (recommended for clean migration)?
│  Remove ESLint (dependencies, config files, VS Code settings)
│
◇  Which frameworks are you using (optional)?
│  React
│
◇  Which editors do you want to configure (recommended)?
│  VSCode / Cursor / Windsurf
│
◇  Which agents do you want to enable (optional)?
│  Cursor
│
◇  Which agent hooks do you want to enable (optional)?
│  Cursor
│
◇  Would you like any of the following (optional)?
│  Husky pre-commit hook, Lefthook pre-commit hook, Lint-staged
│
◑  ...省略安装步骤
◆  Successfully initialized Ultracite configuration!

初始化完成后,Ultracite 会根据你的选择自动完成以下配置:

  • 安装必要的依赖包
  • 生成编辑器配置文件
  • 创建 AI 代码规范提示词(这是 Ultracite 的核心特性之一)
  • 配置 Git Hooks 工具(Husky、Lefthook、Lint-staged)
  • 更新 package.json 脚本
  • 生成 biome.json 配置文件

关于 Git Hooks 工具的详细介绍,可以参考我之前的文章:在 Monorepo 中对代码进行规范(husky + lint-staged)

基本使用

# 检查代码问题
npx ultracite check

# 自动修复和格式化代码
npx ultracite fix

AI 友好特性

这是 Ultracite 的一大亮点 —— 它专门为 AI 编程时代设计,让 AI 助手也能遵循项目的代码规范。

AI 提示词集成

当你选择启用 AI agent(如 Cursor)时,Ultracite 会生成一份详细的代码规范提示词文件 .cursor/rules/ultracite.mdc。这份文件包含了完整的编码规范,涵盖:

  • 类型安全规范:要求使用明确的类型定义,避免 any 类型
  • 现代语法规范:优先使用箭头函数、可选链、解构赋值等现代特性
  • 异步编程规范:正确使用 async/await,妥善处理错误
  • React 最佳实践:函数组件、Hooks 规则、无障碍访问等
  • 代码组织原则:函数复杂度控制、早期返回、关注点分离

有了这份提示词,AI 助手生成的代码会自动遵循这些规范,大大减少了代码审查的工作量。

Cursor Hooks 集成

对于 Cursor 用户,Ultracite 还会生成 hooks 配置(.cursor/hooks.json):

{
  "version": 1,
  "hooks": {
    "afterFileEdit": [
      {
        "command": "npx ultracite fix"
      }
    ]
  }
}

这个配置实现了"保存即格式化"的效果 —— 每次你或 AI 编辑文件后,代码都会自动格式化,保持整个项目的一致性。

Git Hooks 集成

Ultracite 支持主流的 Git Hooks 工具,确保提交的代码都经过检查和格式化。

Husky 集成

生成的 .husky/pre-commit 文件会在提交前自动运行代码检查:

#!/bin/sh
# Exit on any error
set -e

# Check if there are any staged files
if [ -z "$(git diff --cached --name-only)" ]; then
  echo "No staged files to format"
  exit 0
fi

# Store the hash of staged changes to detect modifications
STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)

# Save list of staged files (handling all file states)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
PARTIALLY_STAGED=$(git diff --name-only)

# Stash unstaged changes to preserve working directory
git stash push --quiet --keep-index --message "pre-commit-stash" || true
STASHED=$?

# Run formatter on the staged files
pnpm dlx ultracite fix
FORMAT_EXIT_CODE=$?

# Restore working directory state
if [ $STASHED -eq 0 ]; then
  # Re-stage the formatted files
  if [ -n "$STAGED_FILES" ]; then
    echo "$STAGED_FILES" | while IFS= read -r file; do
      if [ -f "$file" ]; then
        git add "$file"
      fi
    done
  fi

  # Restore unstaged changes
  git stash pop --quiet || true
fi

# Check if staged files actually changed
NEW_STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
if [ "$STAGED_HASH" != "$NEW_STAGED_HASH" ]; then
  echo "✨ Files formatted by Ultracite"
fi

exit $FORMAT_EXIT_CODE

这个脚本会智能处理部分暂存的文件,确保只格式化你要提交的代码。

Lefthook 集成

如果你选择使用 Lefthook,会生成 lefthook.yml 配置:

pre-commit:
  jobs:
    - run: pnpm dlx ultracite fix
      glob:
        - "*.js"
        - "*.jsx"
        - "*.ts"
        - "*.tsx"
        - "*.json"
        - "*.jsonc"
        - "*.css"
      stage_fixed: true

Lefthook 的优势在于并行执行和更灵活的配置选项。

Lint-staged 集成

配合 lint-staged 可以只处理暂存的文件,在 package.json 中会添加:

"lint-staged": {
  "*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}": [
    "pnpm dlx ultracite fix"
  ]
}

VSCode 深度集成

Ultracite 会自动为 VSCode(包括 Cursor、Windsurf)生成配置,实现保存时自动格式化:

// .vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[json]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[css]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[graphql]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "emmet.showExpandedAbbreviation": "never",
  "editor.codeActionsOnSave": {
    "source.fixAll.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

MCP(Model Context Protocol)支持

MCP 是一个开放标准,让 Claude、Cursor 等 AI 工具能够安全地连接外部数据源和系统。可以把它理解为一个"万能遥控器",让 AI 工具能够访问真实世界的数据和功能。Ultracite 通过支持 MCP 来增强你的 AI 开发工作流。

安装配置

  1. 选择你的 AI 工具

    确保你使用的 AI 开发工具支持 MCP:

    • Claude Desktop(免费,推荐初学者使用)
    • Cursor(AI 驱动的代码编辑器)
    • Windsurf by Codeium(AI 开发平台)
    • 其他支持 MCP 的工具
  2. 找到配置文件位置

    根据你的 AI 工具,需要编辑对应的配置文件:

    • Claude Desktop:

      • macOS: ~/Library/Application\ Support/Claude/claude_desktop_config.json
      • Windows: %APPDATA%\Claude\claude_desktop_config.json
    • Cursor.cursor/mcp.json

    • Windsurf.codeium/windsurf/mcp_config.json

    • 其他工具: 查看对应工具的 MCP 文档

  3. 添加 Ultracite 配置

    将以下配置复制到你的 MCP 配置文件中:

    {
      "mcpServers": {
        "ultracite": {
          "command": "npx",
          "args": [
            "-y",
            "mcp-remote",
            "<https://www.ultracite.ai/api/mcp/mcp>"
          ]
        }
      }
    }
    
  4. 重启 AI 工具

    关闭并重新打开你的 AI 应用,让配置生效。

  5. 验证连接

    通过询问你的 AI 助手来测试集成是否成功:

    "Ultracite 有哪些可用的规则?"

    如果配置成功,AI 应该能够列出并解释 Ultracite 的规则!

从 ESLint + Prettier 迁移

迁移过程非常简单:

  1. 运行 pnpm dlx ultracite init
  2. 选择移除现有的 ESLint 和 Prettier 配置
  3. 更新 package.json 中的脚本命令
  4. 删除旧的配置文件

Ultracite 的规则集已经涵盖了大多数常用的 ESLint 和 Prettier 规则,你几乎不需要任何额外配置。

总结

Ultracite 代表了代码质量工具的新方向:简单、快速、AI 友好。它特别适合:

  • 使用 AI 编程的开发者:通过提示词和 hooks 确保 AI 生成高质量代码
  • 追求效率的团队:零配置设计,减少工具链维护成本
  • 任何前端项目:无论使用什么框架,都能获得一致的代码质量
  • Monorepo 项目:原生支持,配置简单

相比传统的 ESLint + Prettier 组合,Ultracite 提供了更快的性能、更简单的配置、更统一的工具链,以及原生的 AI 编程支持。在 AI 辅助编程越来越普及的今天,选择一个 AI 友好的代码规范工具,能让你的开发效率更上一层楼。

《大型网络平台个人信息保护规定(征求意见稿)》公开征求意见

2025年11月22日 18:15
36氪获悉,国家互联网信息办公室、公安部就《大型网络平台个人信息保护规定(征求意见稿)》公开征求意见。征求意见稿提出,大型网络平台服务提供者应当将在中华人民共和国境内运营中收集和产生的个人信息存储在境内。确需向境外提供的,应当符合国家数据出境安全管理有关规定。大型网络平台服务提供者应当按照国家有关规定,健全个人信息出境安全相关技术和管理措施,及时防范、处置个人信息违法违规出境安全风险和威胁。

我国卫星物联网业务商用试验正式启动

2025年11月22日 18:00
11月22日,在2025中国“5G+”工业互联网大会上,工业和信息化部对外宣布,我国卫星物联网业务商用试验正式启动。此次商用试验期为两年。目标通过开展商用试验,丰富卫星通信市场供给,激发市场主体活力,提升行业服务能力,建立安全监管体系,形成可复制可推广的经验和模式,支撑商业航天、低空经济等新兴产业安全健康发展,服务构建新发展格局。(第一财经)

为什么有些人边框不用border属性

作者 爆浆麻花
2025年11月22日 17:46

1) border 会改变布局(占据空间)

border 会参与盒模型,增加元素尺寸。

例如,一个宽度 200px 的元素加上 border: 1px solid #000,实际宽度会变成:

200 + 1px(left) + 1px(right) = 202px

如果不想影响布局,就很麻烦。

使用 box-shadow: 0 0 0 1px #000不会改变大小,看起来像 border,但不占空间。


2) border 在高 DPI 设备上容易出现“模糊/不齐”

特别是 0.5px border(发丝线),在某些浏览器上有锯齿、断线。

transform: scale(0.5) 或伪元素能做更稳定的发丝线。


3) border 圆角 + 发丝线 常出现不规则效果

border + border-radius 在不同浏览器的渲染不一致,容易出现不均匀、颜色不一致的问题。

outline / box-shadow 圆角更稳定。


4) border 不适合做阴影/多层边框

如果你需要两层边框:

双层边框用 border 很难做

而用:

box-shadow: 0 0 0 1px #333, 0 0 0 2px #999;

非常简单。


5) border 和背景裁剪一起用时容易出 bug

比如 background-clipoverflow: hidden 配合 border 会出现背景被挤压、不应该被裁剪却裁剪等问题。


6) hover/active 等状态切换时会“跳动”

因为 border 会改变元素大小。

例子:

.btn { border: 0; }
.btn:hover { border: 1px solid #000; }

鼠标移上去会抖动,因为尺寸变大了。

box-shadow 的话就不会跳。

总结

边框可以分别使用border、outline、box-shadow三种方式去实现,其中outline、box-shadow不会像border一样占据空间。而box-shadow可以用来解决两个元素相邻时边框变宽的问题。所以outline和box-shadow的兼容性和灵活性会更好一点

Next.js 从入门到精通(1):项目架构与 App Router—— 文件系统路由与目录结构全解析

2025年11月22日 17:44

大家好,我是jobleap.cn的小九。

如果你熟悉Python和FastAPI,会很容易接受Next.js的设计思路——两者都遵循“标准驱动、类型安全、高效务实”的核心哲学。对于FastAPI开发者来说,学习Next.js的关键认知转变有两点:一是Next.js的App Router并非单纯的前端UI库,而是“搭载React渲染引擎的Web服务器”,组件默认在服务端运行(类似Python视图函数);二是路由定义方式从“代码装饰器”转向“文件系统”,这种约定优于配置的设计,能让UI、布局与数据逻辑实现物理聚合,大幅提升开发效率。

1. 核心认知:文件系统 = URL路由

FastAPI通过@app.get("/path")这类装饰器显式定义路由,而Next.js App Router的核心规则是:文件夹结构直接映射为URL路径,无需额外配置路由表。

FastAPI与Next.js路由映射对比

FastAPI 装饰器写法 Next.js App Router 文件结构 说明
@app.get("/") app/page.tsx 网站首页(根路由)
@app.get("/posts") app/posts/page.tsx 帖子列表页(一级路由)
@app.get("/posts/{id}") app/posts/[id]/page.tsx 帖子详情页(动态路由,[id]为路径参数)
@app.get("/settings/profile") app/settings/profile/page.tsx 个人资料页(嵌套路由)

这里有个关键约定:page.tsx文件会被识别为可访问的路由终点。你可以在路由文件夹(如app/posts/)中自由放置工具函数(utils.ts)、UI组件(PostCard.tsx)等文件,它们不会被解析为路由。这种“相关资源就近存放”的方式称为Colocation(资源共存),是Next.js 13+的核心改进,能避免文件分散导致的查找成本,让项目结构更清晰。

2. 实战初始化:搭建社区项目骨架

接下来通过命令行初始化项目,全程适配Python开发者的工程化习惯(类型安全、目录分离等):

步骤1:创建Next.js项目

打开终端,执行以下命令创建名为next-community的项目(使用最新稳定版Next.js):

npx create-next-app@latest next-community

步骤2:交互式配置选择(适配Python开发者)

在命令行交互中,推荐如下选择,兼顾类型安全与工程化规范:

  • TypeScript: Yes(类似Python的Type Hints,提供更严格的类型校验,减少bug)
  • ESLint: Yes(代码规范检查工具,保持代码风格一致)
  • Tailwind CSS: Yes(原子化CSS框架,后续章节详解,高效编写样式)
  • src/ directory: Yes(将源码与配置文件分离,类似Python项目的src目录规范)
  • App Router: Yes(本章核心,必须选择)
  • Import alias: Yes(默认@/*别名,简化文件引用路径,避免相对路径混乱)

步骤3:启动项目

配置完成后,进入项目并启动开发服务器:

cd next-community  # 进入项目目录
code .             # 用VS Code打开(可选,替换为你的编辑器)
npm run dev        # 启动开发服务器,默认端口3000

访问http://localhost:3000,即可看到Next.js默认首页,接下来我们将基于这个骨架搭建社区功能。

3. 目录结构解析:App Router的核心约定

项目初始化后,重点关注src/app目录(对应FastAPI的“视图层+路由层”),整体目录结构如下(仅保留核心文件):

next-community/
├── next.config.mjs       # Next.js全局配置(类似FastAPI的config.py)
├── tailwind.config.ts    # Tailwind CSS配置
├── tsconfig.json         # TypeScript配置(类似Python的pyproject.toml类型配置)
└── src/
    └── app/              # 路由与页面核心目录
        ├── layout.tsx    # 全局布局(类似Jinja2的base.html模板)
        ├── page.tsx      # 首页内容(根路由对应的页面)
        ├── globals.css   # 全局样式文件
        └── fonts/        # 字体资源目录

核心约定文件详解(无需死记,理解逻辑即可)

Next.js通过固定文件名实现“约定优于配置”,以下4个文件是开发核心,且与FastAPI的开发逻辑高度呼应:

1. page.tsx:路由页面入口

  • 作用:路由的最终展示页面,默认是服务端组件(RSC),可直接在组件内编写数据获取逻辑(类似FastAPI的路径操作函数,既处理逻辑又返回响应)。
  • 关键:只有page.tsx会被暴露为可访问路由,文件名不可随意修改。

2. layout.tsx:页面布局容器

  • 作用:包裹page.tsx的“外壳”,负责公共UI的复用(如导航栏、页脚),支持嵌套继承。
  • 核心规则:
    • 根布局(app/layout.tsx)必须包含<html><body>标签,是所有页面的基础容器;
    • 子文件夹中的layout.tsx会自动嵌套在父级布局内(类似FastAPI的模板继承);
    • 适用场景:社区的全局导航栏、左侧分类栏等需要在多个页面复用的UI,均放在布局中。

3. loading.tsx:数据加载状态组件

  • 作用:当页面从数据库/接口获取数据时,Next.js会自动显示该组件(如加载动画),无需手动编写状态管理。
  • 与FastAPI对比:FastAPI需手动编写前端JS处理加载状态,Next.js通过约定文件自动实现,简化开发流程。

4. not-found.tsx & error.tsx:错误处理组件

  • not-found.tsx:对应404页面(路由不存在时触发);
  • error.tsx:处理页面渲染或数据获取时的500级错误;
  • 优势:错误处理与页面逻辑分离,且支持局部错误隔离(某子路由报错不影响全局)。

4. 动手实战:搭建社区基础路由与布局

基于上述约定,我们编写核心页面与布局,感受Next.js的路由工作流:

步骤1:清理首页默认代码

打开src/app/page.tsx,删除默认广告内容,编写简洁的首页:

// src/app/page.tsx
export default function Home() {
  return (
    <main className="p-10">
      <h1 className="text-3xl font-bold text-gray-900">欢迎来到 Next 开发者社区</h1>
      <p className="mt-4 text-gray-600">在这里交流技术、分享经验、共同成长</p>
    </main>
  );
}

步骤2:创建“帖子列表”路由

  1. src/app下新建posts文件夹;
  2. posts文件夹中创建page.tsx(路由入口):
// src/app/posts/page.tsx
export default function PostsPage() {
  return (
    <div className="p-10">
      <h2 className="text-2xl font-bold text-gray-900 mb-6">最新技术帖子</h2>
      {/* 帖子卡片示例 */}
      <div className="border rounded-lg p-4 shadow-sm mb-4">
        <h3 className="font-semibold text-lg">Next.js 服务端组件实战技巧</h3>
        <p className="mt-2 text-gray-600">详解RSC的使用场景与性能优化方案...</p>
      </div>
      <div className="border rounded-lg p-4 shadow-sm">
        <h3 className="font-semibold text-lg">FastAPI与Next.js对接最佳实践</h3>
        <p className="mt-2 text-gray-600">前后端分离架构下的数据交互方案...</p>
      </div>
    </div>
  );
}

此时访问http://localhost:3000/posts,即可看到帖子列表页,路由自动通过posts/page.tsx映射生成。

步骤3:实现全局布局(导航栏+页面容器)

修改src/app/layout.tsx,添加全局导航栏(类似Jinja2的base.html公共部分),让所有页面共享导航:

// src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Link from "next/link"; // Next.js专用链接组件(优化跳转性能)

// 引入全局字体
const inter = Inter({ subsets: ["latin"] });

// 网站元数据(标题、描述,类似FastAPI的响应头配置)
export const metadata: Metadata = {
  title: "Next 开发者社区",
  description: "连接开发者的技术交流平台",
};

export default function RootLayout({
  children, // 页面内容注入点(所有page.tsx的内容会被渲染到这里)
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="zh-CN">
      <body className={inter.className}>
        {/* 全局导航栏(所有页面共享) */}
        <nav className="bg-black text-white p-4 flex items-center gap-6">
          <div className="font-bold text-xl">NextCommunity</div>
          <Link href="/" className="hover:text-gray-300 transition-colors">首页</Link>
          <Link href="/posts" className="hover:text-gray-300 transition-colors">浏览帖子</Link>
          <Link href="/login" className="ml-auto bg-white text-black px-4 py-2 rounded hover:bg-gray-100 transition-colors">登录</Link>
        </nav>
        
        {/* 页面内容容器(children为动态注入的页面内容) */}
        <div className="min-h-screen bg-gray-50">{children}</div>
      </body>
    </html>
  );
}

核心优势说明

  • 导航栏复用:所有页面都会自动包含顶部导航,无需重复编写(类似FastAPI的模板继承);
  • 无刷新跳转:使用Link组件跳转时,仅children部分更新,导航栏不重新渲染,既保留SPA的流畅体验,又拥有SSR的SEO优势;
  • 状态保留:跳转时导航栏的状态(如搜索框输入内容)不会丢失,提升用户体验。

5. 架构进阶:推荐的Feature-First目录组织

随着社区功能迭代(如帖子发布、评论、用户中心),仅靠app目录会导致代码混乱。参考Python项目的分层架构(Controller-Service-DAO),推荐在src下建立平行目录,实现“路由与业务逻辑分离”:

推荐目录结构

src/
├── app/              # 路由分发与页面入口(对应Controller/View层)
├── components/       # 可复用UI组件
│   ├── ui/           # 基础组件(按钮、输入框等通用组件)
│   └── business/     # 业务组件(帖子卡片、评论列表等社区专属组件)
├── lib/              # 工具库与配置(对应Utils层)
│   ├── db.ts         # 数据库连接(如Prisma/Drizzle)
│   └── utils.ts      # 通用工具函数(格式校验、日期处理等)
├── types/            # TypeScript类型定义(对应Pydantic模型)
└── services/         # 后端业务逻辑(对应Service层)
    └── posts.ts      # 帖子相关逻辑(创建、查询、删除等)

设计思路与FastAPI呼应

  • app/仅负责“路由映射”和“页面组装”,不包含复杂业务逻辑,类似FastAPI的Controller;
  • services/封装核心业务逻辑(如数据查询、权限校验),类似FastAPI的Service层,可被多个页面复用;
  • components/分离UI组件,实现“一次编写、多处使用”,避免代码冗余;
  • types/统一类型定义,类似Pydantic模型,确保前后端数据类型一致,减少类型错误。

这种结构的优势是:职责清晰、可维护性强,符合Python开发者熟悉的分层架构思维,降低项目迭代成本。

本章核心总结

  1. App Router核心规则:文件系统即路由,page.tsx是路由终点,文件夹结构直接映射URL;
  2. 核心约定文件layout.tsx(布局容器)、page.tsx(页面内容)、loading.tsx(加载状态)、not-found.tsx/error.tsx(错误处理),无需额外配置;
  3. 关键特性:Colocation(资源就近存放)、默认服务端组件(RSC)、布局嵌套继承,兼顾开发效率与性能;
  4. 思维转换:Next.js并非单纯前端框架,而是“前后端一体化”的Web服务器,可直接在组件中编写后端逻辑(下一章详解);
  5. 架构建议:采用Feature-First分层结构,分离路由、组件、业务逻辑,契合Python开发者的工程化思维。

下一步预告

当前页面的数据是静态模拟的,下一章《Next.js从入门到精通(2):路由处理器(Route Handlers)——用标准Request/Response写后端接口》将带你学习:如何在Next.js中编写类似FastAPI的API接口,实现数据的动态获取与提交,让社区项目真正“活”起来。

用 localStorage 打造本地待办清单:一个轻量级的前端实践

作者 www_stdio
2025年11月22日 16:41

用 localStorage 打造本地待办清单:一个轻量级的前端实践

在现代网页开发中,我们常常需要在浏览器端保存一些用户数据,比如用户的偏好设置、临时输入内容,或者像本文要实现的——一个本地待办事项列表(Todo List)。借助浏览器提供的 localStorage,我们可以轻松地将数据持久化存储在用户的设备上,即使关闭页面或重启浏览器,数据也不会丢失。

localStorage 是什么?

localStorage 是 Web Storage API 的一部分,它为每个域名提供了一块独立的存储空间。它的特点是:

  • 永久存储:除非用户手动清除或通过代码删除,否则数据不会过期。
  • 键值对结构:所有数据都以字符串形式存储,因此通常需要配合 JSON.stringify()JSON.parse() 来处理对象。
  • 同源策略限制:只能被同一协议、域名和端口下的页面访问。

实现一个本地待办清单

下面是一个完整的待办清单示例,使用 HTML、CSS 和原生 JavaScript 构建,并利用 localStorage 实现数据持久化。

HTML 结构

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LOCAL TAPAS</title>
    <link rel="stylesheet" href="./common.css">
</head>
<body>
    <div class="wrapper">
        <h2>LOCAL TAPAS</h2>
        <ul class="plates">
            <li>Loading Tapas...</li>
        </ul>
        <form class="add-items">
            <input type="text" placeholder="Item Name" required name="item">
            <input type="submit" value="+ Add Item">
        </form>
    </div>
    <!-- JavaScript 脚本 -->
</body>
</html>

页面包含一个标题、一个待办项列表容器(<ul class="plates">)以及一个用于添加新事项的表单。

CSS 样式亮点

html {
    box-sizing: border-box;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
}

*, *::before, *::after {
    box-sizing: inherit;
}

.plates li {
    display: flex;
}

.plates input {
    display: none;
}

.plates input + label:before {
    content: "⬜️";
    margin-right: 10px;
}

.plates input:checked + label:before {
    content: "✅";
}

这里有几个关键点:

  • 使用 flex 布局让页面整体居中,同时让每个待办项内部也采用弹性布局。
  • 利用 input[type=checkbox] 配合 label 和伪元素 ::before 实现自定义复选框样式。
  • outline 属性用于高亮输入框焦点状态,且不占用盒模型空间。
  • overflow: hidden 可防止子元素溢出父容器(虽然本例未直接使用,但属于常用技巧)。

注意:CSS 中并非所有属性都会继承。例如 font-sizecolor 会从父元素继承,而 backgroundwidthheight 等则不会。开发者需根据需求显式设置。

JavaScript 逻辑

核心逻辑围绕三个函数展开:

1. 初始化数据
const items = JSON.parse(localStorage.getItem('todos')) || [];

尝试从 localStorage 中读取名为 'todos' 的数据,若不存在则初始化为空数组。

2. 渲染列表
function populateList(plates = [], platesList) {
    platesList.innerHTML = plates.map((plate, i) => `
        <li>
            <input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} />
            <label for="item${i}">${plate.text}</label>
        </li>
    `).join('');
}

该函数接收待办项数组和 DOM 容器,动态生成带复选框的列表项,并通过 data-index 记录索引以便后续操作。

3. 添加与切换状态
function addItem(event) {
    event.preventDefault();
    const text = this.querySelector('[name=item]').value.trim();
    if (!text) return;
    items.push({ text, done: false });
    localStorage.setItem('todos', JSON.stringify(items));
    populateList(items, itemsList);
    this.reset();
}

function toggleDone(event) {
    if (event.target.tagName === 'INPUT') {
        const index = event.target.dataset.index;
        items[index].done = !items[index].done;
        localStorage.setItem('todos', JSON.stringify(items));
        populateList(items, itemsList);
    }
}
  • 表单提交时阻止默认刷新行为,提取输入值并存入 items 数组。
  • 点击复选框时,根据 data-index 更新对应项的完成状态。
  • 每次变更后立即同步到 localStorage,确保数据持久化。

最后绑定事件监听器:

addItems.addEventListener('submit', addItem);
itemsList.addEventListener('click', toggleDone);
populateList(items, itemsList); // 初次渲染

小结

这个“Local Tapas”(本地小食清单)虽小,却完整展示了前端开发中的多个核心概念:

  • 数据持久化:通过 localStorage 实现无服务器依赖的本地存储。
  • DOM 操作与事件委托:高效更新界面,避免重复绑定事件。
  • CSS 继承与布局:理解哪些样式可继承,合理使用 Flexbox 实现响应式结构。
  • 函数式思维:将逻辑封装为可复用函数,提升代码可读性与维护性。

无需后端、不依赖框架,仅用浏览器原生能力,就能构建一个实用又美观的交互应用——这正是现代 Web 开发的魅力所在。

vue3自定义v-model

作者 东华帝君
2025年11月22日 15:46

vue 3.0+

1. v-model='color'不自定义属性名

子组件

  • props.modelValue
  • emits("update:modelValue", color.value)
  1. 通过defineProps()拿到props
  2. 通过defineEmits()拿到emits
  3. 通过emit触发更新update:modelValue---- 当使用v-model=时 子组件拿到的是属性为modelValue 的值,这是固定的
<template>
  <label>颜色:<input v-model="color"  /></label>
</template>
<script setup lang="ts">
import { defineProps, defineEmits, ref } from "vue";

const props = defineProps({
  modelValue: String,
});
const emits = defineEmits<{
  (e: "update:modelValue", value: string): void;
}>();
const color = computed({
    get:()=>props.modelValue,
    set:(value:string)=>emits('update:modelValue',value)
})

</script>

父组件 v-model="color"

<script setup lang="ts">
import Child from "@/components/child.vue";
import { ref } from "vue";
const color = ref("red");
</script>

<template>
  <Child v-model="color" />
  <div>color:{{ color }}</div>
</template>

2. v-model='color'自定义属性名

子组件
update:color

<template>
  <label>颜色:<input v-model="color" /></label>
</template>
<script setup lang="ts">
import { defineProps, defineEmits, ref } from "vue";

const props = defineProps({
  color: String,
});
const emits = defineEmits<{
  (e: "update:color", value: string): void;
}>();
const color = computed({
    get:()=>props.modelValue,
    set:(value:string)=>emits('update:modelValue',value)
})

</script>

父组件 v-model:color="color"

<script setup lang="ts">
import Child from "@/components/child.vue";
import { ref } from "vue";
const color = ref("red");
</script>

<template>
  <Child v-model:color="color" />
  <div>color:{{ color }}</div>
</template>

vue 3.4+

子组件

<template>
  <label>颜色:<input v-model="color" /></label>
</template>
<script setup lang="ts">
import { defineModel } from "vue";

const color = defineModel({type:String})

父组件

<script setup lang="ts">
import Child from "@/components/child.vue";
import { ref } from "vue";
const color = ref("red");
</script>

<template>
  <Child v-model="color" />
  <div>color:{{ color }}</div>
</template>

阿斯利康宣布投资20亿美元扩建马里兰州生产基地

2025年11月22日 17:30
当地时间11月21日,阿斯利康宣布计划投资20亿美元,用于扩建其位于马里兰州的长期生产基地。此项投资将为该州创造2600个就业岗位,包括保留当地现有职位、建筑活动相关岗位,以及新增300个高技能职位。(界面新闻)

兆威机电、迈威生物港股IPO获中国证监会备案

2025年11月22日 17:00
11月22日消息,中国证监会国际合作司发布关于深圳市兆威机电股份有限公司、迈威(上海)生物科技股份有限公司境外发行上市备案通知书,兆威机电拟发行不超过69,058,450股境外上市普通股并在香港联合交易所上市。迈威生物拟发行不超过62,664,600股境外上市普通股并在香港联合交易所上市。(金十数据APP)

澳大利亚将禁止机舱内使用充电宝

2025年11月22日 16:00
澳大利亚几家主要航空公司21日宣布,将于下月开始陆续禁止在航班上使用充电宝或为充电宝充电,以降低机舱内火灾风险。这些安全措施将适用于所有国内和国际航班,且不会给予任何豁免。据澳大利亚广播公司21日报道,澳洲航空公司及其旗下的澳航支线、捷星航空宣布将于12月15日起实施禁令,而维珍澳大利亚航空公司的禁令将从12月1日起执行。(财联社)

IDEA研究院孵化企业视启未来获近亿元融资

2025年11月22日 15:44
36氪获悉,孵化自IDEA研究院的视觉大模型企业,视启未来(深圳)科技有限公司宣布完成近亿元天使轮融资。本轮由A股上市公司安凯微领投,昊辰资本、德虎资本、元禾璞华、银杏谷资本、力合中科、数字未来、九安智能等机构跟投。
❌
❌