前言Nuxt 4 计划于 2025 年第二季度正式发布，带来了大量的性能优化和架构改进。本文将详细介绍 Nuxt 3.x 到 4.0 的所有重大变更，并提供完整的迁移步骤指南。快速开始：如何测试 Nuxt 4在正式版发布前，你可以通过以下方式提前体验 Nuxt 4 的特性：1. 升级到最新版本# npm npx nuxt upgrade # yarn yarn nuxt upgrade # pnpm pnpm nuxt upgrade # bun bun x nuxt upgrade2. 启用 Nuxt 4 兼容模式在 nuxt.config.ts 中添加：export default defineNuxtConfig({ future: { compatibilityVersion: 4, }, })3. 使用自动化迁移工具Nuxt 团队与 Codemod 合作提供了自动化迁移工具：npx codemod@latest nuxt/4/migration-recipe这个命令会依次执行所有迁移脚本，你可以选择性地运行需要的部分。主要变更详解🔥 1. 新的目录结构（影响级别：重大）变更内容Nuxt 4 采用了全新的默认目录结构，具有向后兼容性：旧结构（Nuxt 3）：. ├── components/ ├── pages/ ├── plugins/ ├── server/ ├── public/ └── nuxt.config.ts新结构（Nuxt 4）：. ├── app/ # 新增：应用代码目录 │ ├── assets/ │ ├── components/ │ ├── composables/ │ ├── layouts/ │ ├── middleware/ │ ├── pages/ │ ├── plugins/ │ ├── utils/ │ ├── app.vue │ ├── error.vue │ ├── app.config.ts │ └── router.options.ts ├── content/ # 保持在根目录 ├── layers/ # 保持在根目录 ├── modules/ # 保持在根目录 ├── public/ # 保持在根目录 ├── server/ # 保持在根目录 └── nuxt.config.ts # 保持在根目录关键变更默认 srcDir 为 app/：大部分应用代码都放在 app/ 目录下serverDir 默认为 <rootDir>/server：而不是 <srcDir>/server~ 别名指向 app/：例如 ~/components 解析为 app/components/根目录保留：layers/、modules/、public/、server/ 仍在项目根目录迁移理由性能优化：避免 .git/ 和 node_modules/ 被文件监视器扫描IDE 类型安全：server/ 和应用代码运行在不同上下文，分离有助于更好的自动补全迁移步骤自动迁移：npx codemod@latest nuxt/4/file-structure手动迁移：创建 app/ 目录移动以下文件夹到 app/ 下：assets/components/composables/layouts/middleware/pages/plugins/utils/app.vueerror.vueapp.config.ts保持以下文件夹在根目录：nuxt.config.tscontent/layers/modules/public/server/更新第三方配置文件（如 tailwind.config.js、.eslintrc 等）如需保持旧结构：export default defineNuxtConfig({ srcDir: '.', dir: { app: 'app' } })🔄 2. 单例数据获取层（影响级别：中等）变更内容Nuxt 4 重新设计了 useAsyncData 和 useFetch 的数据获取系统：共享引用：相同 key 的所有调用共享 data、error 和 status 引用响应式 key 支持：可以使用 computed、ref 或 getter 函数作为 key数据清理：组件卸载时自动清理数据，避免内存泄漏改进的 getCachedData 控制：每次获取数据时都会调用该函数迁移步骤检查冲突选项：// ❌ 错误：相同 key，不同选项 const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true }) // ✅ 正确：提取为独立的 composable // composables/useUserData.ts export function useUserData(userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: (user) => ({ ...user, lastAccessed: new Date() }) } ) }更新 getCachedData 实现：useAsyncData('key', fetchFunction, { getCachedData: (key, nuxtApp, ctx) => { // ctx.cause 可以是：'initial' | 'refresh:hook' | 'refresh:manual' | 'watch' // 例如：手动刷新时不使用缓存 if (ctx.cause === 'refresh:manual') return undefined return cachedData[key] } })临时禁用新行为：export default defineNuxtConfig({ experimental: { granularCachedData: false, purgeCachedData: false } })📄 3. 路由元数据去重（影响级别：最小）变更内容通过 definePageMeta 设置的路由元数据（如 name、path）现在只能从 route 对象访问，而不再从 route.meta 访问。迁移步骤const route = useRoute() // ❌ 旧写法 console.log(route.meta.name) // ✅ 新写法 console.log(route.name)🏷️ 4. 标准化组件命名（影响级别：中等）变更内容Vue 生成的组件名称现在匹配 Nuxt 的组件命名模式。示例：components/ └── SomeFolder/ └── MyComponent.vue旧命名：Vue 组件名为 MyComponent，Nuxt 自动导入名为 SomeFolderMyComponent新命名：两者都为 SomeFolderMyComponent迁移步骤更新测试和 <KeepAlive> 中的组件名称： <KeepAlive :include="['MyComponent']"> <SomeFolderMyComponent /> </KeepAlive> <KeepAlive :include="['SomeFolderMyComponent']"> <SomeFolderMyComponent /> </KeepAlive>临时禁用：export default defineNuxtConfig({ experimental: { normalizeComponentNames: false } })🎯 5. Unhead v2 升级（影响级别：最小）变更内容用于生成 标签的 Unhead 库升级到 v2 版本：移除的属性：vmid、hid、children、body不再支持 Promise 输入默认使用 Capo.js 进行标签排序迁移步骤移除废弃属性：useHead({ meta: [{ name: 'description', content: '描述内容', // ❌ 移除这些 // vmid: 'description', // hid: 'description' }] })更新导入路径：// ❌ 旧写法 import { useHead } from '@unhead/vue' // ✅ 新写法 import { useHead } from '#imports' // 或 import { useHead } from 'nuxt/app'如需使用模板参数或别名排序插件：import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins' export default defineNuxtPlugin({ setup() { const unhead = injectHead() unhead.use(TemplateParamsPlugin) unhead.use(AliasSortingPlugin) } })🎨 6. SPA 加载屏幕位置变更（影响级别：最小）变更内容SPA 加载模板的 DOM 位置改变： 迁移理由允许 SPA 加载模板保持在 DOM 中直到 Vue 应用 suspense 解析完成，防止白屏闪烁。迁移步骤更新 CSS 选择器或使用新的配置选项：export default defineNuxtConfig({ app: { spaLoaderTag: 'div', spaLoaderAttrs: { id: 'custom-loader' } } })📊 7. error.data 自动解析（影响级别：最小）变更内容抛出错误时的 data 属性现在会自动解析。迁移步骤移除手动解析代码： 🎨 8. 更细粒度的内联样式（影响级别：中等）变更内容Nuxt 现在只为 Vue 组件内联样式，不再内联全局 CSS。迁移理由减少单独的网络请求允许缓存单个全局 CSS 文件减少初始请求的文档下载大小迁移步骤如需恢复旧行为：export default defineNuxtConfig({ features: { inlineStyles: true } })🔍 9. 页面元数据扫描时机调整（影响级别：最小）变更内容现在在调用 pages:extend 钩子之后扫描页面元数据，而不是之前。迁移步骤在 pages:resolved 钩子中覆盖页面元数据：export default defineNuxtConfig({ hooks: { // ❌ 旧写法 // 'pages:extend'(pages) { // ✅ 新写法 'pages:resolved'(pages) { const myPage = pages.find(page => page.path === '/') myPage.meta ||= {} myPage.meta.layout = 'overridden-layout' } } })🚀 10. 共享预渲染数据（影响级别：中等）变更内容启用了实验性功能，在不同页面间共享 useAsyncData 和 useFetch 的数据。迁移理由自动在预渲染页面之间共享 payload 数据，显著提升预渲染性能。迁移步骤确保每个数据的唯一 key 始终解析为相同的数据：// ❌ 不安全：key 不反映实际获取的数据 const route = useRoute() const { data } = await useAsyncData(async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) // ✅ 安全：key 唯一标识获取的数据 const { data } = await useAsyncData(route.params.slug, async () => { return await $fetch(`/api/my-page/${route.params.slug}`) })📦 11. useAsyncData 和 useFetch 的默认值变更（影响级别：最小）变更内容data 默认值从 null 改为 undefinederror 默认值从 null 改为 undefined自动迁移npx codemod@latest nuxt/4/default-data-error-value手动迁移// ❌ 旧写法 if (data.value === null) { } if (error.value === null) { } // ✅ 新写法 if (data.value === undefined) { } if (error.value === undefined) { }🔄 12. 移除 dedupe 的布尔值（影响级别：最小）变更内容refresh 函数的 dedupe 选项不再接受布尔值，必须使用明确的字符串值。自动迁移npx codemod@latest nuxt/4/deprecated-dedupe-value手动迁移const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' })) // ❌ 旧写法 await refresh({ dedupe: true }) await refresh({ dedupe: false }) // ✅ 新写法 await refresh({ dedupe: 'cancel' }) await refresh({ dedupe: 'defer' })🎯 13. 清除数据时尊重默认值（影响级别：最小）变更内容调用 clear 或 clearNuxtData 时，data 会重置为其默认值而不是 undefined。示例const { data, clear } = useAsyncData('key', fetchData, { default: () => [] }) // 调用 clear() 后，data.value 将是 []，而不是 undefined⏳ 14. pending 值对齐（影响级别：中等）变更内容pending 现在是一个计算属性，只有当 status 为 'pending' 时才为 true。迁移步骤 Data: {{ data }} Loading... 🔑 15. Key 变更行为调整（影响级别：中等）变更内容当 immediate: false 时，useAsyncData 和 useFetch 只有在数据已经获取过一次后，才会在 key 变更时重新获取数据。迁移步骤const id = ref('123') const { data, execute } = await useFetch('/api/test', { query: { id }, immediate: false }) // ✅ 需要手动触发第一次 watch(id, execute, { once: true })📉 16. 浅层数据响应性（影响级别：最小）变更内容data 对象现在是 shallowRef 而不是 ref。迁移理由对深层嵌套对象和数组带来显著的性能提升。自动迁移npx codemod@latest nuxt/4/shallow-function-reactivity手动迁移按需启用深度响应性：// ✅ 单个组件启用 const { data } = useFetch('/api/test', { deep: true })全局配置（不推荐）：export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: true } } } })📁 17. builder:watch 绝对路径（影响级别：最小）变更内容builder:watch 钩子现在发出绝对路径而不是相对于 srcDir 的路径。自动迁移npx codemod@latest nuxt/4/absolute-watch-path手动迁移（模块作者）import { relative, resolve } from 'node:fs' nuxt.hook('builder:watch', async (event, path) => { // 使其兼容 Nuxt 3 和 4 path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path)) // ... })🌐 18. 移除 window.__NUXT__ 对象（影响级别：最小）变更内容应用完成水合后，全局 window.__NUXT__ 对象将被移除。迁移步骤// ❌ 旧写法 console.log(window.__NUXT__) // ✅ 新写法 console.log(useNuxtApp().payload)📂 19. 目录索引文件扫描（影响级别：中等）变更内容middleware/ 文件夹中的子文件夹的 index 文件现在也会被注册为 middleware。迁移步骤如需恢复旧行为：export default defineNuxtConfig({ hooks: { 'app:resolve'(app) { app.middleware = app.middleware.filter( mw => !/\/index\.[^/]+$/.test(mw.path) ) } } })📝 20. 模板编译变更（影响级别：最小）变更内容Nuxt 不再使用 lodash/template 编译 .ejs 模板文件。迁移理由移除安全漏洞减少依赖体积更灵活的虚拟语法迁移步骤自动迁移：npx codemod@latest nuxt/4/template-compilation-changes手动迁移方案：方案 1：使用 getContents() 直接处理addTemplate({ fileName: 'my-template.js', getContents({ options }) { return `export default ${JSON.stringify(options)}` } })方案 2：使用 es-toolkit/compatimport { readFileSync } from 'node:fs' import { template } from 'es-toolkit/compat' addTemplate({ fileName: 'appinsights-vue.js', options: { /* some options */ }, getContents({ options }) { const contents = readFileSync( resolver.resolve('./runtime/plugin.ejs'), 'utf-8' ) return template(contents)({ options }) } })替换模板工具：import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork' // 替换 serialize const serialize = (data: any) => JSON.stringify(data, null, 2) .replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1')) // 替换 importName const importName = genSafeVariableName // 替换 importSources const importSources = (sources: string | string[], { lazy = false } = {}) => { return toArray(sources).map((src) => { if (lazy) { return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}` } return genImport(src, genSafeVariableName(src)) }).join('\n') }🔧 21. TypeScript 配置变更（影响级别：最小）变更内容Nuxt 现在为不同上下文生成独立的 TypeScript 配置：.nuxt/tsconfig.app.json - 应用代码.nuxt/tsconfig.server.json - 服务器代码.nuxt/tsconfig.node.json - 构建时代码.nuxt/tsconfig.shared.json - 共享代码.nuxt/tsconfig.json - 向后兼容迁移理由更好的类型安全改进的 IDE 体验更清晰的分离性能优化迁移步骤（可选）启用项目引用：更新根 tsconfig.json：{ "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] }更新类型检查脚本：{ "scripts": { "typecheck": "nuxt prepare && vue-tsc -b --noEmit" } }配置 Node.js TypeScript 选项：export default defineNuxtConfig({ typescript: { // 自定义 app/server TypeScript 配置 tsConfig: { compilerOptions: { strict: true } }, // 自定义构建时 TypeScript 配置 nodeTsConfig: { compilerOptions: { strict: true } } } })🗑️ 22. 移除实验性特性（影响级别：最小）变更内容以下实验性特性不再可配置：experimental.treeshakeClientOnly - 将为 trueexperimental.configSchema - 将为 trueexperimental.polyfillVueUseHead - 将为 falseexperimental.respectNoSSRHeader - 将为 falsevite.devBundler - 将默认使用 vite-node📋 23. 移除顶层 generate 配置（影响级别：最小）变更内容顶层 generate 配置选项不再可用。迁移步骤export default defineNuxtConfig({ // ❌ 旧写法 // generate: { // exclude: ['/admin', '/private'], // routes: ['/sitemap.xml', '/robots.txt'] // } // ✅ 新写法 nitro: { prerender: { ignore: ['/admin', '/private'], routes: ['/sitemap.xml', '/robots.txt'] } } })完整迁移清单准备阶段[ ] 升级到最新的 Nuxt 3.x 版本[ ] 备份项目代码[ ] 运行现有测试确保一切正常自动化迁移[ ] 运行完整迁移工具：npx codemod@latest nuxt/4/migration-recipe手动检查项[ ] 检查并调整新的目录结构[ ] 更新 useAsyncData 和 useFetch 调用[ ] 检查共享 key 的数据获取是否有冲突选项[ ] 更新路由元数据访问方式[ ] 更新组件名称引用[ ] 移除 useHead 中的废弃属性[ ] 检查 SPA 加载模板相关的 CSS[ ] 移除 error.data 的手动解析[ ] 更新 dedupe 选项的布尔值为字符串[ ] 检查 pending 状态的使用[ ] 为需要深度响应性的数据添加 deep: true[ ] 更新 builder:watch 钩子（如果是模块作者）[ ] 替换 window.__NUXT__ 为 useNuxtApp().payload[ ] 迁移 .ejs 模板文件[ ] 更新 generate 配置为 nitro.prerender[ ] （可选）启用新的 TypeScript 项目引用测试阶段[ ] 运行类型检查：npm run typecheck[ ] 运行单元测试[ ] 运行 E2E 测试[ ] 本地开发环境测试[ ] 预渲染测试（如果使用）[ ] 生产构建测试配置文件更新[ ] 更新 tailwind.config.js（如果使用）[ ] 更新 .eslintrc（如果使用）[ ] 更新其他第三方配置文件性能优化建议迁移到 Nuxt 4 后，你可以享受以下性能提升：更快的启动时间：新的目录结构减少了文件监视器的扫描范围更好的内存管理：自动清理未使用的数据优化的 CSS 处理：更细粒度的样式内联策略改进的预渲染：共享数据减少重复请求浅层响应性：深层嵌套数据的性能提升常见问题Q: 必须立即迁移到 Nuxt 4 吗？A: 不必须。Nuxt 4 预计在 2025 年 Q2 发布，你可以继续使用 Nuxt 3.x。但建议提前测试兼容性，并逐步迁移。Q: 如何在不破坏现有功能的情况下测试 Nuxt 4？A: 使用 compatibilityVersion: 4 配置可以选择性地启用 Nuxt 4 特性，同时可以通过配置项恢复 Nuxt 3 的行为。建议在开发分支上测试。Q: 自动迁移工具安全吗？A: Codemod 工具是官方推荐的，但建议：先备份代码使用版本控制仔细审查自动生成的变更运行完整的测试套件Q: 旧的目录结构还能用吗？A: 可以。Nuxt 会自动检测旧结构。如果检测到顶层的 pages/ 目录，新的目录结构不会生效。你也可以通过配置强制使用旧结构。Q: 如何处理第三方模块的兼容性？A:检查模块的官方文档是否有 Nuxt 4 更新在 GitHub 上查看模块的 issues 和 PR如有问题，向模块作者报告Nuxt 团队已经主动迁移了许多流行模块Q: TypeScript 项目引用是必须的吗？A: 不是必须的。现有项目继续使用 .nuxt/tsconfig.json 即可。项目引用是可选的优化，提供更好的类型检查体验。Q: 迁移后性能会提升多少？A: 取决于项目规模和使用的特性：小型项目可能感受不明显大型项目在启动时间、内存使用和预渲染方面会有显著提升使用大量 useAsyncData 的项目会从新的数据获取系统中受益Q: 如何回退到 Nuxt 3？A:移除 compatibilityVersion: 4 配置恢复相关的实验性选项配置如果调整了目录结构，可以通过配置恢复或手动移回版本对比Nuxt 2 vs Nuxt 3 vs Nuxt 4特性/版本Nuxt 2Nuxt 3Nuxt 4Vue 版本233稳定性😊 稳定😊 稳定😊 稳定性能🏎 快速✈️ 更快🚀 最快Nitro 引擎❌✅✅ESM 支持🌙 部分👍 更好✅ 完整TypeScript☑️ 可选✅ 完整✅ 完整Composition API❌✅✅ ❌✅✅自动导入❌✅✅webpack455Vite⚠️ 部分✅✅目录结构扁平扁平app/ 目录数据获取asyncDatauseAsyncData增强的useAsyncData推荐迁移时间线阶段 1：准备期（1-2 周）升级到最新的 Nuxt 3.x阅读官方迁移文档在开发分支启用 compatibilityVersion: 4运行自动迁移工具阶段 2：测试期（2-4 周）修复自动迁移后的问题运行完整测试套件进行性能基准测试处理第三方模块兼容性问题阶段 3：逐步部署（2-4 周）先在开发/测试环境部署收集反馈和监控性能修复发现的问题准备回滚方案阶段 4：生产部署选择低流量时段部署密切监控错误和性能指标准备快速回滚迁移资源官方资源Nuxt 4 文档: https://nuxt.com/docs/4.x升级指南: https://nuxt.com/docs/4.x/getting-started/upgradeGitHub 仓库: https://github.com/nuxt/nuxt发布说明: https://github.com/nuxt/nuxt/releases自动化工具Codemod 注册表: https://go.codemod.com/codemod-registryNuxt 4 迁移配方:npx codemod@latest nuxt/4/migration-recipe社区支持Discord: https://discord.com/invite/nuxtGitHub Discussions: https://github.com/nuxt/nuxt/discussionsTwitter/X: @nuxt_js相关库更新Unhead v2: https://unhead.unjs.io/Nitro: https://nitro.unjs.io/Knitwork: https://github.com/unjs/knitworkes-toolkit: https://es-toolkit.slash.page/总结Nuxt 4 带来了大量的性能优化和开发体验改进，主要变更包括：🎯 核心变更新的 app/ 目录结构 - 更好的性能和类型安全增强的数据获取系统 - 更好的内存管理和状态共享改进的类型系统 - 独立的上下文配置🚀 性能提升更快的启动时间更低的内存占用优化的 CSS 处理改进的预渲染性能🛠️ 开发体验更好的 IDE 支持更清晰的错误信息自动化迁移工具向后兼容性支持✅ 推荐做法使用自动迁移工具快速开始在开发分支先测试逐步启用新特性保持依赖更新关注官方发布说明虽然 Nuxt 4 引入了一些破坏性变更，但大部分都可以通过自动化工具完成迁移，并且 Nuxt 提供了充分的配置选项来保持向后兼容。建议尽早开始测试，以便在正式版发布时能够顺利升级。附录：完整配置示例Nuxt 4 完整配置示例// nuxt.config.ts export default defineNuxtConfig({ // 启用 Nuxt 4 兼容模式 future: { compatibilityVersion: 4, }, // TypeScript 配置 typescript: { strict: true, typeCheck: true, tsConfig: { compilerOptions: { strict: true, } }, nodeTsConfig: { compilerOptions: { strict: true, } } }, // Nitro 配置（替代旧的 generate 配置） nitro: { prerender: { routes: ['/sitemap.xml', '/robots.txt'], ignore: ['/admin', '/api'], } }, // 应用配置 app: { head: { charset: 'utf-8', viewport: 'width=device-width, initial-scale=1', }, // SPA 加载器配置 spaLoaderTag: 'div', spaLoaderAttrs: { id: 'spa-loader' } }, // 特性配置 features: { // 内联样式策略 inlineStyles: false, }, // Unhead 配置 unhead: { renderSSRHeadOptions: { omitLineBreaks: true } }, // 实验性特性（如需临时恢复 Nuxt 3 行为） experimental: { // 这些是示例，通常不需要配置 // scanPageMeta: 'after-resolve', // sharedPrerenderData: true, // defaults: { // useAsyncData: { // deep: false // } // } }, // 钩子示例 hooks: { 'pages:resolved'(pages) { // 在这里修改页面配置 }, 'builder:watch'(event, path) { // 处理文件变更 } } })Package.json 脚本示例{ "name": "my-nuxt4-app", "version": "1.0.0", "scripts": { "dev": "nuxt dev", "build": "nuxt build", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt prepare && vue-tsc -b --noEmit", "lint": "eslint .", "migrate": "npx codemod@latest nuxt/4/migration-recipe" }, "dependencies": { "nuxt": "^3.12.0", "vue": "^3.4.0" }, "devDependencies": { "@nuxt/eslint": "latest", "typescript": "^5.3.0", "vue-tsc": "^2.0.0" } }TypeScript 配置示例（使用项目引用）// tsconfig.json { "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] }本文翻译整理自Nuxt官方文档，由IvanHanloth主写，使用Claude进行翻译、代码修正和文风调整，请注意检查潜在的错误。请以Nuxt官方文档为准。
告别代码烂摊子！2025前端自动化神器（ESLint、Prettier等）一篇搞定！前端工程化：自动化代码质量保障体系构建指南
引言在现代前端工程实践中，代码质量和团队协作效率的提升离不开完善的自动化工具链。本文将系统性地介绍如何构建一套完整的代码质量保障体系，涵盖代码检查、格式化和提交前自动验证等关键环节。问题背景在团队协作开发中，代码风格不一致是一个普遍存在的问题。不同开发者可能采用不同的缩进方式、引号风格等编码习惯，这导致代码审查过程中产生大量与业务逻辑无关的讨论，降低了团队协作效率。通过引入自动化工具链，我们可以有效解决这一问题。本文将介绍以下核心工具的配置与集成：ESLint：静态代码分析工具，用于检测代码质量问题和潜在错误Prettier：代码格式化工具，统一代码风格Stylelint：样式代码检查工具，确保 CSS/SCSS 代码质量Husky：Git hooks 管理工具，在关键节点触发自动化流程Lint-staged：增量检查工具，仅对暂存文件执行检查，提升效率工具概览与职责划分在配置之前，理解各工具的职责边界至关重要，特别是 ESLint 和 Prettier 的区分。ESLint：代码质量分析核心功能ESLint 是一个可插拔、可配置的 JavaScript/TypeScript 静态代码分析工具。它通过解析代码为抽象语法树（AST）来检测代码模式，主要目标是发现潜在错误并强制执行最佳实践。检测范围变量使用前未定义（no-use-before-define）潜在的逻辑错误代码异味和反模式TypeScript 类型相关问题（通过 typescript-eslint）架构特点ESLint 采用完全可插拔的架构。核心规则和插件系统支持社区扩展，可针对特定框架（如 React）或语言特性进行定制。Prettier：代码格式化核心功能Prettier 是一个固执己见（Opinionated）的代码格式化工具。它将代码解析后按照预设规则重新格式化，确保整个团队的代码风格完全一致。处理范围行长度控制缩进方式引号类型分号使用支持多种语言（JavaScript、TypeScript、CSS、HTML、JSON、Markdown 等）设计理念Prettier 有意限制配置选项，秉承"配置一次，永久生效"的理念，最大程度减少团队内部关于代码风格的争论。Stylelint：样式代码检查核心功能Stylelint 是专门针对 CSS 及类 CSS 语法（SCSS、Sass、Less）的现代化 linter 工具。检测能力样式错误（如格式错误的网格区域、重复选择器）代码规范强制（如禁用特定单位、命名模式验证）选择器复杂度控制扩展性支持可共享配置，能够解析嵌入在 HTML、Markdown 和 CSS-in-JS 中的样式代码。Husky：Git Hooks 管理核心功能Husky 是一个轻量级的 Git hooks 管理工具，简化了 pre-commit、pre-push 等钩子的配置和使用。工作机制利用 Git 的 core.hooksPath 配置，实现高效的钩子管理。初始化后在项目根目录创建 .husky/ 目录存放钩子脚本。Lint-staged：增量检查核心功能Lint-staged 仅对 Git 暂存区（staged）的文件执行 linter 和 formatter。性能优势避免对整个项目执行检查，显著提升提交前检查的执行速度。与 Husky 的 pre-commit 钩子配合使用效果最佳。工具职责对照表工具核心功能主要配置文件VS Code 插件 IDESLint代码质量与错误检测eslint.config.jsdbaeumer.vscode-eslintPrettier代码格式化.prettierrc.jsesbenp.prettier-vscodeStylelintCSS/SCSS 质量检查stylelint.config.jsstylelint.vscode-stylelintHuskyGit hooks 管理.husky/pre-commitN/ALint-staged暂存文件任务执行package.jsonN/A分步配置指南本节将详细说明如何为 TypeScript + React 项目构建完整的自动化工具链。环境要求Node.js 版本：^18.18.0、^20.9.0 或 >=21.1.0项目已初始化 package.json第一步：配置 PrettierPrettier 配置相对独立，建议首先完成其设置。安装依赖npm install --save-dev --save-exact prettier使用 --save-exact 标志锁定版本，确保团队成员使用相同版本，避免格式化结果差异。创建配置文件创建 .prettierrc.js 文件：/** @type {import("prettier").Config} */ module.exports = { printWidth: 80, tabWidth: 2, useTabs: false, semi: true, singleQuote: true, quoteProps: 'as-needed', jsxSingleQuote: false, trailingComma: 'all', bracketSpacing: true, arrowParens: 'always', endOfLine: 'lf', };创建忽略文件创建 .prettierignore 文件：node_modules build dist coverage .vscode public第二步：配置 ESLint (Flat Config)重要更新ESLint v9 已将 Flat Config（eslint.config.js）作为默认配置格式，取代了传统的 .eslintrc 格式。Flat Config 采用 ES 模块导入，支持更精确的 glob 模式匹配，配置逻辑更清晰。安装核心依赖npm install --save-dev eslint @eslint/js typescript typescript-eslint安装 React 插件npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks安装 Prettier 集成npm install --save-dev eslint-config-prettierESLint 与 Prettier 集成策略关键概念：仅使用 eslint-config-prettier，避免使用 eslint-plugin-prettier。eslint-config-prettier：禁用与 Prettier 冲突的 ESLint 规则eslint-plugin-prettier（不推荐）：将 Prettier 作为 ESLint 规则运行，会降低性能并产生冗余提示正确的做法是让 ESLint 和 Prettier 独立运行，各司其职。创建配置文件创建 eslint.config.js 文件：import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; import react from 'eslint-plugin-react'; import reactHooks from 'eslint-plugin-react-hooks'; import eslintConfigPrettier from 'eslint-config-prettier'; import globals from 'globals'; export default tseslint.config( // 全局忽略配置 { ignores: ['dist/**', 'build/**', 'node_modules/**'], }, // ESLint 推荐规则 eslint.configs.recommended, // TypeScript-ESLint 推荐规则 ...tseslint.configs.recommended, // React 配置 { files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'], plugins: { react }, languageOptions: { ...react.configs.recommended.languageOptions, globals: { ...globals.browser }, }, rules: { ...react.configs.recommended.rules, // React 17+ 不需要手动导入 React 'react/react-in-jsx-scope': 'off', 'react/jsx-uses-react': 'off', }, }, // React Hooks 配置 { files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'], plugins: { 'react-hooks': reactHooks }, rules: reactHooks.configs.recommended.rules, }, // Prettier 集成配置（必须放在最后） eslintConfigPrettier, );配置说明ignores 字段用于排除不需要检查的文件使用扩展运算符应用推荐规则集eslintConfigPrettier 必须作为最后一个配置项，以确保正确禁用冲突规则第三步：配置 Stylelint安装依赖npm install --save-dev stylelint stylelint-config-standard-scss创建配置文件创建 stylelint.config.js 文件：/** @type {import('stylelint').Config} */ export default { extends: ['stylelint-config-standard-scss'], rules: { 'custom-property-pattern': [ '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', { message: '自定义属性应使用 kebab-case 命名', }, ], 'scss/dollar-variable-pattern': null, }, };创建忽略文件创建 .stylelintignore 文件：node_modules build dist coverage第四步：配置 Husky安装依赖npm install --save-dev husky初始化 Huskynpx husky init此命令会：创建 .husky/ 目录在 package.json 中添加 prepare 脚本验证配置检查 package.json 中是否包含：{ "scripts": { "prepare": "husky" } }prepare 脚本确保在 npm install 后自动设置 Git hooks，团队成员无需手动配置。第五步：配置 Lint-staged安装依赖npm install --save-dev lint-staged配置检查任务在 package.json 中添加：{ "lint-staged": { "*.{js,jsx,ts,tsx}": [ "eslint --fix", "prettier --write" ], "*.{css,scss}": [ "stylelint --fix", "prettier --write" ], "*.{md,json}": "prettier --write" } }配置说明不同文件类型匹配不同的命令序列--fix 和 --write 标志启用自动修复命令按顺序执行第六步：配置 Pre-commit Hook编辑钩子文件编辑 .husky/pre-commit 文件：npx lint-staged完整的工作流程：开发者执行 git commitGit 触发 pre-commit 钩子Husky 执行 .husky/pre-commit 脚本调用 lint-stagedLint-staged 获取暂存文件列表根据配置对文件执行 ESLint、Stylelint 和 Prettier如检查失败，提交中止；如成功，自动修复的内容加入提交VS Code 编辑器集成为提升开发体验，建议配置编辑器实时反馈。安装插件ESLint: dbaeumer.vscode-eslintPrettier: esbenp.prettier-vscodeStylelint: stylelint.vscode-stylelint工作区配置创建 .vscode/settings.json 文件：{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.fixAll.stylelint": "explicit" }, "stylelint.validate": ["css", "scss"] }配置说明设置 Prettier 为默认格式化工具启用保存时自动格式化启用保存时自动修复 ESLint 和 Stylelint 问题指定 Stylelint 验证的文件类型总结通过本文的配置步骤，您可以为项目构建一个完整的自动化代码质量保障体系。这套系统能够：统一团队代码风格自动检测和修复代码问题在提交前拦截不合规代码提供实时的编辑器反馈建议在实际应用中根据项目需求调整具体规则配置，逐步完善自动化流程。附录：快速参考依赖安装汇总核心工具npm install --save-dev --save-exact prettier npm install --save-dev eslint @eslint/js typescript typescript-eslint npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks npm install --save-dev eslint-config-prettier npm install --save-dev stylelint stylelint-config-standard-scss自动化工具npm install --save-dev husky lint-staged配置文件清单本文涉及的配置文件：.prettierrc.js - Prettier 配置.prettierignore - Prettier 忽略文件eslint.config.js - ESLint 配置（Flat Config）stylelint.config.js - Stylelint 配置.stylelintignore - Stylelint 忽略文件.husky/pre-commit - Git pre-commit 钩子.vscode/settings.json - VS Code 工作区配置package.json - Lint-staged 配置本文由IvanHanloth主写，使用Claude进行代码修正和文风调整，请注意检查潜在的错误
Boss Key V2.1.0版本正式发布啦！开源地址：IvanHanloth/Boss-Key (github.com)从这个版本开始，Boss Key将支持鼠标中键、侧键的按键隐藏，支持鼠标移至四角快速隐藏，支持指定时长无操作自动隐藏等超多使用功能，安全摸鱼！新版本中也加入了“进程冻结”功能，支持在隐藏窗口时自动冻结对应进程，减少资源占用。简单唠嗑难得新增这么多功能，所以发了个新的小版本。由于现在功能逐渐增多，我在考虑给Boss Key搭建一个官网或者文档来提供更具体的帮助。有些功能的名称可能对刚接触的用户有点懵逼，加个帮助功能应该会好一点。但是有熟悉博主的水友就要说了：主播主播，你的Easy-Send那么多star你都不给一个官网，Boss-Key才多少star，怎么你就要建官网了？主要Easy-Send好久没更新了，也基本没人用，没有建官网的必要，现有的文档已经足够了。Easy-Send主要是面向开发者的，很多功能对开发者而言探索难度不达。但是Boss Key主要是面向无技术的用户、普通用户，探索功能的时间少，需要更详细的描述，程序内又加不下……（编不下去了，反正就是要建一个（我不管））现在Boss-Key还有三个问题，一个是没有对win7进行兼容，我看有很多用户还在用win7，希望我兼容。其实之前是做过一些尝试的，但是不知道为啥，编译完的东西就是用不了……（而且win7都停止支持多久了，win10都已经停止支持了……怎么还有那么多用win7的（小声bb））另一个问题是容易被拦截的问题。软件被拦截，导致没有办法开机自启，比较麻烦，而且下载的安装包也可能报毒……难搞。想办法搞个证书……？没钱啊。最后一个问题是稳定性的问题，我本来打算把键盘、鼠标监听弄成后台服务的，但是谁知道python只能把脚本封装成windows服务，封装后会出问题……看来得用其他语言重写了……以后再说吧就唠这么多，来看看新的UI吧：倒是感觉有点成熟的工具的样子了……但是今天被同学吐槽说界面好丑……哪里丑了，这么多年来都是这个样子，哪里丑了（好吧，确实没办法跟那些用QT的比，但是我觉得小工具没有必要上Qt吧？（主要是我不会啊））。使用文档看README吧，等我找时间建一下官方文档~下载地址粉丝福利，评论免费下载https://hanloth.lanzn.com/b02k5dt5ab密码:2f0h
前情提要最近做了一个活动大屏项目，博主主要负责后端开发。因为活动大屏有实时增加用户的需求，所以需要创建websocket进行双向通信。后端采用Python Flask开发，运行在linux服务器中，为了提高并发性能，使用gunicorn进行守护并使用--workers=8创建了8个worker。使用nginx进行反向代理，使用eventlet作为classworker并打上了补丁：import eventlet eventlet.monkey_patch()使用Flask-socketio库搭配前端的socket.io实现前后端的信息交换（其实只是后端单向的向前端发送消息，本来可以使用长轮询，但是组长觉得服务器压力太大向想换成websocket（虽然socket.io本身是多种方式进行通讯的，包括长轮询和websocket））。博主表问题描述先说说主要表现吧：前端会反复报错，控制台提示服务器返回400（Failed to load resource: the server responded with a status of 400()），或者提示在完成连接建立前Websocket被关闭WebSocket is closed before the connection is established 。偶尔会出现连接成功但是刷新页面后就无法连接，此时打开网络捕获应该会发现大量Get请求，返回200、400并且sid相同的请求数量大概为3个。其中，状态码为200的请求是socketio用于获取sid的。400请求返回的值类似invalid session xxxxx：问题排查过程最开始觉得是nginx反代有问题，因为在本地测试环境跑是没有问题的，只有服务器上部署了nginx。防火墙之类的因为有正常200请求，直接排除。后来觉得是前端的session处理不对，但是负责前端的哥们的代码和socket.io官网的示例基本一模一样，不会有错，因此也排除。stackoverflow等等社区论坛上的内容看了一圈，基本都是在Node.js中用socket.io的，没有与flask搭配的，因此也没有参考性。在仔细看socket.io官方文档的时候发现，文档中提到了出现类似表现解决连接问题 | Socket.IO，但是这是在多个服务器负载均衡的情况下因为没有共享session出现的。考虑到gunicorn配置时使用了多个worker其中提到的是需要配置服务器粘性会话，于是我就去flask-socketio文档中查找了一下，找到了相关的内容：Deployment — Flask-SocketIO documentation，即，使用redis等中间件作为共享session的服务即可。但是配置完成后问题并没有的到解决（崩溃！！！！）于是我就崩溃的乱翻文档（什么奇怪的解压方式……）解压方式奇怪不重要，重要的是给我翻到解决方案了！就在上面链接对应的位置上方一点点Deployment — Flask-SocketIO documentation，关于gunicorn部署的说明这里，小小的写了一个由于gunicorn负载均衡存在问题，应该避免使用多worker（所以示例文档都是1个worker）找到原因所在，问题就非常好解决了解决方案既然是因为gunicorn多worker导致的，就不用多worker了：使用nginx进行反向代理和负载均衡，gunicorn专心守护进程即可（虽然感觉和直接python跑没有很大区别）因此：将gunicron配置修改为只使用一个worker，即-w 1（可以有多个threading）并启动多个占有不同端口的进程提供服务在nginx反向代理配置中使用负载均衡：http块下添加：upstream nodes{ ip_hash; server 127.0.0.1:45677; server 127.0.0.1:45676; server 127.0.0.1:45675; ## 添加所有你绑定的gunicorn服务端口 }sever块下添加反向代理配置（可以根据需要删除一些配置，timeout通常会更长一些）： location /{ proxy_pass http://nodes; proxy_set_header Host $host; proxy_http_version 1.1; #proxy_cache_bypass $http_upgrade; proxy_ssl_server_name on; # Proxy headers proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Forwarded $proxy_add_forwarded; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # Proxy timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } location /socket.io { proxy_pass http://nodes/socket.io; proxy_buffering off; proxy_set_header Host $host;proxy_http_version 1.1; #proxy_cache_bypass $http_upgrade; proxy_ssl_server_name on; # Proxy headers proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Forwarded $proxy_add_forwarded; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # Proxy timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; }可以自己写个基本自动启动并配置反向代理的端口，类似下图（由于闭源和保密，代码就不贴出来了~，实现不是很难，思路就是使用python脚本创建guniconrn进程，并在nginx配置文件里直接include一个由脚本生成的config来包括所有节点进行负载均衡的管理和以及热重启）反思由于gunicorn需要在linux环境下再能正常使用，我的电脑windows环境是没有办法运行的，所以在本地跑的时候都是直接用python的开发环境（甚至还有debug=True）来跑的。最开始没有怀疑gunicorn的原因是，我之前几个python项目都是用gunicron来跑的，不管是flask框架还是tornado框架都是没有任何问题的，下意识的觉得gunicorn的配置不会有问题。主要问题还是测试环境与本地环境不完全相同以及过于依赖过去的经验。2025/04/21补：注意注意！单worker多进程的时候也是需要部署redis的，需要在socketIO对象处增加message_queue参数，填写redis地址，同时需要安装redis模块，搭配使用。如果不用redis的话，会出现一个emit出错的问题。具体来说就是：假如A连到的是进程1，B连接到的是进程2。如果没有redis的话，进程1emit的内容只有A能收到，B是收不到的，只有使用redis的频道功能才能确保A和B都能收到emit。代码应该形如：socketio = SocketIO( app, cors_allowed_origins='*', message_queue="redis://xxxxxxxx", channel="socketio" )存在的一个问题是redis似乎不能设置密码，因为socketIO模块没有提供填写密码的地方……不过这在内网环境下都不算特别大的问题，我就没有管。如果是外网环境使用的就需要注意这里会产生的安全问题。
多端同步笔记软件Obsidian拓展指南、从OneNote转入指南【附速率限制解决方案】
Obsidian是世界上最好用的markdown多端同步笔记软件！！！！（不接受反驳）今天给大家带来Obsidian的一些拓展指南以及我在从OneNote转入时出现的一些问题的解决方案。顺带一提，从今天起，Obsidian已经完全免费提供软件的服务，包括个人和商业用途！！（此前个人使用免费，商业用途需要付费）也就是目前Obsidian只有在使用官方云同步时才需要进行订阅制的付费，其他功能都是免费的。拓展指南Obsidian有非常丰富、健全的生态，可以通过安装插件深度拓展Obsidian编辑器，提高笔记体验。也可以通过安装主题来改变编辑器的样式，可拓展性非常强。前提安装第三方插件需要首先关闭安全模式。[scode type="yellow" size=""]注意！安装插件的操作存在风险，社区中的第三方插件并不能保证一定安全，有极小概率出现恶意插件进行数据盗用等操作，关闭安全模式前应自行备份所有文件，并且该操作所带来的风险需要由您自己承担。[/scode]主题的安装和管理可以在“外观”选项卡找到，点击“主题”部分的“管理”可以打开主题市场，在左边的选择框中可以选择并使用已安装的主题 【插件】Remotely Save拥有1000万下载量的超强插件，用于将本地的笔记云同步至你信任的网盘等，可以作为官方同步的平替。支持S3、WebDav、Dropbox、R2、OneDrive、Google Drive等超多云盘在市场首页即可找到此插件，点击“安装”即可安装至本地。安装后在插件页面启用该插件即可在设置页面可以自行配置网盘同步相关的配置，此外强烈推荐调整下方框中的几个配置项，这样就能自动进行云同步，及时在其他设备看到修改而日常在使用时也可以通过点击左侧的图标进行同步移动端中也可以安装此插件进行配置的同步。【插件】ExcalidrawObsidian中最热门的插件，有超过3000万的下载量。该插件为Obsidian拓展出了手写画图的功能，推荐在移动端、平板端安装，可以写写画画在插件市场点击“安装”即可安装，点击“启用”以启用插件启用后，可以在左侧的菜单中找到“新建绘图文件”的功能，点击后即可创建一个画板支持在画板中随意写写画画，支持插入笔记、网页、文字、图片等，可以点击右键复制该画板的markdown插入代码，并将画板插入至笔记中【插件】Editing Toolbar很多新手想用obsidian但是却因为记不住markdown语法望而却步，这个插件提供了一个类似富文本编辑器的工具栏，可以通过类似word的方式编辑和排版笔记，对新手非常友好。在插件市场找到“Editing Toolbar”，点击“安装”即可安装，点击“启用”以启用插件。启用插件后可以在主编辑器上方找到添加的工具栏，点击即可快速对文本进行样式调整也可以在设置中调整工具栏的样式、位置等，使其更符合自己的记录习惯。【主题】Obsidianite这是个非常不错的深色主题，对markdown的各类文字进行了样式的修改，非常漂亮。唯一的缺点是没有适配浅色模式，浅色模式下部分美化效果会丢失。点击下载并使用即可下载并启用该主题主题预览：【主题】Blue Topaz非常美观简洁的主题，为不同类型的文字设置了不同的颜色，便于区分，在不同设备下的显示都非常不错【主题】Obsidian Nord灰色系的主题，为不同类型的文字设置了不同的颜色与字体，非常美观的配色，只是在浅色模式下文本的对比度偏低，分辨度较差OneNote转入指南Obsidian官方通过插件的形式提供了从其他笔记导入笔记的功能，使用导入功能需要关闭安全模式并安装 importer插件。该插件由Obsidian官方提供，支持从Apple Notes、Bear、Evernote（印象笔记）、Google Keep、Notion、OneNote、Roam Research、Textbundle等平台导入笔记，也导入支持HTML格式的笔记。进入插件市场搜索importer，选中图中由“Obsidian”开发的插件，点击“安装”安装插件并点击“启用”以启用插件启用插件后，可以在左边的菜单栏中看到Open Importer，点击后即可打开导入窗口点击Sign in按钮，在打开的页面中登录微软账户并授权Obsidian之后即可看到该账户下的所有笔记（注意，仅属于当前账号的笔记会显示，共享的笔记是无法导入的）选择需要导入的笔记后，点击“import”即可自动开始导入常见问题：提示OneNote速率限制提示“Microsoft OneNote has limited how fast notes can be imported. Please try again in 30 minutes to continue importing.”通常就算等30年也还是这个提示如果你使用的不是个人账户（包括各类工作的账户、学校账户等），则会出现该提示。推测是由于Obsidian创建的微软应用在设置授权权限出了问题。解决方案： 将笔记从工作账户导出，再导入至个人账户同步，再导入即可（曲线救国）在OneNote桌面版中登录工作账户，选中需要导出的笔记本，点击导出整个笔记本（.onepkg格式）切换个人账户，双击打开导出的 .onepkg文件，使用OneNote解压缩笔记文件找到解压缩到得地址，使用OneNote打开 .onetoc2格式的文件，此时会在本地打开这个笔记本。在个人账户下，点击右上角的“共享”，OneNote会弹出提示，将笔记上传至个人账户的OneDrive中（一定注意不是工作账户的！！！！）等待上传完成后，就可以在Obsidian中用个人账户登录并导入笔记本了另一个常见问题： 在OneNote在同步时出现无法同步更改等相关提示，这是由于你在云端可能已存在同名的笔记副本，在同步时出现了冲突。此时，你需要登录你的OneDrive，在笔记的文件夹下（一般是文档文件夹）删除与笔记同名的目录（注意小心误删），之后再重新分享或同步即可。
多端同步markdown笔记软件深度体验评测（附软件截图）——还在纠结用什么软件记笔记的同学快进来看看！
前情提要之前一直用的微软的OneNote来记笔记。最开始使用还是非常丝滑的。用了一个学期之后发现OneNote对学计算机的同学不算友好。主要是有些时候希望在笔记中记录一些代码或公式，OneNote中不能添加专门的代码块，没有代码高亮，并且偶尔还会对简拼的代码出现拼写检查的提示。其内置的公式编辑器不是纯血的Latex，而是阉割版的Word公式编辑器，只能局部使用latex语法（例如特殊符号等），编写公式时很难受，且在电脑端以外的设备上会出现兼容性问题……前两天终于忍不住了，决定转移阵地到其他Markdown笔记软件。注意！本文主要针对和我一样有以下需求的同学进行评测：1. 有多端同步需求2. 有公式编写需求3. 有代码插入需求4. 有图片插入需求5. 有笔记多级分类管理需求博主使用Windows+Android系统，仅苹果生态的软件不包括在内。评分满分5星，个人观点，仅供参考。Obsidian（⭐⭐⭐⭐）Obsidian是我最后选中使用的笔记软件。其提供跨平台的客户端，支持多端同步，支持离线编辑，对markdown进行了深度拓展，支持纯血Latex公式，支持代码高亮，支持从OneNote导入笔记。额外还支持文档发布、文档依赖分析、快捷指令、白板等功能。软件本身不收费，使用官方云同步需要按照订阅制收费，可以免费安装插件使用包括WebDav、OneDrive、GoogleDrive、S3、DropBox等主流云盘进行文件同步。他甚至还支持git评分理由：几乎完美的迎合了我的所有需要，界面非常干净整洁，也支持通过插件等进行功能的拓展。为数不多的缺点：对markdown不太熟悉的同学适应需要一段时间（编辑器没有提供传统富文本编辑器上方的样式按钮，但是可以通过右键进行添加或按下/通过快捷指令插入），没有提供快速调整图片大小的功能（只能通过添加标记修改大小）官网：Obsidian - Sharpen your thinkingNotion(⭐⭐⭐)事实上，我最后在Notion和Obsidian之间纠结了很长时间，最后放弃了Notion。这是一个非常非常非常棒的软件，他提供深度魔改版的Markdown（甚至只是用markdown作为创建文本块的标记）、可以任意调整顺序的文本块以及非常多的文本样式编辑辅助。支持多端同步（免费！！）、多人协同、纯血latex公式、图片同步、github仓库协同、提及、发布、链接到其他平台等超多功能。Notion个人使用免费，提供完美的多端同步，非常吸引人，并且功能针对不同用户群体做了特定的优化（学生、打工人等）。但是缺点在于Notion的功能过于“臃肿”，提供了包括Notion AI、日历、多人协同、日记版等作为笔记本不需要的功能，并且在网络不佳时输入会出现延时，代码高亮效果较差，没有快捷的公式输入标记（例如$$）。评分理由：非常棒的生产力工具及多人协同工具，但是对于用作笔记软件而言过于繁杂。重点是免费、多功能、多合一官网：在互联工作空间中管理 Wikis 百科、文档和项目｜NotionJoplin(⭐⭐⭐)Joplin是一个开源的同步markdown编辑器，完全免费。支持自己配置同步至Onedrive、WebDav、DropBox、S3等，支持多级笔记管理，支持直接插入图片等附件、支持自定义笔记排序、支持latex语法、支持代码高亮、支持多人协同、支持从OneNote导入。软件本身不收费，使用官方云同步需要按照订阅制收费评分理由： 不支持快速插入代码块、公式编辑比较麻烦、没有自动markdown标记补全功能、同步配置比较麻烦、Windows下不支持自定义安装位置官网：JoplinTypora(⭐⭐)看到太多Typora的推荐文章，吹的天花乱坠，其实个人感觉一般，就只是一个普通的本地Markdown编辑器（没有任何多端同步可言）。界面非常整洁，支持Latex公式、内置多个主题、支持原生markdown、支持可视化表格编辑、支持类似github的提示框、支持调整代码缩进软件本身买断制收费，提供15天试用评分理由： 没有多端同步、代码高亮效果一般、没有“项目”的概念进行管理、图片插入比较麻烦官网：Typora 官方中文站OneNote(⭐⭐⭐）虽然OneNote并不是一个markdown编辑器，但是在多端同步的笔记中算是非常不错的一个软件了，提供大部分与Word类似的文本编辑功能，支持随意摆放和插入图片、文字，支持Word的公式编辑器，支持同步手写痕迹，支持多人协同、评论等，与OneDrive及Office365深度融合官网：Microsoft OneNote | 适用于您的设备的数字记录笔记应用总结主推Obsidian和Notion两个软件，非常非常好用。国内流行的其他笔记，有道云、印象笔记我觉得功能上一般般还需要付费，看个人喜好吧。EverNote（国际版印象笔记）似乎还算不错，但是没有测试过，就且先不提了。最后附上一张来自知乎总结的一个图（原贴见：https://www.zhihu.com/question/507459346/answer/2503017821），可以用作参考
Lyrics-Downloader V1.2.3版本发布啦此版本提供了Windows 7系统专属封装版本，从下载链接中选择带有"win7"标识的程序下载并运行即可更新内容修复酷狗音乐偶现搜索出错的问题优化界面设计优化运行速度新增Windows7系统专属封装版本项目简介Lyrics Downloader是用于一键批量从音乐网站搜索下载音乐歌词的软件，只需简单配置即可为本地的音乐配上歌词。歌词一键批量下载器项目首发于吾爱破解，由Ivan Hanloth开发使用方法下载后双击打开软件。根据需要选择歌词搜索平台，之后选择音乐所在的文件夹路径并配置后缀名筛选，多个后缀名之间用管道符连接（如：.mp3|.flac），选择歌词需要的编码，点击“开始下载”即可开始下载。为了方便下次使用，可以点击“保存配置”，则下次启动时也将使用本次的配置。歌词下载完成后会自动弹出系统提示。软件截图![1728149916033.jpg]()下载地址粉丝福利，回复直接领取链接：https://hanloth.lanzn.com/iRGVC2bqnmxi密码：c0yx关于安全性程序使用Python+Nuitka编写，进行了单文件封装同时使用了OS等相对较敏感的模块，可能会被杀毒软件识别为木马，但是软件本身安全无毒，放心下载。最新版安全检测报告：腾讯哈勃： https://habo.qq.com/file/showdetail?pk=ADYGY11qB2AIPVs6U2oHYw%3D%3D安恒云沙箱： https://sandbox.dbappsecurity.com.cn/report/dfb6831b-c986-4aa9-b7d4-5316d2590149声明本软件仅提供音乐歌词下载，歌词来源于各大音乐网站，仅供个人学习交流使用。常见问题汇总win7等系统使用出现系统dll文件缺失等通常是由C/C++库、系统dll文件缺失导致的，使用过于精简的系统可能出现这样的问题，由于使用的python版本较新，已经停止对于较低系统版本的支持，可以尝试使用Direct X repair修复工具进行修复。或者使用Win7专供版本（下载链接中带有“win7”标识的文件）lrc无法被识别由于现在各类播放器兼容的编码不相同，无法做到完全的统一，该问题将在近期的版本更新中增加用户自定义编码功能为什么无法下载音乐？请注意！！！本程序只是对已经下载至本地的音乐完成歌词下载适配，本身并不带有音乐解析下载的功能！如果你是为了下载音乐，请移步其他相关工具。
Boss Key V2.0.0版本正式发布啦！开源地址：IvanHanloth/Boss-Key (github.com)从这个版本开始，Boss Key将支持对多窗口进行隐藏，可以设置中进行窗口绑定更新日志V2.0.2 （更新于2025/2/2）新增全选窗口选项新增隐藏窗口后同时隐藏程序托盘图标选项修复无法使用热键关闭程序的问题优化界面UI设计优化配置文件读取应用截图软件介绍Boss-Key老板键是一款开源、免费、简洁的老板键程序，仅需一键即可隐藏并静音当前活动窗口，是一款上班摸鱼神器。使用方法下载后解压，运行Boss-Key.exe即可打开首次启动会自动弹出快捷键设置弹窗，可以配置需要的热键后续可从托盘图标中打开设置弹窗。右键单击托盘图标打开菜单，可以启用“开机自启”选项关于安全性由于使用到大量系统API，并使用Nuitka编译，可能会被杀毒软件误报为病毒，但是软件本身不含任何木马。同时软件已开源，可查看开源地址下载地址粉丝福利，评论免费下载https://hanloth.lanzn.com/b02k5dt5ab密码:2f0h
简介在3.1节中，我们使用Nand逻辑门完成了对基础逻辑门的构建，而在本节中，我们通过使用基础逻辑门，逐步构建起计算机的核心部件之一，算数逻辑单元（Arithmetic Logic Unit，ALU）。为了适应不同学习需要，本节下分三个小节：布尔算数运算构建基础算数逻辑芯片构建算数逻辑单元布尔算数运算部分，将介绍不同进制之间的转化方法、正数负数的二进制表示、二进制数的加减法，包括补码、反码等计算机基础概念，如果你已熟练掌握这些内容，则可以跳过这一部分。构建基础算数逻辑芯片部分，将从半加器开始，慢慢运用二进制加法规则，逐渐使用HDL实现二进制数加减法、递增等基础算数运算芯片。构建算数逻辑单元部分，将综合运用前面两个部分的内容，将二进制运算集中为一个完整的逻辑芯片中，组建成算术逻辑单元ALU。ALU与现代计算机构成算数逻辑单元（Arithmetic Logic Unit，ALU）是现代计算机的核心组件之一，其负责执行所有的算数和逻辑操作，包括加法、减法、乘法、除法以及与、或、非等基本逻辑运算。可以说，计算机能够处理和计算数据，全仰赖于ALU的高效工作。在计算机系统中，ALU通常嵌入在中央处理单元（CPU）内部。CPU依靠ALU来完成各类运算任务，而这些任务是运行程序和操作系统的基础。ALU的性能直接影响到整个计算机的处理速度和效率，因此其设计和优化是计算机体系结构中的关键环节。由于我们构建的计算机只是相对较小的计算机，因此在Nand2Tetris中，我们构建的ALU负责以下任务：逻辑：取与、取或、取非算数：归零、置1或-1、取原、取反、递增、递减、求和、求差总共有16种不同的输出（实际ALU共有64种运算）其逻辑门表示如下：
简介从第2.2节我们可以知道，所有的基础逻辑门都可以通过Nand逻辑门不断组合实现，但是一直只在芯片中使用Nand来构建其他逻辑芯片的话，过程过于繁琐，因此，我们将在使用Nand构建基础逻辑门之后，通过逻辑门的复用来简化我们的设计，因此此节主要是使用Nand构建基础逻辑门，并基于基础逻辑门构造其他的一些基础逻辑芯片。Nand芯片是一个预设芯片，表示Not-And（或 Not(And(a,b))，在上一章节有提及）true是预设值，表示1（可为任意位数）false是预设值，表示0（可为任意位数）预设芯片和预设值可以在任意位置直接调用本节对应项目1（Nand2Tetris所在目录/projects/1），所有文件基础框架、测试文件均在项目文件夹中。请通过编辑、完善项目文件夹中的 .hdl文件，按照顺序完成下面的内容。（逻辑门与芯片在此教程以及Nand2Tetris是同等概念，但是通常认为逻辑门是相对较小的基础逻辑单元，而芯片则是相对较大的组合逻辑单元）如果你还没有掌握Nand2Tetris的基础、基础布尔运算、Hardware Simulator的使用，请先阅读第2章[post cid="746" cover="" size="small"/]构建基础逻辑门CHIP NotNot门的逻辑在前面的章节有所提及，其标准如下：名称：Not输入：in输出：out功能：如果in为1，则out为0；如果in为0，则out为1测试文件：Not.tstCHIP AndAnd门的逻辑在前面的章节有所提及，其标准如下：名称：And输入：a, b输出：out功能：只有a与b均为1时out为1，否则为0测试文件：And.tstCHIP OrOr门的逻辑在前面的章节有所提及，其标准如下：名称：Or输入：a, b输出：out功能：当a与b中存在1时为1，否则均为0测试文件：Or.tstCHIP XorXor，即异或（Exclusive or）其标准如下：名称：Xor输入：a, b输出：out功能：当a与b同为1或同为0时，为0，否则为1测试文件：Xor.tstXor的真值表如下：abXor(a,b)110101011000逻辑门表示为：[collapse status="false" title="一种可能的逻辑"]通过And(a,Not(b))筛选出其中的第二行，也就是两者不相等的情况之一再通过And(Not(a),b)筛选出其中的第三行，也就是另一种两者不相等的情况abNot(a)Not(b)And(a,Not(b))And(Not(a),b)110000100110011001001100此时，将两个表达式计算得到的值进行或运算（Or），即可得到实际Xor的目标值，即 Xor(a,b)=Or(And(a,Not(b)),And(Not(a),b))通过逻辑门可表示为[/collapse]CHIP MuxMux，复用器（Multiplexor），其标准如下：名称：Mux输入：a, b, sel输出：out功能：当sel为1时，输出b，否则输出a测试文件：Mux.tstMux的真值表如下：abselout11111010011100101101100101000000逻辑门表示为：[collapse status="false" title="一种可能的逻辑"]part1=And(Not(sel),a)，在sel为1的时候，返回的是0，sel为0时，返回的是a的值part2=And(sel,b)，在sel为1的时候，返回的是b的值，sel为0时，返回的是0Or(part1,part2)，去除为0的情况[/collapse]CHIP DMuxDMux，解复用器（Demultiplexor），其标准如下：名称：DMux输入：in, sel输出：a, b功能：当sel为1时，a为0，b为in，当sel为0时，a为in，b为0测试文件：DMux.tstMux的真值表如下：selab10in0in0逻辑门表示为[collapse status="false" title="一种可能的逻辑"]a=Mux(0,in,sel)当sel为1时，a为in，当sel为0时，a为0b=Mux(in,0,sel)当sel为1时，b为0，当sel为0时，b为in[/collapse]构建多位基础逻辑门上面的所有逻辑门，都只能接受“1位”的数据，也就是，每个输入与输出都只能是1位的数据（0/1）。但是实际构建计算机时，我们需要能够处理多位数据线（或者总线，bus）的数据，可能是16位、32位、64位或者更高位数。Nand2Tetris目标构建的计算机是16位的计算机，因此我们主要构建16位的芯片。我们通过类似数组的方式获取不同的位上的数据。通过下面的方式指定数据的位数：<引脚名称>[数据位数]通过下面的方式获取数据的指定位置的数据（数据索引从0开始，最右边的为第0位，从右往左递增）：<引脚名称>[数据索引]假设有一个二进制数据 a[16]，其值为7591，二进制表示为 0001 1101 1010 0111，如果需要取右起第3个1，则使用 a[2]，如果需要取左起第4位的1，则应使用 a[12]HDL可以使用索引切片截取一个数据的其中一部分，其语法如下：<引脚名称>[开始索引..结束索引]例如，在上面的例子中，如果希望截取 1101这一段数据，应该使用 a[8:11]来截取CHIP Not1616位的Not，标准如下：名称：Not16输入：in[16]输出：out[16]功能：对in的每一位取Not运算测试文件：Not16.tst[collapse status="false" title="一种可能的逻辑"]out[0]=Not(in[0])对第一位取反out[1]=Not(in[1])对第二位取反……[/collapse]CHIP And1616位的And，标准如下：名称：And16输入：a[16], b[16]输出：out[16]功能：对a与b的每一位取And运算测试文件：And16.tst[collapse status="false" title="一种可能的逻辑"]out[0]=And(a[0],b[0])对第一位取与out[1]=And(a[1],b[1])对第二位取与……[/collapse]CHIP Or1616位的Or，标准如下：名称：Or16输入：a[16], b[16]输出：out[16]功能：对a与b的每一位取Or运算测试文件：Or16.tst[collapse status="false" title="一种可能的逻辑"]out[0]=Or(a[0],b[0])对第一位取或out[1]=Or(a[1],b[1])对第二位取或……[/collapse]CHIP Mux1616位的Mux，标准如下：名称：Mux16输入：a[16], b[16], sel输出：out[16]功能：根据sel的值，对每一位取Mux运算测试文件：Mux16.tst[collapse status="false" title="一种可能的逻辑"]out[0]=Mux(a[0],b[0],sel)对第一位取复位器out[1]=Mux(a[1],b[1],sel)对第二位取复位器……[/collapse]构建多位多路逻辑门构建计算机时，常常会重复用到一些芯片，如果一直重复只使用基础的逻辑芯片来实现功能，会使得描述显得臃肿、繁杂。为了避免这样的情况出现，我们需要封装一些使用率、重复率较高的芯片集合为一个整体来使用，这便是多路逻辑门（Multi-Way）。与多位逻辑门相似，多路逻辑门会对基础逻辑芯片进行封装，但是不同的是，多位逻辑门是用于处理一个更“长”的数据，而多路逻辑门是对单个重复逻辑的简化。多位多路逻辑门则是多路封装接受多位数据的逻辑门。多位多路逻辑门的命名有一定规范：<基础芯片名><封装规格>Way<数据位数>例如，Or8Way就是对Or逻辑进行的封装，封装了8路，每个数据1位（1忽略不写），Mux4Way16则是对Mux逻辑进行封装，封装了4路，每个数据16位（或者也可以理解为对Mux16进行了4路封装）。没理解？没关系，先从简单的入手：CHIP Or8Way8路的Or，标准如下：名称：Or8Way输入：in[8]输出：out功能：对in的每一位取Or，即out=in[0] or in[1] or ... or in[7]测试文件：Or8Way.tst[collapse status="false" title="一种可能的逻辑"]out=Or(...Or(Or(Or(a[0],a[1]),a[2]),a[3])...,a[7])[/collapse]CHIP Mux4Way164路的Mux16，标准如下：名称：Mux4Way16输入：a[16], b[16], c[16], d[16], sel[2]输出：out[16]功能：如果sel=00，则返回a，如果sel=01，则返回b，如果sel=10，则返回c，如果sel=11，则返回d测试文件：Mux4Way16.tst其逻辑门可以表示为[collapse status="false" title="一种可能的逻辑"]out=Mux16(Mux16(a, b, sel[0]), Mux16(c, d, sel[0]), sel[1])先用一次Mux16，根据sel[0]在a/b之间，c/d之间选出正确的值，再根据sel[1]选择具体要返回哪一个[/collapse]CHIP Mux8Way168路的Mux16，标准如下：名称：Mux8Way16输入：a[16], b[16], c[16], d[16], e[16], f[16], g[16], h[16], sel[3]输出：out[16]功能：在下表中，根据sel的值对应输出in的值到outselout000a001b010c011d100e101f110g111h测试文件：Mux8Way16.tst[collapse status="false" title="一种可能的逻辑"]out=Mux16(Mux4Way16(a, b, c, d, sel[0..1]), Mux4Way16(e, f, g, h, sel[0..1]), sel[2])先用一次Mux4Way16，根据sel[0..1]在a/b/c/d之间，e/f/g/h之间选出正确的值，再根据sel[1]选择具体要返回哪一个[/collapse]CHIP DMux4Way4路的DMux，标准如下：名称：DMux4Way输入：in, sel[2]输出：a, b, c, d功能：在下表中，根据sel的值对应输出in到abcd四个值selabcd00in000010in001000in011000in测试文件：DMux4Way.tst其逻辑门可以表示为[collapse status="false" title="一种可能的逻辑"]ab,cd=DMux(in,sel[1])a,b=DMux(ab,sel[0])c,d=DMux(cd,sel[0])先用一次DMux，根据sel[1]解开ab的值和cd的值，之后再用DMux根据sel[0]解开a/b和c/d[/collapse]CHIP DMux8Way8路的DMux，标准如下：名称：DMux8Way输入：in, sel[2]输出：a, b, c, d, e, f, g, h功能：在下表中，根据sel的值对应输出in的值到outselabcdefgh000in00000000010in00000001000in00000011000in00001000000in00010100000in00110000000in01110000000in测试文件：DMux8Way.tst[collapse status="false" title="一种可能的逻辑"]abcd,efgh=DMux(in,sel[2])a,b,c,d=DMux4Way(abcd,sel[0..1])e,f,g,h=DMux4Way(efgh,sel[0..1])先用一次DMux，根据sel[2]解开abcd的值和efgh的值，之后再用DMux4Way根据sel[0..1]解开a/b/c/d和e/f/g/h[/collapse]结语到这里，恭喜你，你已经完成了所有基础逻辑门的构建。如果你独立完成了所有芯片的构建并通过测试，说明你已经非常好的掌握了基础逻辑以及HDL的编写，这是学习计算机基础路途上的一大步。如果你经过思考，发现独立实现过于困难，依赖“一种可能的逻辑”才完成了完整的构建，那么没关系，至少你已经掌握了怎么编写HDL。那么你可以考虑再花费一些时间，思考如何用其他逻辑组合来实现相同的功能。这对你后面的部分有很大帮助。那么到这里为止，我们已经完成了project1中的所有内容，接下来的章节则会继续完成后面的项目。如果你对HDL语言的基础掌握还不牢靠，或者还不会进行HDL文件的测试、不能熟练使用Hardware Simulator，那么建议你再认真阅读一下前面的教程，那会非常有帮助。