[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fWd6ft11eGO2cexHvhaRvqFG5GJ8L8eHt4onn-MTEc3w":3,"$fJU-4tot_gC5fDkujNeoE-cGsdMy5V_KcdUXLuAnTFgw":18,"$fjf3nmX7OjRaDcYhHRulFkMEK2xSv_tZkAeWl1Fy4S20":423},{"slug":4,"title":5,"description":6,"content":7,"content_html":8,"pub_date":9,"tags":10,"draft":17},"vite-vue3-ts-elementplus-pinia","用 Vite+（vp）从零搭建 Vue3 + TypeScript + Element Plus + Pinia + Vue Router","使用 Vite+ 统一工具链（vp）一条命令搭建 Vue3 全家桶，涵盖按需导入、Pinia store、路由配置，以及常见坑的解决方案。","## 什么是 Vite+（vp）\n\n[Vite+](https:\u002F\u002Fviteplus.dev) 是 VoidZero 推出的统一 Web 工具链，把 Vite、Vitest、Oxlint、Rolldown、tsdown 集成到一个 `vp` 命令里。Node.js 版本管理、包管理器检测、dev\u002Fbuild\u002Ftest 全部通过 `vp` 统一入口，不需要分别装多个工具。\n\n```bash\n# 安装（Linux \u002F macOS）\ncurl -fsSL https:\u002F\u002Fvite.plus | bash\n\n# 验证\nvp --version\n```\n\n---\n\n## 1. 创建项目\n\n`vp create vue` 底层调用 `create-vue`，支持交互式选项，一次性把 TypeScript、Pinia、Vue Router 全勾上：\n\n```bash\n# 交互式（推荐，会问你要不要 TS\u002FRouter\u002FPinia 等）\nvp create vue\n\n# 非交互式（全部选 yes，直接得到完整骨架）\nvp create vue -- --ts --router --pinia --eslint\n```\n\n进入项目目录，安装依赖：\n\n```bash\ncd my-app\nnpm install    # 或 pnpm install \u002F yarn\n```\n\n---\n\n## 2. 安装 Element Plus\n\n```bash\nnpm install element-plus\n```\n\n### 完整引入（简单但体积大）\n\n在 `main.ts` 中：\n\n```ts\nimport { createApp } from 'vue'\nimport ElementPlus from 'element-plus'\nimport 'element-plus\u002Fdist\u002Findex.css'\nimport App from '.\u002FApp.vue'\n\nconst app = createApp(App)\napp.use(ElementPlus)\napp.mount('#app')\n```\n\n### 按需引入（推荐：自动导入，零配置）\n\n安装 `unplugin-vue-components` 和 `unplugin-auto-import`：\n\n```bash\nnpm install -D unplugin-vue-components unplugin-auto-import\n```\n\n修改 `vite.config.ts`：\n\n```ts\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs\u002Fplugin-vue'\nimport AutoImport from 'unplugin-auto-import\u002Fvite'\nimport Components from 'unplugin-vue-components\u002Fvite'\nimport { ElementPlusResolver } from 'unplugin-vue-components\u002Fresolvers'\n\nexport default defineConfig({\n  plugins: [\n    vue(),\n    AutoImport({\n      resolvers: [ElementPlusResolver()],\n      \u002F\u002F 自动导入 Vue 相关函数：ref, reactive, computed 等\n      imports: ['vue', 'vue-router', 'pinia'],\n      dts: 'src\u002Fauto-imports.d.ts',\n    }),\n    Components({\n      resolvers: [ElementPlusResolver()],\n      dts: 'src\u002Fcomponents.d.ts',\n    }),\n  ],\n})\n```\n\n配置后，在模板中直接使用 Element Plus 组件，无需 import：\n\n```vue\n\u003Ctemplate>\n  \u003Cel-button type=\"primary\" @click=\"handleClick\">点击\u003C\u002Fel-button>\n  \u003Cel-input v-model=\"inputVal\" placeholder=\"请输入\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\n\u002F\u002F ref、reactive 等也自动导入，无需手写 import\nconst inputVal = ref('')\nconst handleClick = () => {\n  ElMessage.success('操作成功')\n}\n\u003C\u002Fscript>\n```\n\n---\n\n## 3. 完整的 vite.config.ts\n\n```ts\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs\u002Fplugin-vue'\nimport AutoImport from 'unplugin-auto-import\u002Fvite'\nimport Components from 'unplugin-vue-components\u002Fvite'\nimport { ElementPlusResolver } from 'unplugin-vue-components\u002Fresolvers'\nimport { fileURLToPath, URL } from 'node:url'\n\nexport default defineConfig({\n  plugins: [\n    vue(),\n    AutoImport({\n      resolvers: [ElementPlusResolver()],\n      imports: ['vue', 'vue-router', 'pinia'],\n      dts: 'src\u002Fauto-imports.d.ts',\n      eslintrc: {\n        enabled: true,     \u002F\u002F 生成 ESLint 配置，避免 no-undef 警告\n        filepath: '.eslintrc-auto-import.json',\n      },\n    }),\n    Components({\n      resolvers: [ElementPlusResolver()],\n      dts: 'src\u002Fcomponents.d.ts',\n    }),\n  ],\n  resolve: {\n    alias: {\n      '@': fileURLToPath(new URL('.\u002Fsrc', import.meta.url)),\n    },\n  },\n  server: {\n    port: 3000,\n    open: true,\n    proxy: {\n      '\u002Fapi': {\n        target: 'http:\u002F\u002Flocalhost:8080',\n        changeOrigin: true,\n        rewrite: (path) => path.replace(\u002F^\\\u002Fapi\u002F, ''),\n      },\n    },\n  },\n  build: {\n    outDir: 'dist',\n    sourcemap: false,\n    rollupOptions: {\n      output: {\n        \u002F\u002F 代码分割：第三方库单独打包\n        manualChunks: {\n          'vue-vendor': ['vue', 'vue-router', 'pinia'],\n          'element-plus': ['element-plus'],\n        },\n      },\n    },\n  },\n})\n```\n\n---\n\n## 4. Pinia Store 完整示例\n\n### 定义 Store\n\n```ts\n\u002F\u002F src\u002Fstores\u002Fuser.ts\nimport { defineStore } from 'pinia'\n\ninterface UserInfo {\n  id: number\n  name: string\n  email: string\n  avatar: string\n  roles: string[]\n}\n\ninterface UserState {\n  userInfo: UserInfo | null\n  token: string\n  isLoading: boolean\n}\n\nexport const useUserStore = defineStore('user', {\n  state: (): UserState => ({\n    userInfo: null,\n    token: localStorage.getItem('token') || '',\n    isLoading: false,\n  }),\n\n  getters: {\n    isLoggedIn: (state) => !!state.token,\n    userName: (state) => state.userInfo?.name ?? '未登录',\n    hasRole: (state) => (role: string) => state.userInfo?.roles.includes(role) ?? false,\n  },\n\n  actions: {\n    async login(username: string, password: string) {\n      this.isLoading = true\n      try {\n        \u002F\u002F 调用登录 API\n        const response = await fetch('\u002Fapi\u002Fauth\u002Flogin', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application\u002Fjson' },\n          body: JSON.stringify({ username, password }),\n        })\n        const data = await response.json()\n        this.token = data.token\n        this.userInfo = data.userInfo\n        localStorage.setItem('token', data.token)\n      } finally {\n        this.isLoading = false\n      }\n    },\n\n    logout() {\n      this.token = ''\n      this.userInfo = null\n      localStorage.removeItem('token')\n    },\n\n    async fetchUserInfo() {\n      if (!this.token) return\n      const response = await fetch('\u002Fapi\u002Fuser\u002Finfo', {\n        headers: { Authorization: `Bearer ${this.token}` },\n      })\n      this.userInfo = await response.json()\n    },\n  },\n})\n```\n\n### 在组件中使用（storeToRefs）\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp v-if=\"isLoggedIn\">欢迎，{{ userName }}\u003C\u002Fp>\n    \u003Cel-button v-else @click=\"handleLogin\" :loading=\"isLoading\">\n      登录\n    \u003C\u002Fel-button>\n    \u003Cel-button v-if=\"isLoggedIn\" @click=\"handleLogout\">退出\u003C\u002Fel-button>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nimport { useUserStore } from '@\u002Fstores\u002Fuser'\nimport { storeToRefs } from 'pinia'\n\nconst userStore = useUserStore()\n\n\u002F\u002F storeToRefs：将 state 和 getter 解构为响应式 ref\n\u002F\u002F 注意：actions 不能用 storeToRefs，直接从 store 解构\nconst { isLoggedIn, userName, isLoading } = storeToRefs(userStore)\nconst { login, logout } = userStore\n\nconst handleLogin = async () => {\n  await login('admin', 'password123')\n  ElMessage.success('登录成功')\n}\n\nconst handleLogout = () => {\n  logout()\n  ElMessage.info('已退出登录')\n}\n\u003C\u002Fscript>\n```\n\n### Composition API 风格的 Store（推荐）\n\n```ts\n\u002F\u002F src\u002Fstores\u002Fcounter.ts\nimport { defineStore } from 'pinia'\nimport { ref, computed } from 'vue'\n\nexport const useCounterStore = defineStore('counter', () => {\n  \u002F\u002F state\n  const count = ref(0)\n  const name = ref('Counter')\n\n  \u002F\u002F getters（computed）\n  const doubleCount = computed(() => count.value * 2)\n\n  \u002F\u002F actions\n  function increment() {\n    count.value++\n  }\n\n  function decrement() {\n    count.value--\n  }\n\n  async function incrementAsync() {\n    await new Promise((resolve) => setTimeout(resolve, 500))\n    count.value++\n  }\n\n  return { count, name, doubleCount, increment, decrement, incrementAsync }\n})\n```\n\n---\n\n## 5. Vue Router 4 配置\n\n### 路由文件\n\n```ts\n\u002F\u002F src\u002Frouter\u002Findex.ts\nimport { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'\nimport { useUserStore } from '@\u002Fstores\u002Fuser'\n\n\u002F\u002F 懒加载路由（推荐：只在访问时才加载对应 chunk）\nconst routes: RouteRecordRaw[] = [\n  {\n    path: '\u002F',\n    name: 'Layout',\n    component: () => import('@\u002Flayouts\u002FDefaultLayout.vue'),\n    children: [\n      {\n        path: '',\n        name: 'Home',\n        component: () => import('@\u002Fviews\u002FHomeView.vue'),\n        meta: { title: '首页', requiresAuth: false },\n      },\n      {\n        path: 'dashboard',\n        name: 'Dashboard',\n        component: () => import('@\u002Fviews\u002FDashboardView.vue'),\n        meta: { title: '仪表盘', requiresAuth: true },\n      },\n      {\n        path: 'users',\n        name: 'Users',\n        component: () => import('@\u002Fviews\u002FUsersView.vue'),\n        meta: { title: '用户管理', requiresAuth: true, roles: ['admin'] },\n      },\n    ],\n  },\n  {\n    path: '\u002Flogin',\n    name: 'Login',\n    component: () => import('@\u002Fviews\u002FLoginView.vue'),\n    meta: { title: '登录', requiresAuth: false },\n  },\n  {\n    path: '\u002F:pathMatch(.*)*',\n    name: 'NotFound',\n    component: () => import('@\u002Fviews\u002FNotFound.vue'),\n  },\n]\n\nconst router = createRouter({\n  history: createWebHistory(import.meta.env.BASE_URL),\n  routes,\n  scrollBehavior(to, from, savedPosition) {\n    if (savedPosition) {\n      return savedPosition\n    }\n    return { top: 0 }\n  },\n})\n\n\u002F\u002F 全局路由守卫\nrouter.beforeEach(async (to, from, next) => {\n  \u002F\u002F 设置页面标题\n  document.title = to.meta.title ? `${to.meta.title} - My App` : 'My App'\n\n  const userStore = useUserStore()\n\n  \u002F\u002F 需要登录的页面\n  if (to.meta.requiresAuth && !userStore.isLoggedIn) {\n    next({ name: 'Login', query: { redirect: to.fullPath } })\n    return\n  }\n\n  \u002F\u002F 已登录不能访问登录页\n  if (to.name === 'Login' && userStore.isLoggedIn) {\n    next({ name: 'Dashboard' })\n    return\n  }\n\n  next()\n})\n\nexport default router\n```\n\n---\n\n## 6. TypeScript 类型扩充\n\n### 扩充路由 meta 类型\n\n```ts\n\u002F\u002F src\u002Ftypes\u002Frouter.d.ts\nimport 'vue-router'\n\ndeclare module 'vue-router' {\n  interface RouteMeta {\n    title?: string\n    requiresAuth?: boolean\n    roles?: string[]\n    keepAlive?: boolean\n    icon?: string\n  }\n}\n```\n\n### 扩充全局属性\n\n```ts\n\u002F\u002F src\u002Ftypes\u002Fglobal.d.ts\n\n\u002F\u002F 扩充 window 对象\ndeclare global {\n  interface Window {\n    __APP_CONFIG__: {\n      apiBaseUrl: string\n      version: string\n    }\n  }\n}\n\n\u002F\u002F 给 Vue 组件实例添加全局属性\ndeclare module 'vue' {\n  interface ComponentCustomProperties {\n    $filters: {\n      formatDate: (date: Date) => string\n      formatMoney: (amount: number) => string\n    }\n  }\n}\n\nexport {}\n```\n\n### 环境变量类型\n\n```ts\n\u002F\u002F src\u002Ftypes\u002Fenv.d.ts\n\u002F\u002F\u002F \u003Creference types=\"vite\u002Fclient\" \u002F>\n\ninterface ImportMetaEnv {\n  readonly VITE_APP_TITLE: string\n  readonly VITE_API_BASE_URL: string\n  readonly VITE_APP_ENV: 'development' | 'staging' | 'production'\n}\n\ninterface ImportMeta {\n  readonly env: ImportMetaEnv\n}\n```\n\n---\n\n## 7. 项目目录结构最佳实践\n\n```\nmy-app\u002F\n├── public\u002F                   # 静态资源（不经过 Vite 处理）\n│   └── favicon.ico\n├── src\u002F\n│   ├── api\u002F                  # API 请求层\n│   │   ├── http.ts           # axios 实例配置\n│   │   ├── user.ts           # 用户相关 API\n│   │   └── index.ts          # 统一导出\n│   ├── assets\u002F               # 静态资源（经 Vite 处理）\n│   │   ├── images\u002F\n│   │   └── styles\u002F\n│   │       ├── variables.scss\n│   │       └── global.scss\n│   ├── components\u002F           # 通用组件\n│   │   ├── common\u002F           # 公共基础组件\n│   │   └── business\u002F         # 业务组件\n│   ├── composables\u002F          # 组合式函数（hooks）\n│   │   ├── useRequest.ts\n│   │   └── useTheme.ts\n│   ├── layouts\u002F              # 布局组件\n│   │   ├── DefaultLayout.vue\n│   │   └── AuthLayout.vue\n│   ├── router\u002F\n│   │   └── index.ts\n│   ├── stores\u002F               # Pinia stores\n│   │   ├── user.ts\n│   │   ├── app.ts\n│   │   └── index.ts\n│   ├── types\u002F                # TypeScript 类型定义\n│   │   ├── api.d.ts\n│   │   ├── router.d.ts\n│   │   ├── env.d.ts\n│   │   └── global.d.ts\n│   ├── utils\u002F                # 工具函数\n│   │   ├── format.ts\n│   │   └── storage.ts\n│   ├── views\u002F                # 页面组件\n│   │   ├── HomeView.vue\n│   │   ├── DashboardView.vue\n│   │   └── LoginView.vue\n│   ├── App.vue\n│   ├── main.ts\n│   ├── auto-imports.d.ts     # 自动生成（unplugin-auto-import）\n│   └── components.d.ts       # 自动生成（unplugin-vue-components）\n├── .env                      # 环境变量\n├── .env.development\n├── .env.production\n├── vite.config.ts\n├── tsconfig.json\n└── package.json\n```\n\n---\n\n## 8. 常见报错和解决方案\n\n### 报错 1：找不到模块 'element-plus'\n\n```\nCannot find module 'element-plus' or its corresponding type declarations.\n```\n\n**解决**：确保安装了 element-plus 和类型声明：\n\n```bash\nnpm install element-plus\n```\n\nElement Plus 自带 TypeScript 类型，不需要单独安装 @types。\n\n### 报错 2：ElMessage 未定义\n\n使用 `unplugin-auto-import` 后，`ElMessage`、`ElMessageBox` 等应该自动导入，但有时需要重启 TS 服务：\n\n```bash\n# 在 VSCode 中：Ctrl+Shift+P → TypeScript: Restart TS Server\n```\n\n或者手动在需要的文件中引入：\n\n```ts\nimport { ElMessage } from 'element-plus'\n```\n\n### 报错 3：Pinia 报错 \"getActivePinia was called with no active Pinia\"\n\n在 router.beforeEach 中使用 store 时，需要确保 Pinia 已经在 createApp 时挂载：\n\n```ts\n\u002F\u002F main.ts - 顺序很重要\nconst app = createApp(App)\napp.use(createPinia())   \u002F\u002F 必须在 router 之前\napp.use(router)\napp.mount('#app')\n```\n\n### 报错 4：路由懒加载在开发模式下很慢\n\n这是正常现象，Vite 在开发模式下按需编译。生产构建后每个路由会生成独立 chunk，加载速度正常。\n\n### 报错 5：storeToRefs 导致响应式丢失\n\n直接解构 store（不使用 storeToRefs）会导致响应式丢失：\n\n```ts\n\u002F\u002F ❌ 错误：这样 count 不是响应式的\nconst { count } = useCounterStore()\n\n\u002F\u002F ✅ 正确：使用 storeToRefs\nconst { count } = storeToRefs(useCounterStore())\n```\n\n注意：actions 不能用 storeToRefs，直接解构即可：\n\n```ts\nconst store = useCounterStore()\nconst { count } = storeToRefs(store)    \u002F\u002F state\u002Fgetter 用 storeToRefs\nconst { increment } = store              \u002F\u002F action 直接解构\n```\n\n### 报错 6：TypeScript 报错 \"模块 '@\u002Fxxx' 无法找到\"\n\n检查 `tsconfig.json` 是否配置了路径别名：\n\n```json\n{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@\u002F*\": [\"src\u002F*\"]\n    }\n  }\n}\n```\n\n`vite.config.ts` 中的 `resolve.alias` 和 `tsconfig.json` 中的 `paths` 需要同步配置。\n\n---\n\n## 总结\n\n这套组合（Vite + Vue3 + TypeScript + Element Plus + Pinia + Vue Router）是 2024 年 Vue 生态最主流的选型：\n\n- **开发体验**：Vite 的 HMR 极快，TypeScript 提供类型安全\n- **UI**：Element Plus 组件丰富，按需引入零配置\n- **状态管理**：Pinia 比 Vuex 更简洁，完整的 TypeScript 支持\n- **路由**：Vue Router 4 的懒加载和路由守卫满足企业级需求\n\n按照本文的配置搭建，可以得到一个生产可用的前端项目骨架。\n","\u003Ch2 id=\"什么是-vite-vp\">什么是 Vite+（vp）\u003C\u002Fh2>\n\u003Cp>\u003Ca href=\"https:\u002F\u002Fviteplus.dev\">Vite+\u003C\u002Fa> 是 VoidZero 推出的统一 Web 工具链，把 Vite、Vitest、Oxlint、Rolldown、tsdown 集成到一个 \u003Ccode>vp\u003C\u002Fcode> 命令里。Node.js 版本管理、包管理器检测、dev\u002Fbuild\u002Ftest 全部通过 \u003Ccode>vp\u003C\u002Fcode> 统一入口，不需要分别装多个工具。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 安装（Linux \u002F macOS）\ncurl -fsSL https:\u002F\u002Fvite.plus | bash\n\n# 验证\nvp --version\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"1-创建项目\">1. 创建项目\u003C\u002Fh2>\n\u003Cp>\u003Ccode>vp create vue\u003C\u002Fcode> 底层调用 \u003Ccode>create-vue\u003C\u002Fcode>，支持交互式选项，一次性把 TypeScript、Pinia、Vue Router 全勾上：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 交互式（推荐，会问你要不要 TS\u002FRouter\u002FPinia 等）\nvp create vue\n\n# 非交互式（全部选 yes，直接得到完整骨架）\nvp create vue -- --ts --router --pinia --eslint\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>进入项目目录，安装依赖：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">cd my-app\nnpm install    # 或 pnpm install \u002F yarn\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"2-安装-element-plus\">2. 安装 Element Plus\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm install element-plus\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"完整引入-简单但体积大\">完整引入（简单但体积大）\u003C\u002Fh3>\n\u003Cp>在 \u003Ccode>main.ts\u003C\u002Fcode> 中：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">import { createApp } from 'vue'\nimport ElementPlus from 'element-plus'\nimport 'element-plus\u002Fdist\u002Findex.css'\nimport App from '.\u002FApp.vue'\n\nconst app = createApp(App)\napp.use(ElementPlus)\napp.mount('#app')\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"按需引入-推荐-自动导入-零配置\">按需引入（推荐：自动导入，零配置）\u003C\u002Fh3>\n\u003Cp>安装 \u003Ccode>unplugin-vue-components\u003C\u002Fcode> 和 \u003Ccode>unplugin-auto-import\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm install -D unplugin-vue-components unplugin-auto-import\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>修改 \u003Ccode>vite.config.ts\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">import { defineConfig } from 'vite'\nimport vue from '@vitejs\u002Fplugin-vue'\nimport AutoImport from 'unplugin-auto-import\u002Fvite'\nimport Components from 'unplugin-vue-components\u002Fvite'\nimport { ElementPlusResolver } from 'unplugin-vue-components\u002Fresolvers'\n\nexport default defineConfig({\n  plugins: [\n    vue(),\n    AutoImport({\n      resolvers: [ElementPlusResolver()],\n      \u002F\u002F 自动导入 Vue 相关函数：ref, reactive, computed 等\n      imports: ['vue', 'vue-router', 'pinia'],\n      dts: 'src\u002Fauto-imports.d.ts',\n    }),\n    Components({\n      resolvers: [ElementPlusResolver()],\n      dts: 'src\u002Fcomponents.d.ts',\n    }),\n  ],\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>配置后，在模板中直接使用 Element Plus 组件，无需 import：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-vue\">&lt;template&gt;\n  &lt;el-button type=&quot;primary&quot; @click=&quot;handleClick&quot;&gt;点击&lt;\u002Fel-button&gt;\n  &lt;el-input v-model=&quot;inputVal&quot; placeholder=&quot;请输入&quot; \u002F&gt;\n&lt;\u002Ftemplate&gt;\n\n&lt;script setup lang=&quot;ts&quot;&gt;\n\u002F\u002F ref、reactive 等也自动导入，无需手写 import\nconst inputVal = ref('')\nconst handleClick = () =&gt; {\n  ElMessage.success('操作成功')\n}\n&lt;\u002Fscript&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"3-完整的-vite-config-ts\">3. 完整的 vite.config.ts\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-ts\">import { defineConfig } from 'vite'\nimport vue from '@vitejs\u002Fplugin-vue'\nimport AutoImport from 'unplugin-auto-import\u002Fvite'\nimport Components from 'unplugin-vue-components\u002Fvite'\nimport { ElementPlusResolver } from 'unplugin-vue-components\u002Fresolvers'\nimport { fileURLToPath, URL } from 'node:url'\n\nexport default defineConfig({\n  plugins: [\n    vue(),\n    AutoImport({\n      resolvers: [ElementPlusResolver()],\n      imports: ['vue', 'vue-router', 'pinia'],\n      dts: 'src\u002Fauto-imports.d.ts',\n      eslintrc: {\n        enabled: true,     \u002F\u002F 生成 ESLint 配置，避免 no-undef 警告\n        filepath: '.eslintrc-auto-import.json',\n      },\n    }),\n    Components({\n      resolvers: [ElementPlusResolver()],\n      dts: 'src\u002Fcomponents.d.ts',\n    }),\n  ],\n  resolve: {\n    alias: {\n      '@': fileURLToPath(new URL('.\u002Fsrc', import.meta.url)),\n    },\n  },\n  server: {\n    port: 3000,\n    open: true,\n    proxy: {\n      '\u002Fapi': {\n        target: 'http:\u002F\u002Flocalhost:8080',\n        changeOrigin: true,\n        rewrite: (path) =&gt; path.replace(\u002F^\\\u002Fapi\u002F, ''),\n      },\n    },\n  },\n  build: {\n    outDir: 'dist',\n    sourcemap: false,\n    rollupOptions: {\n      output: {\n        \u002F\u002F 代码分割：第三方库单独打包\n        manualChunks: {\n          'vue-vendor': ['vue', 'vue-router', 'pinia'],\n          'element-plus': ['element-plus'],\n        },\n      },\n    },\n  },\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"4-pinia-store-完整示例\">4. Pinia Store 完整示例\u003C\u002Fh2>\n\u003Ch3 id=\"定义-store\">定义 Store\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Fstores\u002Fuser.ts\nimport { defineStore } from 'pinia'\n\ninterface UserInfo {\n  id: number\n  name: string\n  email: string\n  avatar: string\n  roles: string[]\n}\n\ninterface UserState {\n  userInfo: UserInfo | null\n  token: string\n  isLoading: boolean\n}\n\nexport const useUserStore = defineStore('user', {\n  state: (): UserState =&gt; ({\n    userInfo: null,\n    token: localStorage.getItem('token') || '',\n    isLoading: false,\n  }),\n\n  getters: {\n    isLoggedIn: (state) =&gt; !!state.token,\n    userName: (state) =&gt; state.userInfo?.name ?? '未登录',\n    hasRole: (state) =&gt; (role: string) =&gt; state.userInfo?.roles.includes(role) ?? false,\n  },\n\n  actions: {\n    async login(username: string, password: string) {\n      this.isLoading = true\n      try {\n        \u002F\u002F 调用登录 API\n        const response = await fetch('\u002Fapi\u002Fauth\u002Flogin', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application\u002Fjson' },\n          body: JSON.stringify({ username, password }),\n        })\n        const data = await response.json()\n        this.token = data.token\n        this.userInfo = data.userInfo\n        localStorage.setItem('token', data.token)\n      } finally {\n        this.isLoading = false\n      }\n    },\n\n    logout() {\n      this.token = ''\n      this.userInfo = null\n      localStorage.removeItem('token')\n    },\n\n    async fetchUserInfo() {\n      if (!this.token) return\n      const response = await fetch('\u002Fapi\u002Fuser\u002Finfo', {\n        headers: { Authorization: `Bearer ${this.token}` },\n      })\n      this.userInfo = await response.json()\n    },\n  },\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"在组件中使用-storetorefs\">在组件中使用（storeToRefs）\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-vue\">&lt;template&gt;\n  &lt;div&gt;\n    &lt;p v-if=&quot;isLoggedIn&quot;&gt;欢迎，{{ userName }}&lt;\u002Fp&gt;\n    &lt;el-button v-else @click=&quot;handleLogin&quot; :loading=&quot;isLoading&quot;&gt;\n      登录\n    &lt;\u002Fel-button&gt;\n    &lt;el-button v-if=&quot;isLoggedIn&quot; @click=&quot;handleLogout&quot;&gt;退出&lt;\u002Fel-button&gt;\n  &lt;\u002Fdiv&gt;\n&lt;\u002Ftemplate&gt;\n\n&lt;script setup lang=&quot;ts&quot;&gt;\nimport { useUserStore } from '@\u002Fstores\u002Fuser'\nimport { storeToRefs } from 'pinia'\n\nconst userStore = useUserStore()\n\n\u002F\u002F storeToRefs：将 state 和 getter 解构为响应式 ref\n\u002F\u002F 注意：actions 不能用 storeToRefs，直接从 store 解构\nconst { isLoggedIn, userName, isLoading } = storeToRefs(userStore)\nconst { login, logout } = userStore\n\nconst handleLogin = async () =&gt; {\n  await login('admin', 'password123')\n  ElMessage.success('登录成功')\n}\n\nconst handleLogout = () =&gt; {\n  logout()\n  ElMessage.info('已退出登录')\n}\n&lt;\u002Fscript&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"composition-api-风格的-store-推荐\">Composition API 风格的 Store（推荐）\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Fstores\u002Fcounter.ts\nimport { defineStore } from 'pinia'\nimport { ref, computed } from 'vue'\n\nexport const useCounterStore = defineStore('counter', () =&gt; {\n  \u002F\u002F state\n  const count = ref(0)\n  const name = ref('Counter')\n\n  \u002F\u002F getters（computed）\n  const doubleCount = computed(() =&gt; count.value * 2)\n\n  \u002F\u002F actions\n  function increment() {\n    count.value++\n  }\n\n  function decrement() {\n    count.value--\n  }\n\n  async function incrementAsync() {\n    await new Promise((resolve) =&gt; setTimeout(resolve, 500))\n    count.value++\n  }\n\n  return { count, name, doubleCount, increment, decrement, incrementAsync }\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"5-vue-router-4-配置\">5. Vue Router 4 配置\u003C\u002Fh2>\n\u003Ch3 id=\"路由文件\">路由文件\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Frouter\u002Findex.ts\nimport { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'\nimport { useUserStore } from '@\u002Fstores\u002Fuser'\n\n\u002F\u002F 懒加载路由（推荐：只在访问时才加载对应 chunk）\nconst routes: RouteRecordRaw[] = [\n  {\n    path: '\u002F',\n    name: 'Layout',\n    component: () =&gt; import('@\u002Flayouts\u002FDefaultLayout.vue'),\n    children: [\n      {\n        path: '',\n        name: 'Home',\n        component: () =&gt; import('@\u002Fviews\u002FHomeView.vue'),\n        meta: { title: '首页', requiresAuth: false },\n      },\n      {\n        path: 'dashboard',\n        name: 'Dashboard',\n        component: () =&gt; import('@\u002Fviews\u002FDashboardView.vue'),\n        meta: { title: '仪表盘', requiresAuth: true },\n      },\n      {\n        path: 'users',\n        name: 'Users',\n        component: () =&gt; import('@\u002Fviews\u002FUsersView.vue'),\n        meta: { title: '用户管理', requiresAuth: true, roles: ['admin'] },\n      },\n    ],\n  },\n  {\n    path: '\u002Flogin',\n    name: 'Login',\n    component: () =&gt; import('@\u002Fviews\u002FLoginView.vue'),\n    meta: { title: '登录', requiresAuth: false },\n  },\n  {\n    path: '\u002F:pathMatch(.*)*',\n    name: 'NotFound',\n    component: () =&gt; import('@\u002Fviews\u002FNotFound.vue'),\n  },\n]\n\nconst router = createRouter({\n  history: createWebHistory(import.meta.env.BASE_URL),\n  routes,\n  scrollBehavior(to, from, savedPosition) {\n    if (savedPosition) {\n      return savedPosition\n    }\n    return { top: 0 }\n  },\n})\n\n\u002F\u002F 全局路由守卫\nrouter.beforeEach(async (to, from, next) =&gt; {\n  \u002F\u002F 设置页面标题\n  document.title = to.meta.title ? `${to.meta.title} - My App` : 'My App'\n\n  const userStore = useUserStore()\n\n  \u002F\u002F 需要登录的页面\n  if (to.meta.requiresAuth &amp;&amp; !userStore.isLoggedIn) {\n    next({ name: 'Login', query: { redirect: to.fullPath } })\n    return\n  }\n\n  \u002F\u002F 已登录不能访问登录页\n  if (to.name === 'Login' &amp;&amp; userStore.isLoggedIn) {\n    next({ name: 'Dashboard' })\n    return\n  }\n\n  next()\n})\n\nexport default router\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"6-typescript-类型扩充\">6. TypeScript 类型扩充\u003C\u002Fh2>\n\u003Ch3 id=\"扩充路由-meta-类型\">扩充路由 meta 类型\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Ftypes\u002Frouter.d.ts\nimport 'vue-router'\n\ndeclare module 'vue-router' {\n  interface RouteMeta {\n    title?: string\n    requiresAuth?: boolean\n    roles?: string[]\n    keepAlive?: boolean\n    icon?: string\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"扩充全局属性\">扩充全局属性\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Ftypes\u002Fglobal.d.ts\n\n\u002F\u002F 扩充 window 对象\ndeclare global {\n  interface Window {\n    __APP_CONFIG__: {\n      apiBaseUrl: string\n      version: string\n    }\n  }\n}\n\n\u002F\u002F 给 Vue 组件实例添加全局属性\ndeclare module 'vue' {\n  interface ComponentCustomProperties {\n    $filters: {\n      formatDate: (date: Date) =&gt; string\n      formatMoney: (amount: number) =&gt; string\n    }\n  }\n}\n\nexport {}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"环境变量类型\">环境变量类型\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F src\u002Ftypes\u002Fenv.d.ts\n\u002F\u002F\u002F &lt;reference types=&quot;vite\u002Fclient&quot; \u002F&gt;\n\ninterface ImportMetaEnv {\n  readonly VITE_APP_TITLE: string\n  readonly VITE_API_BASE_URL: string\n  readonly VITE_APP_ENV: 'development' | 'staging' | 'production'\n}\n\ninterface ImportMeta {\n  readonly env: ImportMetaEnv\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"7-项目目录结构最佳实践\">7. 项目目录结构最佳实践\u003C\u002Fh2>\n\u003Cpre>\u003Ccode>my-app\u002F\n├── public\u002F                   # 静态资源（不经过 Vite 处理）\n│   └── favicon.ico\n├── src\u002F\n│   ├── api\u002F                  # API 请求层\n│   │   ├── http.ts           # axios 实例配置\n│   │   ├── user.ts           # 用户相关 API\n│   │   └── index.ts          # 统一导出\n│   ├── assets\u002F               # 静态资源（经 Vite 处理）\n│   │   ├── images\u002F\n│   │   └── styles\u002F\n│   │       ├── variables.scss\n│   │       └── global.scss\n│   ├── components\u002F           # 通用组件\n│   │   ├── common\u002F           # 公共基础组件\n│   │   └── business\u002F         # 业务组件\n│   ├── composables\u002F          # 组合式函数（hooks）\n│   │   ├── useRequest.ts\n│   │   └── useTheme.ts\n│   ├── layouts\u002F              # 布局组件\n│   │   ├── DefaultLayout.vue\n│   │   └── AuthLayout.vue\n│   ├── router\u002F\n│   │   └── index.ts\n│   ├── stores\u002F               # Pinia stores\n│   │   ├── user.ts\n│   │   ├── app.ts\n│   │   └── index.ts\n│   ├── types\u002F                # TypeScript 类型定义\n│   │   ├── api.d.ts\n│   │   ├── router.d.ts\n│   │   ├── env.d.ts\n│   │   └── global.d.ts\n│   ├── utils\u002F                # 工具函数\n│   │   ├── format.ts\n│   │   └── storage.ts\n│   ├── views\u002F                # 页面组件\n│   │   ├── HomeView.vue\n│   │   ├── DashboardView.vue\n│   │   └── LoginView.vue\n│   ├── App.vue\n│   ├── main.ts\n│   ├── auto-imports.d.ts     # 自动生成（unplugin-auto-import）\n│   └── components.d.ts       # 自动生成（unplugin-vue-components）\n├── .env                      # 环境变量\n├── .env.development\n├── .env.production\n├── vite.config.ts\n├── tsconfig.json\n└── package.json\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"8-常见报错和解决方案\">8. 常见报错和解决方案\u003C\u002Fh2>\n\u003Ch3 id=\"报错-1-找不到模块-element-plus\">报错 1：找不到模块 ‘element-plus’\u003C\u002Fh3>\n\u003Cpre>\u003Ccode>Cannot find module 'element-plus' or its corresponding type declarations.\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>解决\u003C\u002Fstrong>：确保安装了 element-plus 和类型声明：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm install element-plus\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Element Plus 自带 TypeScript 类型，不需要单独安装 @types。\u003C\u002Fp>\n\u003Ch3 id=\"报错-2-elmessage-未定义\">报错 2：ElMessage 未定义\u003C\u002Fh3>\n\u003Cp>使用 \u003Ccode>unplugin-auto-import\u003C\u002Fcode> 后，\u003Ccode>ElMessage\u003C\u002Fcode>、\u003Ccode>ElMessageBox\u003C\u002Fcode> 等应该自动导入，但有时需要重启 TS 服务：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 在 VSCode 中：Ctrl+Shift+P → TypeScript: Restart TS Server\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>或者手动在需要的文件中引入：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">import { ElMessage } from 'element-plus'\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"报错-3-pinia-报错-getactivepinia-was-called-with-no-active-pinia\">报错 3：Pinia 报错 “getActivePinia was called with no active Pinia”\u003C\u002Fh3>\n\u003Cp>在 router.beforeEach 中使用 store 时，需要确保 Pinia 已经在 createApp 时挂载：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F main.ts - 顺序很重要\nconst app = createApp(App)\napp.use(createPinia())   \u002F\u002F 必须在 router 之前\napp.use(router)\napp.mount('#app')\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"报错-4-路由懒加载在开发模式下很慢\">报错 4：路由懒加载在开发模式下很慢\u003C\u002Fh3>\n\u003Cp>这是正常现象，Vite 在开发模式下按需编译。生产构建后每个路由会生成独立 chunk，加载速度正常。\u003C\u002Fp>\n\u003Ch3 id=\"报错-5-storetorefs-导致响应式丢失\">报错 5：storeToRefs 导致响应式丢失\u003C\u002Fh3>\n\u003Cp>直接解构 store（不使用 storeToRefs）会导致响应式丢失：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">\u002F\u002F ❌ 错误：这样 count 不是响应式的\nconst { count } = useCounterStore()\n\n\u002F\u002F ✅ 正确：使用 storeToRefs\nconst { count } = storeToRefs(useCounterStore())\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>注意：actions 不能用 storeToRefs，直接解构即可：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-ts\">const store = useCounterStore()\nconst { count } = storeToRefs(store)    \u002F\u002F state\u002Fgetter 用 storeToRefs\nconst { increment } = store              \u002F\u002F action 直接解构\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"报错-6-typescript-报错-模块-xxx-无法找到\">报错 6：TypeScript 报错 “模块 ‘@\u002Fxxx’ 无法找到”\u003C\u002Fh3>\n\u003Cp>检查 \u003Ccode>tsconfig.json\u003C\u002Fcode> 是否配置了路径别名：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;compilerOptions&quot;: {\n    &quot;baseUrl&quot;: &quot;.&quot;,\n    &quot;paths&quot;: {\n      &quot;@\u002F*&quot;: [&quot;src\u002F*&quot;]\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>vite.config.ts\u003C\u002Fcode> 中的 \u003Ccode>resolve.alias\u003C\u002Fcode> 和 \u003Ccode>tsconfig.json\u003C\u002Fcode> 中的 \u003Ccode>paths\u003C\u002Fcode> 需要同步配置。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"总结\">总结\u003C\u002Fh2>\n\u003Cp>这套组合（Vite + Vue3 + TypeScript + Element Plus + Pinia + Vue Router）是 2024 年 Vue 生态最主流的选型：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>开发体验\u003C\u002Fstrong>：Vite 的 HMR 极快，TypeScript 提供类型安全\u003C\u002Fli>\n\u003Cli>\u003Cstrong>UI\u003C\u002Fstrong>：Element Plus 组件丰富，按需引入零配置\u003C\u002Fli>\n\u003Cli>\u003Cstrong>状态管理\u003C\u002Fstrong>：Pinia 比 Vuex 更简洁，完整的 TypeScript 支持\u003C\u002Fli>\n\u003Cli>\u003Cstrong>路由\u003C\u002Fstrong>：Vue Router 4 的懒加载和路由守卫满足企业级需求\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>按照本文的配置搭建，可以得到一个生产可用的前端项目骨架。\u003C\u002Fp>\n","2024-08-27",[11,12,13,14,15,16],"vue","vite","typescript","element-plus","pinia","vite-plus",false,[19,32,43,55,65,72,79,86,93,100,109,118,128,137,145,153,162,171,180,190,197,207,213,220,226,235,242,249,257,267,275,283,286,296,306,314,324,335,345,354,362,368,376,384,392,400,408,415],{"slug":20,"title":21,"description":22,"pub_date":23,"tags":24,"draft":17,"word_count":31},"ide-skills-guide","Agent Skills 完全指南：21 款第三方 Skill 深度评测与使用心得","全面评测 21 款第三方 Agent Skills，涵盖 Vue 生态、前端设计、构建工具、实用工具四大分类。从安装配置到实际使用场景，带你了解每个 Skill 的功能特点、最佳实践与使用心得。","2026-06-15",[25,26,27,28,29,30],"agent","skills","AI","效率工具","前端","Vue",4169,{"slug":33,"title":34,"description":35,"pub_date":36,"tags":37,"draft":17,"word_count":42},"linux-kernel-skeleton-struct-funcptr-container_of","Linux 内核骨架：struct、函数指针与 container_of","读懂 Linux 内核源码的三件套：巨大的 struct 组合代替继承、函数指针表实现虚派发、container_of 宏从嵌入成员找回完整对象。","2026-05-09",[38,39,40,41],"linux","kernel","C","container_of",1369,{"slug":44,"title":45,"description":46,"pub_date":47,"tags":48,"draft":17,"word_count":54},"astro-complete-guide-2025","Astro 5 深度剖析：Islands 架构原理、构建优化与 Cloudflare Workers 边缘部署","从编译器视角解析 Astro 5 的 Islands 架构实现原理，Content Layer API 的 Vite 插件机制，Server Islands 的流式渲染，以及如何在 Cloudflare Workers + D1 边缘环境下榨干性能。","2026-05-08",[49,50,51,52,53],"astro","frontend","cloudflare","performance","architecture",3663,{"slug":56,"title":57,"description":58,"pub_date":59,"tags":60,"draft":17,"word_count":64},"llm-prompt-engineering","Prompt Engineering 实战：让 LLM 真正听话的技巧","System prompt 怎么写、Few-shot 怎么设计、Chain-of-Thought 原理，以及常见失败模式和调试方法。","2026-05-03",[61,62,63],"ai","llm","工程实践",1723,{"slug":66,"title":67,"description":68,"pub_date":59,"tags":69,"draft":17,"word_count":71},"rag-system-design","RAG 系统设计：从 naive 到 production-ready","Retrieval-Augmented Generation 不只是「向量数据库 + LLM」，分块策略、召回质量、重排序、缓存才是工程核心。",[61,70,62,63],"rag",1613,{"slug":73,"title":74,"description":75,"pub_date":59,"tags":76,"draft":17,"word_count":78},"git-advanced-workflow","Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用","merge 会了，但 rebase 总搞错？bisect 找 bug 提交？interactive rebase 整理历史？这篇一次说清楚。",[77,63],"git",1396,{"slug":80,"title":81,"description":82,"pub_date":59,"tags":83,"draft":17,"word_count":85},"docker-practical-guide","Docker 实战：从会用到用好","会 docker run 不够，Dockerfile 最佳实践、多阶段构建、Compose 编排、镜像瘦身才是日常真正需要的。",[84,38,63],"docker",1268,{"slug":87,"title":88,"description":89,"pub_date":59,"tags":90,"draft":17,"word_count":92},"anthropics-skills-guide","anthropics\u002Fskills：Anthropic 官方 Agent Skills 仓库解析","Anthropic 官方开源的 Agent Skills 标准仓库，127k stars，解析 SKILL.md 规范、17 个示例 skill 的设计模式，以及如何在 Claude Code \u002F Claude.ai \u002F API 中使用",[61,91,25,26],"Claude",2090,{"slug":94,"title":95,"description":96,"pub_date":59,"tags":97,"draft":17,"word_count":99},"karpathy-claude-code-guidelines","Karpathy 的 LLM 编码批评与 CLAUDE.md 最佳实践","基于 Andrej Karpathy 对 LLM 编程助手的观察，forrestchang 提炼出一个 CLAUDE.md 文件，4 条原则解决 AI 编码的典型失控问题：乱猜假设、过度设计、乱改代码、目标不清",[61,91,98,63],"Claude Code",2699,{"slug":101,"title":102,"description":103,"pub_date":59,"tags":104,"draft":17,"word_count":108},"typescript-advanced-patterns","TypeScript 高级模式：让类型系统为你工作","基础 TS 会了但类型总是 any？条件类型、映射类型、模板字面量类型、infer 关键字才是 TS 的真正威力。",[13,105,106,107],"类型系统","前端工程","高级模式",1419,{"slug":110,"title":111,"description":112,"pub_date":59,"tags":113,"draft":17,"word_count":117},"linux-performance-tuning","Linux 性能调优实战：从 top 到 perf 的完整工具链","遇到性能问题不知道从哪下手？这篇建立系统化的排查思路，从 CPU\u002F内存\u002FIO\u002F网络逐层分析。",[38,114,115,116],"性能","运维","系统编程",1524,{"slug":119,"title":120,"description":121,"pub_date":59,"tags":122,"draft":17,"word_count":127},"python-functional-programming","Python 函数式编程：map\u002Ffilter\u002Freduce 之外","Python 不是纯函数式语言，但 functools、itertools、偏函数、闭包这些工具用好了能让代码简洁一个量级。",[123,124,125,126],"python","函数式","闭包","装饰器",1867,{"slug":129,"title":130,"description":131,"pub_date":59,"tags":132,"draft":17,"word_count":136},"python-oop-guide","Python 面向对象：__init__ 之外你需要知道的","Python OOP 不只是 class + __init__，魔术方法、描述符、元类才是真正的武器。",[123,133,134,135],"OOP","面向对象","魔术方法",1792,{"slug":138,"title":139,"description":140,"pub_date":59,"tags":141,"draft":17,"word_count":144},"python-data-structures","Python 内置数据结构深度解析","list、dict、set、tuple 不只是数据容器，搞懂它们的底层实现和时间复杂度，才能写出高性能 Python。",[123,142,114,143],"数据结构","算法",1517,{"slug":146,"title":147,"description":148,"pub_date":59,"tags":149,"draft":17,"word_count":152},"python-basics-quick-start","Python 快速上手：写给有编程基础的人","已经会其他语言，想快速掌握 Python 的语法特性和思维方式，这篇是捷径。",[123,150,151],"入门","基础",1607,{"slug":154,"title":155,"description":156,"pub_date":59,"tags":157,"draft":17,"word_count":161},"python-dataclass-pydantic","Python dataclass vs Pydantic：数据类选型指南","dataclass 是标准库的轻量选择，Pydantic v2 是带验证的重武器，什么时候用哪个，这篇说清楚。",[123,158,159,160],"dataclass","pydantic","数据验证",1323,{"slug":163,"title":164,"description":165,"pub_date":59,"tags":166,"draft":17,"word_count":170},"python-asyncio-practical","Python asyncio 实战：从回调地狱到协程优雅","asyncio 是 Python 异步编程的核心，搞懂 event loop、Task、gather 这些概念才能写出真正高效的异步代码。",[123,167,168,169],"asyncio","并发","网络编程",1258,{"slug":172,"title":173,"description":174,"pub_date":59,"tags":175,"draft":17,"word_count":179},"python-type-hints-guide","Python 类型注解完全指南：从入门到实践","Python 3.5+ 引入类型注解，配合 mypy\u002Fpyright 让 Python 也能享受静态类型检查的好处。",[123,176,177,178],"typescript-style","type-hints","工具链",1102,{"slug":181,"title":182,"description":183,"pub_date":184,"tags":185,"draft":17,"word_count":189},"pwa-install-update-button","PWA 踩坑：为什么安装按钮从来不出现","从 beforeinstallprompt 到 Service Worker waiting，把 PWA 的安装与更新提示真正做对","2026-05-02",[186,187,188],"pwa","javascript","web",1683,{"slug":191,"title":192,"description":193,"pub_date":194,"tags":195,"draft":17,"word_count":196},"openclaw-vs-hermes-agent","OpenClaw vs Hermes Agent：两个本地优先 Agent 的设计差异","OpenClaw（Novita AI）和 Hermes Agent（Nous Research）都是本地运行的个人 AI Agent，但在记忆系统、技能学习、运行环境和模型生态上走了不同的路。深入对比两种架构的核心差异。","2026-05-01",[61,25,62],1679,{"slug":198,"title":199,"description":200,"pub_date":194,"tags":201,"draft":17,"word_count":206},"cpp-random-design-patterns","C++ 设计模式实战：RAII、观察者、工厂","用现代 C++（C++17\u002F20）实现三种高频设计模式：RAII 资源管理、观察者模式事件系统、工厂模式插件架构。每种模式给出问题场景、实现代码和真实工程案例。",[202,203,204,205],"cpp","设计模式","c++17","工程",2613,{"slug":208,"title":209,"description":210,"pub_date":194,"tags":211,"draft":17,"word_count":212},"data-structures-fundamentals","数据结构基础：从数组到红黑树","系统梳理常用数据结构的核心原理、时间复杂度和适用场景。数组、链表、栈、队列、哈希表、二叉树、堆、图，每种结构附实现要点和 C++ 代码片段。",[142,143,202,151],3004,{"slug":214,"title":215,"description":216,"pub_date":217,"tags":218,"draft":17,"word_count":219},"ai-agent-what-is","什么是 AI Agent？从 LLM 到自主执行","LLM 本身是无状态问答机，Agent 是什么让它’动’起来的？本文深入解析 Agent 的四个核心能力、ReAct 框架、工具调用原理，以及主流框架横向对比。","2026-04-30",[61,25,62],2116,{"slug":221,"title":222,"description":223,"pub_date":217,"tags":224,"draft":17,"word_count":225},"ai-agent-memory","AI Agent 的记忆系统：从上下文窗口到长期记忆","深入拆解 AI Agent 的四种记忆类型、上下文窗口压缩策略、RAG 向量检索原理，以及三种典型失败模式和工程选型建议。",[61,25,70],2052,{"slug":227,"title":228,"description":229,"pub_date":217,"tags":230,"draft":17,"word_count":234},"network-proxy-vpn-guide","代理与翻墙技术原理：从 HTTP 代理到现代协议","深入解析代理与 VPN 的本质区别，梳理从 SOCKS5 到 Shadowsocks、V2Ray\u002FXray、Hysteria2 的协议演进，以及机场订阅的技术本质。",[231,232,233],"网络","代理","协议",2148,{"slug":236,"title":237,"description":238,"pub_date":217,"tags":239,"draft":17,"word_count":152},"algorithm-binary-search","二分查找：永远写不对？记住这个模板","彻底搞清楚二分查找的边界问题：闭区间和左闭右开两套模板、三道经典 LeetCode 题目完整 C++ 实现，以及二分答案的进阶思路。",[143,240,241,202],"二分查找","leetcode",{"slug":243,"title":244,"description":245,"pub_date":217,"tags":246,"draft":17,"word_count":248},"algorithm-sliding-window","滑动窗口算法：从暴力到 O(n) 的思维跃迁","系统讲解滑动窗口算法的核心模板、适用题型，配合三道经典 LeetCode 题目的完整 C++ 实现，彻底理解双指针收缩思路。",[143,247,241,202],"滑动窗口",1943,{"slug":250,"title":251,"description":252,"pub_date":217,"tags":253,"draft":17,"word_count":256},"network-clash-config","Clash \u002F Mihomo 配置详解：规则、策略组与分流","深入解析 Clash\u002FMihomo 的核心配置结构，包括代理节点、策略组类型、规则优先级、DNS fake-ip 模式，以及一份实用的完整配置模板。",[231,254,232,255],"clash","配置",1292,{"slug":258,"title":259,"description":260,"pub_date":261,"tags":262,"draft":17,"word_count":266},"hid-hotplug","HID 设备热插拔检测：从 udev 到 node-hid","在 Linux 上用 node-hid + usb 库实现可靠的 USB HID 设备热插拔检测，踩坑记录","2026-04-28",[202,263,38,264,265],"hid","nodejs","electron",2039,{"slug":268,"title":269,"description":270,"pub_date":271,"tags":272,"draft":17,"word_count":274},"electron-ipc-types","Electron IPC 类型安全：从 any 到完全类型化","用 TypeScript 泛型封装 Electron IPC，彻底消灭 any，preload 契约集中管理","2026-04-25",[265,13,273,11],"ipc",1446,{"slug":276,"title":277,"description":278,"pub_date":279,"tags":280,"draft":17,"word_count":282},"element-plus-popover-hide","手动关闭多个 el-popover（不用 v-model:visible）","通过 ref + Reflect.get 调用 hide() 方法手动关闭 Element Plus Popover，解释 Vue3 Proxy 导致无法直接调用实例方法的原因。","2024-10-25",[11,14,281],"vue3",1321,{"slug":4,"title":5,"description":6,"pub_date":9,"tags":284,"draft":17,"word_count":285},[11,12,13,14,15,16],1960,{"slug":287,"title":288,"description":289,"pub_date":290,"tags":291,"draft":17,"word_count":295},"cef-lnk2038-iterator-debug-level","CEF LNK2038：解决 _ITERATOR_DEBUG_LEVEL 不匹配错误","分析 CEF（Chromium Embedded Framework）集成时出现的 LNK2038 _ITERATOR_DEBUG_LEVEL 链接错误，从根本原因到解决方案的完整指南。","2024-05-07",[202,292,293,294],"CEF","Visual Studio","链接错误",1509,{"slug":297,"title":298,"description":299,"pub_date":300,"tags":301,"draft":17,"word_count":305},"npm-electron-install-fix","彻底解决 npm 安装 Electron 失败的问题","分析 npm install electron 失败的根本原因（下载二进制超时\u002F被墙），通过国内镜像（npmmirror）彻底解决，并介绍多种备选方案和常见错误排查。","2024-03-01",[265,302,303,304],"npm","前端工具链","国内镜像",1494,{"slug":307,"title":308,"description":309,"pub_date":310,"tags":311,"draft":17,"word_count":313},"git-out-of-memory","解决 git 报错：Fatal: Out of memory, malloc failed","分析 git 大仓库操作时出现 Out of memory malloc failed 的根本原因，通过调整 pack.windowMemory、http.postBuffer 和 git repack 彻底解决。","2024-01-31",[77,38,312],"工具",2244,{"slug":315,"title":316,"description":317,"pub_date":318,"tags":319,"draft":17,"word_count":323},"vmware-tools-install","在 VMware 虚拟机中安装 open-vm-tools 完整指南","详解 VMware Tools 的作用、open-vm-tools 与官方 VMware Tools 的区别，以及在 Ubuntu 虚拟机中安装并生效的完整步骤和常见问题排查。","2023-11-21",[320,38,321,322],"VMware","Ubuntu","虚拟机",2523,{"slug":325,"title":326,"description":327,"pub_date":328,"tags":329,"draft":17,"word_count":334},"load-balancing-algorithms","负载均衡算法完全指南：从轮询到一致性哈希","系统梳理静态与动态负载均衡算法，涵盖轮询、随机、权重、IP Hash、一致性 Hash、最少连接、最快响应等，并对比 Nginx、Dubbo、Spring Cloud LoadBalancer 的实现差异。","2023-11-15",[330,331,332,333],"分布式","负载均衡","Nginx","微服务",1764,{"slug":336,"title":337,"description":338,"pub_date":339,"tags":340,"draft":17,"word_count":344},"win-cw2a-ca2w","ATL 字符串转换：CW2A 与 CA2W 完全指南","详解 ATL 宏 CW2A\u002FCA2W 在 Unicode 与 ANSI 之间的字符串转换用法、头文件依赖、USES_CONVERSION 宏的作用与常见陷阱。","2023-06-09",[202,341,342,343],"windows","ATL","字符串",1665,{"slug":346,"title":347,"description":348,"pub_date":339,"tags":349,"draft":17,"word_count":353},"csharp-sendmessage-cpp","C# 通过 SendMessage 向 C++ 窗口发送消息与字符串","使用 P\u002FInvoke 调用 user32.dll 的 SendMessage，从 C# 发送自定义 WM_USER 消息及字符串指针给 C++ 原生窗口，并在 C++ 侧正确接收和转换。",[350,202,341,351,352],"C#","互操作","PInvoke",1554,{"slug":355,"title":356,"description":357,"pub_date":358,"tags":359,"draft":17,"word_count":361},"win-postmessage-vector","Windows PostMessage 跨线程传递 std::vector 指针","通过 PostMessage 在 Windows 消息队列中传递 std::vector 指针，使用 reinterpret_cast 将指针装入 LPARAM，并在接收方正确释放内存。","2023-05-26",[202,341,360],"WinAPI",1823,{"slug":363,"title":364,"description":365,"pub_date":358,"tags":366,"draft":17,"word_count":367},"exe-dll-single-package","将 EXE 和 DLL 打包成单一可执行文件","介绍两种将 exe 和依赖 dll 打包成单文件的方案：Enigma Virtual Box 和 WinRAR 自解压，适合发布 Windows 桌面程序时简化分发流程。",[341,202,312],1619,{"slug":369,"title":370,"description":371,"pub_date":358,"tags":372,"draft":17,"word_count":375},"cpp-random-mt19937","C++ 现代随机数生成：用 mt19937 彻底告别 rand()","深入讲解为什么 rand() 不够用，以及如何用 C++11 的 \u003Crandom> 库正确生成高质量随机数，涵盖 mt19937、各种分布和线程安全。",[202,373,374],"c++11","random",1549,{"slug":377,"title":378,"description":379,"pub_date":380,"tags":381,"draft":17,"word_count":383},"win-startup-registry","C++ 实现程序开机自启动：注册表方式详解","通过操作 Windows 注册表 Run 键实现程序开机自启动，包括 HKCU 与 HKLM 区别、完整封装代码、工作目录问题和 UAC 权限处理。","2022-12-26",[341,202,382],"registry",1201,{"slug":385,"title":386,"description":387,"pub_date":388,"tags":389,"draft":17,"word_count":391},"mfc-cstring-wparam","MFC 中 CString 与 WPARAM 之间的转换","详解 MFC 消息传递中 CString 无法直接强转为 WPARAM 的原因，以及两种正确的转换方案，并介绍结构体指针传递的正确姿势。","2022-11-25",[390,202,341],"mfc",1546,{"slug":393,"title":394,"description":395,"pub_date":396,"tags":397,"draft":17,"word_count":399},"duilib-static-build","正确编译 Duilib 静态库：避免 ATL 依赖和链接错误","详解如何用 DuiLib_Static.vcxproj 编译 Duilib 静态库，解决 VARIANT 未定义、Unicode 配置不匹配和 ATL 依赖等常见问题。","2022-08-24",[202,398,341,390],"duilib",2639,{"slug":401,"title":402,"description":403,"pub_date":404,"tags":405,"draft":17,"word_count":407},"mfc-dpi-adaptive","MFC 界面自适应不同分辨率","MFC 对话框程序实现控件和字体随分辨率自动缩放的完整方案，附 DPI Awareness 配置说明","2022-08-17",[390,202,341,406],"dpi",1414,{"slug":409,"title":410,"description":411,"pub_date":412,"tags":413,"draft":17,"word_count":414},"mfc-drag-window","MFC 无标题栏窗口客户区拖动：三种方法对比","MFC 对话框去掉标题栏后如何实现拖动移动窗口，三种方案完整实现与适用场景分析","2022-08-16",[390,202,341],1633,{"slug":416,"title":417,"description":418,"pub_date":419,"tags":420,"draft":17,"word_count":422},"algorithm-number-complement","整数的补数：位运算掩码解法","LeetCode 476 题，用掩码 XOR 实现整数补数，附 C++\u002FPython\u002FJava 三种实现及补数与补码的区别","2021-03-08",[143,421,241],"位运算",1374,[]]