\n\u003C/template>\n```\n\n至此,我们就可以通过 `/`、`/about` 来访问俩个页面了。免去了使用 `vue-router` 配置路由的过程。\n\n### 2-1、嵌套路由\n\n文件结构:\n\n```bash\n-| pages/\n--| parent/\n---| child.vue\n--| parent.vue\n```\n\n这将生成如下路由:\n\n```ts\n[\n {\n path: '/parent',\n component: '~/pages/parent.vue',\n name: 'parent',\n children: [\n {\n path: 'child',\n component: '~/pages/parent/child.vue',\n name: 'parent-child'\n }\n ]\n }\n]\n```\n\n要展示 `child.vue`,需要在 `pages/parent.vue` 中写入 `\u003CNuxtPage />`\n\n```vue\n\u003C!-- pages/parent.vue -->\n\u003Ctemplate>\n \u003Cdiv>\n \u003Ch1>I am the parent view\u003C/h1>\n \u003CNuxtPage :foobar=\"123\" />\n \u003C/div>\n\u003C/template>\n```\n\n```vue\n\u003C!-- pages/parent/child.vue -->\n\u003Cscript setup lang=\"ts\">\nconst props = defineProps(['foobar'])\n\nconsole.log(props.foobar)\n\u003C/script>\n```\n\n### 2-2、动态路由\n\n我们可以把任何东西写入方括号使它成为动态路由参数\n\n```bash\n-| pages/\n--| index.vue\n--| users-[group]/\n---| [id].vue\n```\n\n当我们访问路由 `/user-admin/123` 时,就可以通过 `useRoute()` 获取到路由参数\n\n```vue\n\u003Cscript setup lang=\"ts\">\nconst route = useRoute()\n\nconsole.log(route.params.group) // admin\nconsole.log(route.params.id) // 123\n\u003C/script>\n```\n\n### 2-3、全局路由\n\n文件结构:\n\n```bash\n-| pages/\n--| [...slug].vue\n```\n\n这将匹配全部路由,比如当跳转 `/hello/world`,将获取到所有参数:\n\n```vue\n\u003Cscript setup lang=\"ts\">\nconst route = useRoute()\n\nconsole.log(route.params.slug) // ['hello', 'world']\n\u003C/script>\n```\n\n### 2-4、路由组\n\n当不希望影响路由且需要将一部分路由文件放在同一个文件夹的时候,可以将文件夹使用圆括号包裹:\n\n```bash\n-| pages/\n--| index.vue\n--| (marketing)/\n---| about.vue\n---| contact.vue\n```\n\n这将生成 `/`、`/about`、`/concat` 三个路由,`marketing` 将被忽略。\n\n### 2-5、路由元数据\n\n可以使用 `definePageMeta` 为每个路由定义元数据,`definePageMeta` 宏由 `Nuxt` 提供。\n\n```vue\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n title: 'My home page'\n})\n\u003C/script>\n```\n\n然后通过 `route` 获取元数据:\n\n```vue\n\u003Cscript setup lang=\"ts\">\nconst route = useRoute()\n\nconsole.log(route.meta.title) // My home page\n\u003C/script>\n```\n\n### 2-6、路由跳转\n\n`Nuxt` 提供了路由跳转的方案 `navigateTo()`,基本使用方法与 `vue-router` 路由跳转一致。\n\n```vue\n\u003Cscript setup lang=\"ts\">\nnavigateTo({ path: '/', query: { id: 'xxxxx' }, replace: true })\n// nuxt 基于文件的路由会自动生成 name\nnavigateTo({ name: 'about', params: { id: 'xxxx' }, replace: true })\n\u003C/script>\n```\n\n!!! warning 警告\n在模板语法内使用 `navigateTo()` 会导致错误!!!\n\n```vue\n\u003Ctemplate>\n \u003Cdiv>\n \u003C!-- 此方法不被允许 -->\n \u003Cbutton @click=\"() => navigateTo('/')\">首页\u003C/button>\n\n \u003C!-- 使用 NuxtLink 替代 -->\n \u003CNuxtLink to=\"/\">首页\u003C/NuxtLink>\n \u003C/div>\n\u003C/template>\n```\n\n!!!\n\n当然,我们也可以使用 `vue-router` 进行跳转。\n\n```vue\n\u003Cscript setup lang=\"ts\">\nconst router = useRouter()\nrouter.push({ path: '/', query: { id: 'xxx' }, replace: true })\nrouter.push({ name: 'about', params: { id: 'xxx' }, replace: true })\n\u003C/script>\n```\n\n此外,`navigateTo()` 还提供了一部分支持\n\n- 导航到外部链接\n\n```vue\n\u003Cscript setup lang=\"ts\">\n// 这将不被允许\nnavigateTo('https://nuxt.com')\n\n// 正确方法\nnavigateTo('https://nuxt.com', {\n external: true\n})\n\u003C/script>\n\n```\n\n- 打开新标签页\n\n```vue\n\u003Cscript setup lang=\"ts\">\nnavigateTo('https://nuxt.com', {\n open: {\n target: '_blank'\n }\n})\n\u003C/script>\n```\n\n### 2-8、客户端/服务端页面\n\n在定义页面时添加 `.client.vue` 或者 `.server.vue` 后缀来定义客户端/服务端页面。\n\n!!! info\n这里先补充一部分知识:只有在 `setup` 顶层的代码会被服务端执行,交互式代码执行都在客户端(比如下方的 `onClick` )。\n!!!\n\n- 客户端页面\n\n```vue\n\u003C!-- pages/search.client.vue -->\n\u003Cscript lang=\"ts\" setup>\nconsole.log('client')\n\nconst onClick = () => {\n console.log('click')\n}\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n this is client page\n \u003Cbutton @click=\"onClick\">测试\u003C/button>\n \u003C/div>\n\u003C/template>\n```\n\n当我们访问 `/search` 时,`Node` 终端并不会打印出 `client`,在我们的浏览器控制台才会出现,且 `onClick` 事件正常触发。\n\n- 服务端页面\n\n```vue\n\u003C!-- pages/about.server.vue -->\n\u003Cscript setup lang=\"ts\">\nconsole.log('server')\n\nconst onClick = () => {\n console.log('click')\n}\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n this is server page\n \u003Cbutton @click=\"onClick\">测试\u003C/button>\n \u003C/div>\n\u003C/template>\n```\n\n这里注意一个问题:服务端页面在客户端是可以正常访问的,区别在于当我们访问 `/about` 时,`Node` 终端能够打印出 `server`,而在浏览器控制台中不会出现,且 `onClick` 事件不再生效。\n\n# 三、Layouts\n\n当多个页面存在相同的内容时,例如:页头和页脚,就可以使用 `layout` 来包裹页面。\n\n在 `app.vue` 中添加 `\u003CNuxtLayout>` 包裹 `\u003CNuxtPage>`:\n\n```vue\n\u003Ctemplate>\n \u003CNuxtLayout>\n \u003CNuxtPage />\n \u003C/NuxtLayout>\n\u003C/template>\n```\n\n添加 `layouts/default.vue`:\n\n```vue\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cp>Default Layouts\u003C/p>\n \u003Cslot />\n \u003C/div>\n\u003C/template>\n```\n\n## 1、自定义 Layout\n\n添加自定义 `layout`:\n\n```bash\n-| layouts\n--| default.vue\n--| custom.vue\n```\n\n在需要使用的页面中指定 `layout`:\n\n```vue\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n layout: 'custom'\n})\n\u003C/script>\n```\n\n## 2、全局指定 Layout\n\n```vue\n\u003C!-- app.vue -->\n\u003Cscript setup lang=\"ts\">\nconst layout = \"custom\";\n\u003C/script>\n\n\u003Ctemplate>\n \u003CNuxtLayout :name=\"layout\">\n \u003CNuxtPage />\n \u003C/NuxtLayout>\n\u003C/template>\n```\n\n## 3、动态改变 Layout\n\n```vue\n\u003Cscript setup lang=\"ts\">\n// 关闭默认 layout\ndefinePageMeta({\n layout: false\n})\nfunction enableCustomLayout() {\n setPageLayout('custom')\n}\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cbutton @click=\"enableCustomLayout\">Update layout\u003C/button>\n \u003C/div>\n\u003C/template>\n```\n\n## 4、页面中单独使用 Layout\n\n```vue\n\u003C!-- layouts/custom.vue -->\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cheader>\n \u003Cslot name=\"header\">\n Default header content\n \u003C/slot>\n \u003C/header>\n \u003Cmain>\n \u003Cslot />\n \u003C/main>\n \u003C/div>\n\u003C/template>\n```\n\n```vue\n\u003C!-- pages/index.vue -->\n\u003Cscript setup lang=\"ts\">\n// 关闭默认 layout\ndefinePageMeta({\n layout: false\n})\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003CNuxtLayout name=\"custom\">\n \u003Ctemplate #header> Some header template content. \u003C/template>\n\n The rest of the page\n \u003C/NuxtLayout>\n \u003C/div>\n\u003C/template>\n```\n\n# 四、过渡动画\n\n`Nuxt` 支持 `page` 以及 `layout` 过渡动画。\n\n## 1、全局设置\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n app: {\n // name 要与动画效果的样式对应\n pageTransition: { name: 'fade', mode: 'out-in' },\n layoutTransition: { name: 'fade', mode: 'out-in' }\n }\n})\n```\n\n```vue\n\u003C!-- app.vue -->\n\u003Ctemplate>\n \u003CNuxtLayout>\n \u003CNuxtPage />\n \u003C/NuxtLayout>\n\u003C/template>\n\n\u003Cstyle scoped>\n/*\n 此处先将动画样式写在 app.vue 中,后续讲解到静态资源篇时,再考虑移入全局样式里。\n 此处动画可自定义\n*/\n.fade-enter-active,\n.fade-leave-active {\n transition: opacity 0.5s ease;\n}\n.fade-enter-from,\n.fade-leave-to {\n opacity: 0;\n}\n\u003C/style>\n```\n\n## 2、页面单独设置\n\n- 页面过渡效果\n\n```vue\n\u003C!-- pages/xxx.vue -->\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n pageTransition: {\n // 动画效果自行定义到 app.vue\n name: 'custom'\n }\n})\n\u003C/script>\n```\n\n- layout 过渡效果\n\n```vue\n\u003C!-- pages/xxx.vue -->\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n layout: 'custom',\n layoutTransition: {\n name: 'custom'\n }\n})\n\u003C/script>\n```\n\n## 3、禁用动画\n\n- 页面单独设置\n\n```vue\n\u003C!-- pages/xxx.vue -->\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n pageTransition: false,\n layoutTransition: false\n})\n\u003C/script>\n```\n\n- 全局禁用\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n app: {\n pageTransition: false,\n layoutTransition: false\n }\n})\n```\n\n## 4、钩子函数\n\n```vue\n\u003C!-- pages/xxx.vue -->\n\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n pageTransition: {\n name: 'fade',\n mode: 'out-in',\n onBeforeEnter: (el) => {\n console.log('Before enter...')\n },\n onEnter: (el, done) => {},\n onAfterEnter: (el) => {}\n }\n})\n\u003C/script>\n```\n\n## 5、直接添加到 NuxtPage\n\n```vue\n\u003C!-- app.vue -->\n\u003Ctemplate>\n \u003Cdiv>\n \u003CNuxtLayout>\n \u003CNuxtPage :transition=\"{\n name: 'bounce',\n mode: 'out-in'\n }\" />\n \u003C/NuxtLayout>\n \u003C/div>\n\u003C/template>\n```\n\n除此之外,过渡动画还包括一些高级应用,例如:**动态过渡效果** 等等,建议参考 [官方文档](https://nuxt.com/docs/getting-started/transitions) 。\n\n# 五、尾话\n\n`Nuxt` 系列教程基础篇(页面)到此也基本结束,总体感觉不是特别好😞。在我初步计划里此教程只包括一些简单、基础的使用,不会有这么多内容。但是在联合官方文档整合内容时,总感觉这里缺一块那里缺一块也就没什么东西了,且一部分内容也是基础性的,在我初步计划中缺失的,导致后续不断拼凑直接搞出了这么多东西。之后的篇章可能会多花些时间去尽可能简化内容(当然,**数据获取篇** 是不可能的,我初步计划里这篇就很复杂,需要考虑的东西很多,会尽可能全面的呈现出来)。\n","published",[25,29],{"id":26,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":27,"count":28},"0d74dd1ce88b406f95baf97ef4aaeba5","Nuxt",0,{"id":30,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":31,"count":28},"b275fc6d0c804757ba835246eb54aaee","前端",{"id":33,"createdAt":34,"updatedAt":34,"creatorId":12,"modifierId":12,"title":35,"sketch":36,"coverId":37,"cover":38,"content":43,"status":23,"tags":44},"8ac5e8dda0a7426f88aebde8280b2b5c","2025-04-22","Nuxt 系列 - 开篇","介绍一些基础概念、新建项目、规划此系列的后续","095906fc6e4c4ac0aed990709e87d4ba",{"id":37,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":39,"size":40,"objectName":41,"url":42},"nuxt-start.jpg",170195,"ac39fdb8-d331-445c-99c9-5567e8b2079b.jpg","https://wlitd.com/for-the-worthy/ac39fdb8-d331-445c-99c9-5567e8b2079b.jpg","# 一、前言\n\n大约在此博客第一个版本(`Vue`)完成一俩个月之后,因考虑对搜索引擎的支持进而将目光转向了 `Nuxt`,真是一入“侯门”深似海啊。难以想象作为一个比较成熟的开源框架,网上的资料那么少且一步一个坑。随后便萌生出将自己将自己的踩坑之旅整理为一个相对完善且简单的 `Nuxt` 系列教程,后续因为时间、写作思路始终没有确定等等一系列原因,导致 `Nuxt` 重构都已经完成了,记录还没写😅,可怜了我存活长达半年的 `Vue` 版本。问题总是接踵而至,重构部署之后我发现服务器内存到了临界边缘了,导致 `Nacos` 时不时就宕机(也不知道之前怎么想的,脑抽要用微服务),之前就有用Go重构后端的想法,顺便添加一些需要的新功能,比如我目前最需要的保存为草稿(对于我这种写作废物来说,一篇文章可能需要反复的删改且耗时n天才能完成),如今更加坚定。不过这都是后话了,现在还是先完成这个系列教程吧。\n\n# 二、基础概念\n\n虽然我也比较厌烦这些枯燥乏味的概念,但是基础的认知还是有必要的。\n\n## 1、服务端渲染(Server-Side Rendering)\n\n### 1-1、单页应用(SPA)\n\n在说 `SSR` 之前我们先聊聊单页应用(SPA),`Vue` 就是一个单页应用框架,虽然它也支持 `SSR` 实现,但是实现过程过于复杂。官方也建议使用一种更通用的、更集成化的解决方案,`Nuxt` 就是其中之一。单页应用是指在客户端(也就是我们使用的浏览器)加载初始 `HTML`、`CSS` 和 `JavaScript` 后,后续通过执行 `JavaScript` 代码,并由前端框架动态渲染 `DOM`,这就意味着客户端第一时间是没有完整的 `HTML` 的,搜索引擎爬虫难以获取动态内容,同时客户端需要时间执行代码导致页面加载速度也会相应减慢。\n\n### 1-2、SSR\n\n在了解了单页应用后,理解 `SSR` 就容易多了,服务端渲染是指在服务端(这里区别理解于我们常说的后端)生成完整的 `HTML` 发送到客户端进行展示。\n\n相较于单页应用,`SSR` 的优势有:\n\n- **更快的首屏加载**:客户端已经请求到了完整渲染的页面。除此之外,数据获取过程在首次访问时在服务端完成,相比于从客户端获取,可能有更快的数据库连接。\n- **更好的 `SEO`**:搜索引擎爬虫可以获取到完全渲染的页面。\n\n劣势有:\n\n- **开发部分**:使用 `Vue` 实现 `SSR` 过于复杂,而使用其它开源框架又有学习成本。除此之外,还需要考虑使用的插件是否能支持 `SSR`,如果不支持,是否有替代或者解决方案。\n- **服务器负载**:服务端渲染的应用需要一个能让 `Node` 服务器运行的环境,不像完全静态的 `SPA` 那样可以部署在任意的静态文件服务器上,对于服务器可能会有更高的 `CPU` 和 `内存` 要求。\n\n!!! warning 真的需要吗?\n在你使用 `SSR` 之前,确保你真的需要它。如果对于 `首屏加载速度` 以及 `SEO` 的要求没有那么高的话,不是很有必要入坑。\n!!!\n\n## 2、Nuxt\n\n一个构建于 `Vue` 生态系统之上的全栈框架,它为编写 `Vue SSR` 应用提供了丝滑的开发体验(熟悉之后确实丝滑😆)。\n\n部分特性(可配置覆盖):\n\n- **基于文件的路由**:根据 `page` 文件夹自动生成路由,免去了使用 `vue-router` 时需要的大段大段的路由配置。\n- **代码分割**:`Nuxt` 会自动将代码分割成更小的块,这有助于减少应用程序的初始加载时间。\n- **SSR**\n- **自动导入**:在使用 `composables` 和 `components` 文件夹下的导出时不再需要导入,有点像那个 `auto import` 插件,不过只针对这俩个文件夹。\n- **数据获取工具**:`Nuxt` 提供对 `SSR` 友好的数据获取方式,不再需要自己引入 `axios` 等。\n- **零配置的 `TypeScript` 支持**\n- **配套的构建工具**\n\n# 三、新建项目\n\n## 1、`Nuxt` 初始化\n\n!!! info 基础环境\n\n- `Node` - `18.x` 或者更新\n !!!\n\n打开终端执行命令,根据自己的需求一路 `y` 下去就完了。\n\n```bash [id:npm]\nnpm create nuxt \u003Cproject-name>\n```\n\n```bash [id:pnpm]\npnpm create nuxt \u003Cproject-name>\n```\n\n```bash [id:yarn]\nyarn create nuxt \u003Cproject-name>\n```\n\n可能会出现错误\n\n```bash\nError: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json: TypeError: fetch failed\n```\n\n如何处理:“科学”上网或者网上一堆配置来配置去的,这里给出我的解决方案。\n\nWin11 -> 设置 -> 网络和 Internet -> 高级网络设置 -> WLAN -> 更多适配器选项\n\n\n\n进入到项目根目录\n\n```bash\ncd \u003Cproject-name>\n```\n\n运行\n\n```bash\nnpm run dev\n```\n\n## 2、适配\n\n目前 `Nuxt` 最新版本已经到了 `3.16.x` 了,考虑到后续需要集成 `Naive UI`,此库在 `Nuxt 3.16.x` 会出现问题,截止2025-04-24暂未修复,所以需要降低一下版本。\n\n```json\n// package.json\n// 将版本固定至3.15的最新版\n\"nuxt\": \"3.15.4\",\n```\n\n```bash\nnpm i nuxt@3.15.4\n```\n\n## 3、目录结构\n\n初始化完成后可能不一定有所有的目录,缺哪个直接新建出来就完了,后续的教程会以此目录结构分组。\n\n```bash\n-| .nuxt/\n-| .output/\n-| assets/\n-| components/\n-| composables/\n-| layouts/\n-| middleware/\n-| node_modules/\n-| pages/\n-| plugins/\n-| public/\n-| server/\n--| api/\n--| tsconfig.json\n-| utils/\n-| .gitignore\n-| app.vue\n-| nuxt.config.ts\n-| package.json\n-| tsconfig.json\n-| README.md\n```\n\n# 四、后续\n\n整理一下此系列后续的计划,完成过程中可能会有所变动。\n叠个甲:所记录的内容皆为一步步踩坑的经验,可能不一定全面且算不上最优实现,详情请参考[官网文档](https://nuxt.com/)。\n\n```bash\n-| Nuxt系列教程/\n--| 基础篇/\n---| 页面篇(layouts、pages、路由)\n---| 中间件篇(middleware)\n---| 静态资源篇(assets、public、styling)\n---| 插件篇(plugins)\n---| 组件篇(components、composables)\n---| 数据获取篇(server)\n---| SEO篇(这个还在计划中,还没搞明白怎么玩😅)\n--| 集成篇/\n---| 状态管理篇(Pinia、顺带补充一下 Nuxt 自带的状态管理)\n---| UI篇(Naive UI、主题切换)\n---| 粒子特效篇(tsparticles)\n---| 滚动动画篇(WOW.js)\n---| 一部分好用的第三方库(集成比较简单就放在一起了,包括:vueuse、echarts、swiper、unocss)\n---| Icon篇(这个也是初步计划,此前使用的 iconfont 感觉不是那么舒服,后续考虑改为 Icones )\n```\n",[45,46],{"id":26,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":27,"count":28},{"id":30,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":31,"count":28},{"id":48,"createdAt":49,"updatedAt":49,"creatorId":12,"modifierId":12,"title":50,"sketch":51,"coverId":52,"cover":53,"content":58,"status":23,"tags":59},"1d84cbe79c2e42b7a49e51980fc184f1","2025-04-02","nvm 下升级 Node 的 pnpm 版本","使用 nvm 管理 Node 版本时,简便的 pnpm 升级方案","150321f01f2c4d0f867e30234875612c",{"id":52,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":54,"size":55,"objectName":56,"url":57},"pnpm.jpg",335168,"62b7243d-19c9-418f-bad0-da4926b48677.jpg","https://wlitd.com/for-the-worthy/62b7243d-19c9-418f-bad0-da4926b48677.jpg","# 一、前言\n\n随着开发和维护不同的项目,对于 `Node` 的版本要求也五花八门的,后面也是开始使用 `nvm` 来管理 `Node` 的版本。后面在某次安装依赖的时候,提示 `pnpm` 可以升级了,按照提示的方案升级\n\n```bash\npnpm add -g pnpm\n```\n\n出现了错误\n\n```bash\nUnable to find global bin directory\n```\n\n猜测原因大概是因为 `pnpm` 的环境被 `nvm` 包了一层,没办法,只能去找其他的方案。\n\n# 二、使用 corepack 升级 pnpm\n\n1. 启用 corepack\n\n```bash\ncorepack enable\n```\n\n2. 升级\n\n```bash\ncorepack prepare pnpm@latest --activate\n```\n\n3. 验证\n\n```bash\npnpm -v\n```\n",[60],{"id":30,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":31,"count":28},{"id":62,"createdAt":63,"updatedAt":63,"creatorId":12,"modifierId":12,"title":64,"sketch":65,"coverId":66,"cover":67,"content":72,"status":23,"tags":73},"a7f0fbd6a3434755bf7764c3372828cb","2025-04-01","Antfu ESlint 配置","便捷的 ESlint 配置方案","65f0efbadf474d01978b8155f3e75b69",{"id":66,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":68,"size":69,"objectName":70,"url":71},"eslint.jpg",290387,"70941071-da27-4f47-ad8a-546a8a90c9b8.jpg","https://wlitd.com/for-the-worthy/70941071-da27-4f47-ad8a-546a8a90c9b8.jpg","# 一、前言\n\n早在此博客开篇之后,我发布过一篇 `ESlint v9` 版本的配置教程,虽然已经基本弄清楚了 `ESlint` 的基础配置方法,但是随着一个接一个项目的配置下来,越发觉得配置过程还是太过于繁琐(虽然基本都是 `ctrl c + v`,但是我就是懒😝),且包含了大量的 `npm` 包。本着不折腾会死人的原则,开始寻找更加简单的配置方案。慢慢的,一个开源库浮现在我眼前:`@antfu/eslint-config`\n\n# 二、关于 @antfu/eslint-config\n\n该库原本是作者——[Anthony Fu](https://github.com/antfu) 的个人 `ESlint` 配置,随着越来越多人的使用,作者将其简易封装后开源。我们可以到[仓库](https://github.com/antfu/eslint-config)查看说明文档。\n\n## 1、特性\n\n- 自动格式化(在没有 `Preitter` 的情况下独立使用)\n- 合理的默认值,最佳实践,仅一行配置\n- 专为 `TypeScript`、`JSX`、`Vue`、`JSON`、`YAML`、`Toml`、`Markdown` 等而设计,开箱即用。\n- 固执己见,但非常可定制\n- `Eslint` 扁平配置\n- 可选 `React`,`Svelte`,`UnoCSS`,`Astro`,`Solid` 支持\n- 默认情况下尊重 `.gitignore`\n- 需要 `ESLint v9.5.0+`\n\n# 三、使用\n\n## 1、安装\n\n使用 `CLI` 工具安装\n\n```bash\npnpm dlx @antfu/eslint-config@latest\n```\n\n手动安装\n\n```bash\npnpm i -D eslint @antfu/eslint-config\n```\n\n## 2、配置\n\n在 **根目录** 下创建 `eslint.config.mjs` 文件\n\n```mjs\n// eslint.config.mjs\nimport antfu from '@antfu/eslint-config'\n\nexport default antfu()\n```\n\n在 `package.json` 中添加 `script`\n\n```json\n{\n \"scripts\": {\n \"lint\": \"eslint\",\n \"lint:fix\": \"eslint --fix\"\n }\n}\n\n```\n\n## 3、VS Code 保存自动格式化\n\n`VS Code` 的 `setting.json` 中添加\n\n```json\n\"editor.codeActionsOnSave\": {\n \"source.fixAll.eslint\": \"explicit\",\n \"source.organizeImports\": \"never\"\n}\n```\n\n## 4、定制化\n\n此处只做部分演示\n\n### 4-1、添加忽略文件\n\n```mjs\n// eslint.config.mjs\nimport antfu from '@antfu/eslint-config'\n\nexport default antfu({\n ignores: ['node_modules', 'dist', 'public']\n})\n```\n\n### 4-2、覆盖原有的规则\n\n```mjs\n// eslint.config.mjs\nimport antfu from '@antfu/eslint-config'\n\nexport default antfu({\n ignores: ['node_modules', 'dist', 'public', '.nuxt'],\n\n rules: {\n // 覆盖原antfu的末尾逗号规则\n 'style/comma-dangle': ['error', 'never']\n }\n})\n```\n\n### 4-3、针对特定文件的规则\n\n```mjs\n// eslint.config.mjs\nimport antfu from '@antfu/eslint-config'\n\nexport default antfu(\n {\n // 针对vue文件\n files: ['**/*.vue'],\n rules: {\n 'vue/operator-linebreak': ['error', 'before']\n }\n }\n)\n```\n\n!!! info\n关于更多的定制化规则建议查看原文档:[地址](https://github.com/antfu/eslint-config)\n!!!\n\n# 四、Nuxt\n\n安装依赖\n\n```bash\npnpm i @nuxt/eslint -D\n```\n\n!!! info\n如果使用 `TypeScript` 的话,还需要安装 `typescript`\n\n```bash\npnpm i typescript -D\n```\n\n!!!\n\n在 `nuxt.config.ts` 中配置\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n modules: [\n '@nuxt/eslint'\n ],\n eslint: {\n config: {\n standalone: false // \u003C---\n }\n }\n})\n```\n\n修改 `eslint.config.mjs`\n\n```mjs\n// eslint.config.mjs\nimport antfu from '@antfu/eslint-config'\nimport withNuxt from './.nuxt/eslint.config.mjs'\n\nexport default withNuxt(\n antfu({\n // ...@antfu/eslint-config options\n })\n)\n```\n\n# 五、结语\n\n相较于之前的一步步 `ESlint` 配置,通过 `@antfu/eslint-config` 来配置可以说精简到极致(懒人福音),且高度可定制,其中默认的规则也是极佳实践。\n",[74],{"id":30,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":31,"count":28},{"id":76,"createdAt":77,"updatedAt":77,"creatorId":12,"modifierId":12,"title":78,"sketch":79,"coverId":80,"cover":81,"content":86,"status":23,"tags":87},"d95a9d787f164172b29b6c385aa52a89","2025-02-21","Linux 升级 Redis","Redis 升级指北","3f6fdc0e89244a57a1ce6659a5652e34",{"id":80,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":82,"size":83,"objectName":84,"url":85},"redis.jpg",350124,"223d639a-e2c4-49a4-971c-a16b4a55b1de.jpg","https://wlitd.com/for-the-worthy/223d639a-e2c4-49a4-971c-a16b4a55b1de.jpg","# 一、前言\n\n在2024年10月,`Redis` 公布了一个高危漏洞 **Redis 缓冲区溢出漏洞(CVE-2024-31449)**,解决办法是升级一下 `Redis`。直到前几天偶然登到服务器上才发现这个问题,趁着这俩天有时间给升级一下(说是升级,我怎么感觉像是重装一样🤔)。\n\n!!! info\n根据[这里](https://github.com/redis/redis/security/advisories/GHSA-whxg-wx83-85p5)可知受到影响的 `Redis` 版本为:\n2.8.18 \u003C=\n修复的版本为:\n6.2.16、7.2.6、7.4.1\n!!!\n\n# 二、旧版本\n\n## 1、查看旧版本信息\n\n```bash\n# 登录 redis 客户端\nredis-cli\n# 输入密码(如果有)\nauth xxxxx\n\n# 查找备份的数据文件\n127.0.0.1:6379> config get dir\n1) \"dir\"\n2) \"/usr/local/redis/redis-7.0.5\"\n127.0.0.1:6379> config get dbfilename\n1) \"dbfilename\"\n2) \"dump.rdb\"\n127.0.0.1:6379>\n```\n\n!!! question 这里注意一个问题\n如果采用配置文件启动 `redis` 服务且配置项 `dir` 为默认 `./` 时,备份文件路径是你启动 `redis` 服务时的路径。\n举个栗子:\n当你在 `root` 目录下通过命令启动 `redis`\n\n```bash\nredis-server xxxxx/redis.conf\n```\n\n获取到的 `dir` 路径为 `/root`\n备份文件为 `aof` 应该也是如此(没试过)\n!!!\n\n## 2、备份数据\n\n因我在本地测试,所以先插入俩条数据\n\n```bash\n127.0.0.1:6379> mset hello1 hello1 hello2 hello2\nOK\n\n# 备份数据\n127.0.0.1:6379> SAVE\nOK\n\n# 关闭旧版本\n127.0.0.1:6379> shutdown\nnot connected> exit\n```\n\n# 三、新版本\n\n## 1、安装新版本\n\n[下载地址](https://download.redis.io/releases/)\n自行选择下载版本,我这里选用最新版7.4.2,不要问为什么,头铁。\n通过 `ftp` 或 `wget` 命令下载到服务器,我这里采用 `wget`。\n\n```bash\n# 下载\nwget https://download.redis.io/releases/redis-7.4.2.tar.gz\n\n# 解压\ntar -zxvf redis-7.4.2.tar.gz\n\n# 切换到redis目录\ncd redis-7.4.2\n\n# 编译(这里需要gcc环境,已经安装过 redis 就不详细描述了)\nmake\n\n# 安装\nmake install\n```\n\n## 2、配置文件合并、恢复数据\n\n```bash\n# 备份默认配置文件\ncp redis.conf redis.conf.default\n\n# 将旧版本配置文件复制过来(一般来说采用旧版配置文件就可,如果有什么新配置项请自行合并)\ncp -ar /usr/local/redis/redis-7.0.5/redis.conf ./\ncp:是否覆盖\"./redis.conf\"? y\n\n# 复制旧版本数据\ncp /usr/local/redis/redis-7.0.5/dump.rdb ./\n```\n\n## 3、启动新版本 redis,验证正确性\n\n```bash\n# 启动 redis 服务\nsrc/redis-server ../redis.conf\n\n# 连接客户端\nredis-cli\n\n# 输入密码(如果有)\nauth xxxxx\n\n# 查看版本信息\n127.0.0.1:6379> info\n# Server\nredis_version:7.4.2\n\n# 查看原数据\n127.0.0.1:6379> keys *\n1) \"hello1\"\n2) \"hello2\"\n```\n\n到此 `redis` 升级完成\n\n# 四、一个小问题\n\n升级完后启动 `redis` 抛出了一个 `Warning`\n\n```bash\nWARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.\n```\n\n大概意思是:必须启用内存复用!没有它,在内存不足的情况下,后台保存或复制可能会失败。如果被禁用,它也可能导致故障,但不会造成低内存条件,请参阅[链接](https://github.com/jemalloc/jemalloc/issues/1328)。要解决此问题,请添加 `“vm.Overcommit_memory = 1”` 到 `/etc/sysctl.conf` 中,然后重新启动或运行命令 `“sysctl vm. conf”`。`Overcommit_memory =1’` 才能生效\n\n处理一下\n\n```bash\nvim /etc/sysctl.conf\n```\n\n```bash\n# 在末尾添加\nvm.overcommit_memory = 1\n```\n\n保存退出,使更改立刻生效\n\n```bash\nsudo sysctl -p\n```\n\n之后重启 `redis` 警告就没了\n",[88],{"id":89,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":90,"count":28},"9228c39807f94582abbf2fab112033c0","中间件",{"id":92,"createdAt":77,"updatedAt":77,"creatorId":12,"modifierId":12,"title":93,"sketch":94,"coverId":95,"cover":96,"content":101,"status":23,"tags":102},"a57998c8a74d40aab71ed0da388e14bf","删除 Windows 服务","Windows 11 移除服务的三种方法","0397185a158f4fe2b253333d3d4a6a97",{"id":95,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":97,"size":98,"objectName":99,"url":100},"window.jpg",180785,"d9c0cd0d-546a-44cc-92da-4934325be406.jpg","https://wlitd.com/for-the-worthy/d9c0cd0d-546a-44cc-92da-4934325be406.jpg","# 一、前言\n\n`Windows` 服务除了包括保证系统使用的 `Windows` 服务外,部分应用程序在安装时可能会自动添加一些服务保证应用的使用。当应用程序卸载后,可能会残留一部分的服务,对于强迫症来说这是不可接受的,所以我们想点办法给它解决掉。\n\n!!! warning 警告\n本文所涉及的操作均为 **危险** 操作,请确保知晓其后果再进行操作。\n!!!\n\n# 二、了解服务\n\n在删除 `Windows` 服务之前,需要先了解有哪些服务正在运行,以及它们的作用。\n使用 `win + r` 输入 `services.msc` 打开服务管理器。\n在服务管理器中,会按字母顺序列出:服务的名称列表、简要描述、当前状态、启动类型和登录身份等信息。\n\n# 三、移除 Windos 服务\n\n## 1、使用 cmd(命令提示符)删除 Windows 服务\n\n1. 使用管理员打开 `cmd`\n2. 执行命令列出所有服务\n\n```bash\nsc queryex typ=service state=all\n```\n\n3. 找到你所要删除的服务的 **SERVICE_NAME**,执行命令删除服务\n\n```bash\nsc delete 服务名称\n```\n\n!!! tip 命令执行后会有反馈提示\n执行成功后,服务会被立即删除,不用重启。\n!!!\n\n## 2、使用 Windows PowerShell 删除服务\n\n1. 使用管理员打开 `Windows PowerShell`\n2. 执行命令查看服务列表\n\n```bash\nGet-Service\n```\n\n3. 找到你所要删除的服务的 **Name**,执行命令删除服务\n\n```bash\nsc delete 服务名称\n```\n\n!!! tip 命令执行后,除报错外不会有提示\n需要重启系统,服务才会被删除\n!!!\n\n## 3、使用注册表删除服务\n\n1. 使用 `win + r` 输入 `regedit` 打开注册表编辑器\n2. 导航到以下路径\n\n```bash\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\n```\n\n3. 右键点击所要删除的服务名称,选择删除即可\n\n!!! tip\n需要重启系统,服务才会被删除。\n!!!\n",[103],{"id":104,"createdAt":17,"updatedAt":17,"creatorId":12,"modifierId":12,"name":105,"count":28},"faada6729e704bc784a241445b35d163","操作系统",9,["Reactive",108],{"$si18n:cached-locale-configs":109,"$si18n:resolved-locale":110,"$scolor-mode":111},{},"",{"preference":112,"value":112,"unknown":113,"forced":114},"system",true,false,["Set"],["ShallowReactive",117],{"$fFiQKRiRGB1okSLZVXpttwaJWfdX8hoctr9Q0A-zObxs":-1},"/",{"user":120,"theme":126,"locale":129,"global":130},{"userInfo":121},["Ref",122],["Reactive",123],{"id":110,"email":110,"isEnabled":113,"avatar":124,"roles":125},{"size":28,"name":110,"objectName":110,"url":110},[],{"currentThemeName":127},["EmptyRef",128],"\"\"",{},{"title":131},["Ref",132],"for the worthy"]