[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"chapter-book:Q18bJ8x0":3,"chapter:yj6y7QX9":174},{"id":4,"created_at":5,"updated_at":5,"code":6,"author_id":7,"title":8,"slug":9,"cover_theme_key":10,"visibility":11,"status":12,"sort_order":4,"favorite_count":13,"view_count":14,"author":15,"chapter_count":27,"word_count":28,"free_count":13,"is_favorite":24,"book_status":12,"unparted":29},10,"2024-09-15T01:09:18.387+08:00","Q18bJ8x0",1,"从零开始学vue3","从零开始学vue3-10","default","public","published",0,2,{"id":7,"created_at":16,"updated_at":17,"code":18,"username":19,"display_name":20,"role":21,"avatar_url":22,"is_active":23,"is_banned":24,"follower_count":13,"following_count":25,"last_login_at":26},"2023-11-30T23:53:12.952+08:00","2026-10-20T16:16:54.352+08:00","Mg87g8Gk","1701359592888","大苹果","verified_author","\u002Fcuts\u002F17021220625513129.png",true,false,3,"2026-10-20T16:12:35+08:00",18,91373,[30,38,46,54,62,70,78,86,94,102,110,118,126,134,142,150,158,166],{"id":31,"created_at":32,"updated_at":32,"code":33,"author_id":7,"book_id":4,"slug":34,"article_id":35,"title":36,"sort_order":31,"content":37,"status":12},95,"2024-09-15T01:11:35.71+08:00","4N8m71KA","vue3学习路线-95",508,"vue3学习路线","学习 Vue 3 的新特性和编程方式，需要循序渐进地掌握基础知识、核心特性、以及一些高级功能。以下是一个从零开始学习 Vue 3 的学习路径：\n\n---\n\n## 1. **基础知识和环境搭建**\n\n### 1.1 JavaScript 和 ES6 基础\n\n在学习 Vue 3 之前，首先要掌握 JavaScript 的基础知识和 ES6+ 的特性，比如：\n\n- 箭头函数、模板字符串\n- 解构赋值、模块化 (`import`\u002F`export`)\n- `Promise`、`async\u002Fawait`\n\n### 1.2 搭建开发环境\n\n- 安装 Node.js 和 npm\n- 使用 `Vue CLI` 创建 Vue 3 项目\n  ```bash\n  npm install -g @vue\u002Fcli\n  vue create my-vue3-project\n  ```\n- 安装 Vite 作为项目构建工具\n  ```bash\n  npm init vite@latest my-vue3-project --template vue\n  cd my-vue3-project\n  npm install\n  npm run dev\n  ```\n\n---\n\n## 2. **Vue 3 核心基础**\n\n### 2.1 Vue 实例与模板语法\n\n- 理解 Vue 实例的结构：`data`、`methods`、`computed`、`watch`。\n- 学习 Vue 的模板语法和指令（`v-bind`、`v-for`、`v-if`、`v-model` 等）。\n\n### 2.2 组合式 API（Composition API）\n\n- **Composition API** 是 Vue 3 的核心新特性，取代了 Vue 2 的 Options API，代码更具模块化和复用性。\n- 重点学习 `setup` 函数的使用：\n  ```js\n  export default {\n    setup() {\n      const count = ref(0);\n      const increment = () => count.value++;\n\n      return { count, increment };\n    }\n  }\n  ```\n- 学习 `ref`、`reactive`、`computed`、`watch` 的使用和区别。\n\n---\n\n## 3. **Vue 3 进阶特性**\n\n### 3.1 生命周期钩子\n\n- 熟悉 Vue 3 的生命周期钩子：`onMounted`、`onUpdated`、`onUnmounted`。\n  ```js\n  import { onMounted } from 'vue';\n\n  export default {\n    setup() {\n      onMounted(() => {\n        console.log('Component is mounted');\n      });\n    }\n  };\n  ```\n\n### 3.2 Teleport 和 Fragment\n\n- **Teleport**：用于将组件渲染到 DOM 的某个指定位置，而不是父组件 DOM 层次结构中的默认位置。\n- **Fragment**：允许组件不必被一个单一的 DOM 根元素包裹，可以返回多个兄弟元素。\n\n---\n\n## 4. **组件化开发**\n\n### 4.1 父子组件通信\n\n- 父子组件的 props 和 emit 事件机制。\n- Vue 3 提供了 `emits` 选项用于更好地管理事件：\n  ```js\n  export default {\n    props: ['message'],\n    emits: ['update'],\n    setup(props, { emit }) {\n      const updateMessage = () => emit('update', 'new message');\n      return { updateMessage };\n    }\n  };\n  ```\n\n### 4.2 插槽（Slots）\n\n- **默认插槽** 和 **具名插槽** 的使用。\n- **作用域插槽**，实现父组件获取子组件的数据。\n\n---\n\n## 5. **路由管理（Vue Router 4）**\n\n### 5.1 安装 Vue Router 4\n\n```bash\nnpm install vue-router@next\n```\n\n### 5.2 基本路由配置\n\n- 路由创建和基础配置：\n  ```js\n  import { createRouter, createWebHistory } from 'vue-router';\n  import Home from '.\u002Fcomponents\u002FHome.vue';\n  import About from '.\u002Fcomponents\u002FAbout.vue';\n\n  const routes = [\n    { path: '\u002F', component: Home },\n    { path: '\u002Fabout', component: About }\n  ];\n\n  const router = createRouter({\n    history: createWebHistory(),\n    routes\n  });\n  ```\n\n### 5.3 动态路由和导航守卫\n\n- 使用动态路由：`\u002Fuser\u002F:id`\n- 实现全局导航守卫（`beforeEach`、`afterEach`）。\n\n---\n\n## 6. **状态管理（Vuex 4 或 Pinia）**\n\n### 6.1 使用 Vuex 进行状态管理\n\n- 了解 Vuex 4 的核心概念：`state`、`getters`、`mutations`、`actions`。\n\n### 6.2 轻量状态管理 Pinia\n\n- Vue 3 推荐的 Pinia 状态管理库，作为 Vuex 的替代方案：\n\n  ```bash\n  npm install pinia\n  ```\n\n  ```js\n  import { defineStore } from 'pinia';\n\n  export const useStore = defineStore('main', {\n    state: () => ({ count: 0 }),\n    actions: {\n      increment() {\n        this.count++;\n      }\n    }\n  });\n  ```\n\n---\n\n## 7. **数据请求（Axios 与 Vue）**\n\n### 7.1 安装 Axios\n\n```bash\nnpm install axios\n```\n\n### 7.2 发送 GET 和 POST 请求\n\n- 在 `setup` 中使用 `axios` 发送 HTTP 请求：\n  ```js\n  import axios from 'axios';\n  import { ref, onMounted } from 'vue';\n\n  export default {\n    setup() {\n      const data = ref([]);\n\n      onMounted(async () => {\n        const response = await axios.get('\u002Fapi\u002Fdata');\n        data.value = response.data;\n      });\n\n      return { data };\n    }\n  };\n  ```\n\n---\n\n## 8. **性能优化与调试**\n\n### 8.1 Vue DevTools\n\n- 使用 Vue 3 版的 Vue DevTools 进行调试和性能优化。\n\n### 8.2 Vue 3 的性能提升\n\n- Vue 3 对虚拟 DOM 的优化、编译器优化、Tree Shaking 等。\n\n---\n\n## 9. **项目实战**\n\n### 9.1 创建一个简单的单页应用（SPA）\n\n- 结合 Vue 3 的核心特性（Vue Router、Vuex\u002FPinia、Axios），实现一个完整的前端应用。\n\n### 9.2 项目中的性能优化\n\n- 异步组件与懒加载。\n- 使用 Webpack 或 Vite 进行代码分割和打包优化。\n\n---\n\n## 10. **测试与发布**\n\n### 10.1 单元测试和端到端测试\n\n- 使用 Vue Test Utils 进行组件单元测试。\n\n### 10.2 项目打包和部署\n\n- 使用 Vite 或 Webpack 打包。\n- 将项目部署到服务器或静态托管平台（如 Vercel、Netlify 等）。\n\n---\n\n## 11. **进阶学习**\n\n### 11.1 服务端渲染（SSR）\n\n- 使用 Nuxt.js 或 Vue 3 的服务端渲染功能，生成静态页面，提升 SEO 和首屏加载速度。\n\n### 11.2 使用 TypeScript\n\n- 学习如何在 Vue 3 中结合 TypeScript，提升代码的类型安全性和可维护性。\n\n---\n\n### 学习路线总结：\n\n1. JavaScript 和 ES6 基础\n2. Vue 3 基础（实例、模板语法、生命周期）\n3. 组合式 API 与新特性\n4. 组件化开发与插槽\n5. 路由管理（Vue Router 4）\n6. 状态管理（Vuex 或 Pinia）\n7. Axios 数据请求与管理\n8. 性能优化与调试\n9. 项目实战与部署\n10. 进阶学习（SSR、TypeScript 等）\n\n按这个学习路径循序渐进，你可以全面掌握 Vue 3 的新特性和最佳实践，最终能开发出高质量的 Vue 3 项目。\n",{"id":39,"created_at":40,"updated_at":40,"code":41,"author_id":7,"book_id":4,"slug":42,"article_id":43,"title":44,"sort_order":39,"content":45,"status":12},96,"2024-09-16T13:49:41.511+08:00","1l63RZKg","组合式-apicomposition-api-96",509,"组合式 API（Composition API）","Vue 3 引入的 **Composition API** 是一套全新的 API，它的设计初衷是解决 Vue 2 中 Options API 难以重用逻辑和模块化的问题。Composition API 更加灵活，便于代码的逻辑抽离、模块化、复用性强，并且在大型项目中更具优势。它基于函数的编程风格，可以让我们在同一个 `setup()` 函数内组织组件的各种逻辑。\n\n## 1. **Composition API 的核心概念**\n\n### 1.1 `setup` 函数\n\n`setup` 是 Vue 3 组件中使用 Composition API 的入口。在组件实例创建之前执行，它是 Composition API 的核心。在 `setup` 中，可以定义响应式状态、生命周期钩子、计算属性等。\n\n```js\nexport default {\n  setup() {\n    \u002F\u002F 组件逻辑写在这里\n    return {}; \u002F\u002F 返回值将暴露给模板\n  }\n}\n```\n\n### 1.2 `ref` 和 `reactive`\n\n- **`ref`**：创建一个可以跟踪单个基本类型数据的响应式对象。当你使用 `ref` 创建一个响应式数据时，Vue 会自动将其包装为对象形式，该对象包含 `.value` 属性来保存数据。\n\n```js\nimport { ref } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0); \u002F\u002F 创建响应式变量\n    const increment = () => {\n      count.value++; \u002F\u002F 通过 .value 访问和修改\n    };\n\n    return { count, increment };\n  }\n}\n```\n\n- **`reactive`**：创建一个响应式对象，适用于复杂的结构，如对象或数组。\n\n```js\nimport { reactive } from 'vue';\n\nexport default {\n  setup() {\n    const state = reactive({\n      count: 0,\n      user: {\n        name: 'Alice',\n        age: 30\n      }\n    });\n\n    const increment = () => {\n      state.count++;\n    };\n\n    return { state, increment };\n  }\n}\n```\n\n### 1.3 `computed` 计算属性\n\n`computed` 用于定义计算属性，它的返回值会根据依赖的响应式数据自动更新。\n\n```js\nimport { ref, computed } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n    const double = computed(() => count.value * 2);\n\n    return { count, double };\n  }\n}\n```\n\n### 1.4 `watch` 和 `watchEffect`\n\n- **`watch`**：监听响应式数据的变化，并执行回调函数。常用于异步操作或副作用逻辑。\n\n```js\nimport { ref, watch } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n  \n    watch(count, (newValue, oldValue) => {\n      console.log(`count changed from ${oldValue} to ${newValue}`);\n    });\n\n    return { count };\n  }\n}\n```\n\n- **`watchEffect`**：立即执行传入的副作用函数，并自动追踪依赖的响应式数据。\n\n```js\nimport { ref, watchEffect } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n  \n    watchEffect(() => {\n      console.log(`count is now: ${count.value}`);\n    });\n\n    return { count };\n  }\n}\n```\n\n### 1.5 生命周期钩子\n\nComposition API 提供的生命周期钩子类似于 Options API 中的生命周期钩子，但需要通过函数调用形式使用。\n\n- `onMounted`：组件挂载完成后触发\n- `onUnmounted`：组件销毁前触发\n- `onUpdated`：组件更新后触发\n\n```js\nimport { onMounted, onUnmounted } from 'vue';\n\nexport default {\n  setup() {\n    onMounted(() => {\n      console.log('Component has been mounted');\n    });\n\n    onUnmounted(() => {\n      console.log('Component is about to be destroyed');\n    });\n\n    return {};\n  }\n}\n```\n\n### 1.6 `provide` 和 `inject`\n\n- **`provide`**：向下传递数据，使得祖先组件能够将数据提供给后代组件。\n- **`inject`**：接收来自祖先组件通过 `provide` 提供的数据。\n\n```js\n\u002F\u002F Parent.vue\nimport { provide, ref } from 'vue';\n\nexport default {\n  setup() {\n    const message = ref('Hello from Parent');\n    provide('message', message); \u002F\u002F 提供数据\n\n    return {};\n  }\n}\n\n\u002F\u002F Child.vue\nimport { inject } from 'vue';\n\nexport default {\n  setup() {\n    const message = inject('message'); \u002F\u002F 注入数据\n\n    return { message };\n  }\n}\n```\n\n## 2. **组合式 API 的实际应用**\n\n### 2.1 创建一个计数器组件\n\n我们可以通过 Composition API 实现一个简单的计数器组件。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>计数值：{{ count }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { ref } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n    const increment = () => count.value++;\n\n    return { count, increment };\n  }\n}\n\u003C\u002Fscript>\n```\n\n### 2.2 响应式数据与计算属性\n\n我们可以通过 `computed` 和 `ref` 实现一个双倍计数器，并展示 Vue 3 中的响应式机制。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>计数值：{{ count }}\u003C\u002Fp>\n    \u003Cp>双倍计数值：{{ doubleCount }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { ref, computed } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n    const doubleCount = computed(() => count.value * 2);\n\n    const increment = () => count.value++;\n\n    return { count, doubleCount, increment };\n  }\n}\n\u003C\u002Fscript>\n```\n\n### 2.3 `watch` 实现副作用逻辑\n\n通过 `watch`，我们可以实现对响应式数据的精确监听。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>计数值：{{ count }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { ref, watch } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n\n    const increment = () => count.value++;\n\n    watch(count, (newValue, oldValue) => {\n      console.log(`count 从 ${oldValue} 变为 ${newValue}`);\n    });\n\n    return { count, increment };\n  }\n}\n\u003C\u002Fscript>\n```\n\n## 3. **组合式 API 的优势**\n\n- **逻辑拆分和复用**：在 Vue 2 中，逻辑往往是通过生命周期钩子、数据和方法进行组织的，导致同一逻辑分散在组件的不同位置。而 Vue 3 的 Composition API 可以将相关的逻辑组合在一起，更容易复用和模块化。\n- **代码更简洁**：Composition API 允许通过函数方式组织代码，使得代码逻辑更加集中。\n- **灵活性高**：它为大型项目提供了更好的逻辑复用性，并且可以无缝集成 TypeScript。\n\n## 4. **总结**\n\nVue 3 的 Composition API 为我们提供了一种更灵活的方式来组织和复用代码。通过 `setup` 函数，我们可以创建更简洁、更易维护的组件。核心的 `ref`、`reactive`、`computed` 和 `watch` 等工具帮助我们轻松管理响应式状态，同时生命周期钩子也变得更易于理解和使用。\n\n对于开发大型项目，Composition API 提供了更强的灵活性和可维护性，建议在学习 Vue 3 时优先掌握这一新特性。\n",{"id":47,"created_at":48,"updated_at":48,"code":49,"author_id":7,"book_id":4,"slug":50,"article_id":51,"title":52,"sort_order":47,"content":53,"status":12},97,"2024-09-16T14:09:28.329+08:00","7vXPE5KZ","vue-3-生命周期钩子-97",510,"Vue 3 生命周期钩子","在 Vue 3 中，生命周期钩子依然是组件的核心组成部分，用来在组件不同的生命周期阶段执行特定的代码逻辑。Vue 3 中的生命周期钩子发生了变化，不再像 Vue 2 中那样使用特定的选项属性，而是通过 Composition API 提供了函数的形式。\n\n## Vue 3 生命周期钩子概览\n\nVue 3 中的生命周期分为几个阶段，每个阶段都有相应的钩子函数：\n\n1. **组件创建阶段**：\n\n   - `setup`：初始化组件，数据和方法的定义阶段\n   - `beforeCreate`（Options API）\n   - `created`（Options API）\n2. **挂载阶段**：\n\n   - `beforeMount`：组件挂载到 DOM 之前\n   - `mounted`：组件挂载到 DOM 之后\n3. **更新阶段**：\n\n   - `beforeUpdate`：响应式数据更新且虚拟 DOM 重新渲染前\n   - `updated`：虚拟 DOM 更新并反映到真实 DOM 之后\n4. **卸载阶段**：\n\n   - `beforeUnmount`：组件卸载前\n   - `unmounted`：组件卸载后\n\n在 Vue 3 的 Composition API 中，对应的生命周期钩子函数为：\n\n- `onBeforeMount`\n- `onMounted`\n- `onBeforeUpdate`\n- `onUpdated`\n- `onBeforeUnmount`\n- `onUnmounted`\n\n此外，还有一些特殊的生命周期钩子：\n\n- `onActivated`：当使用 `\u003Ckeep-alive>` 包裹组件时，组件激活时触发。\n- `onDeactivated`：当使用 `\u003Ckeep-alive>` 包裹组件时，组件停用时触发。\n- `onErrorCaptured`：捕获子孙组件的错误。\n\n### 1. **组件创建阶段**\n\n#### `beforeCreate` & `created`（Options API）\n\n- **作用**：`beforeCreate` 和 `created` 这两个钩子主要用于初始化组件。在这个阶段，组件实例已经创建，但还没有挂载到 DOM，也没有生成任何模板。此时，数据已经可用（对于 Composition API 来说是 `setup` 阶段）。\n- **触发时机**：在实例被初始化之后，组件还未挂载时。\n\n```js\nexport default {\n  beforeCreate() {\n    console.log('beforeCreate: 组件实例刚刚被创建');\n  },\n  created() {\n    console.log('created: 组件实例已创建，但还未挂载');\n  }\n}\n```\n\n在 Composition API 中，`beforeCreate` 和 `created` 的作用几乎完全由 `setup` 替代。\n\n### 2. **挂载阶段**\n\n#### `onBeforeMount`\n\n- **作用**：在组件挂载到 DOM 之前调用。此时模板已编译好，但尚未插入到页面中，数据和 DOM 还未结合。\n- **触发时机**：挂载组件之前。\n\n```js\nimport { onBeforeMount } from 'vue';\n\nexport default {\n  setup() {\n    onBeforeMount(() => {\n      console.log('beforeMount: 组件即将挂载');\n    });\n  }\n}\n```\n\n#### `onMounted`\n\n- **作用**：组件已经被挂载到 DOM 上，所有的 DOM 操作已经完成。在这个阶段，组件的元素可以在页面中被访问，常用于初始化 DOM 交互（如获取 DOM 元素的大小或进行第三方库的初始化）。\n- **触发时机**：挂载组件之后。\n\n```js\nimport { onMounted } from 'vue';\n\nexport default {\n  setup() {\n    onMounted(() => {\n      console.log('mounted: 组件已挂载');\n    });\n  }\n}\n```\n\n### 3. **更新阶段**\n\n#### `onBeforeUpdate`\n\n- **作用**：组件响应式数据发生变化时，在虚拟 DOM 更新之前调用。此时你可以在组件更新前做一些事情，比如取消某些操作，或处理特定的数据变化。\n- **触发时机**：数据发生变化，但还未渲染到页面时。\n\n```js\nimport { onBeforeUpdate } from 'vue';\n\nexport default {\n  setup() {\n    onBeforeUpdate(() => {\n      console.log('beforeUpdate: 组件数据发生变化，虚拟 DOM 即将更新');\n    });\n  }\n}\n```\n\n#### `onUpdated`\n\n- **作用**：在组件的虚拟 DOM 重新渲染并更新到真实 DOM 后调用。此时，最新的 DOM 已经反映了最新的数据状态。可以用来执行某些与 DOM 相关的更新后操作。\n- **触发时机**：数据发生变化并已经渲染到页面之后。\n\n```js\nimport { onUpdated } from 'vue';\n\nexport default {\n  setup() {\n    onUpdated(() => {\n      console.log('updated: 组件更新完成，DOM 已经更新');\n    });\n  }\n}\n```\n\n### 4. **卸载阶段**\n\n#### `onBeforeUnmount`\n\n- **作用**：在组件实例即将被销毁时调用。在这个阶段，组件仍然保留在 DOM 中，通常用于清除定时器或取消订阅等操作。\n- **触发时机**：组件即将从 DOM 中卸载时。\n\n```js\nimport { onBeforeUnmount } from 'vue';\n\nexport default {\n  setup() {\n    onBeforeUnmount(() => {\n      console.log('beforeUnmount: 组件即将卸载');\n    });\n  }\n}\n```\n\n#### `onUnmounted`\n\n- **作用**：组件被销毁后调用。在这个阶段，组件的 DOM 元素已经被移除，所有的事件监听器、定时器等也被清除，常用于资源的释放和一些清理操作。\n- **触发时机**：组件完全从 DOM 中移除之后。\n\n```js\nimport { onUnmounted } from 'vue';\n\nexport default {\n  setup() {\n    onUnmounted(() => {\n      console.log('unmounted: 组件已卸载');\n    });\n  }\n}\n```\n\n### 5. **特殊的生命周期钩子**\n\n#### `onActivated` & `onDeactivated`\n\n- **作用**：当组件被 `\u003Ckeep-alive>` 包裹时，这两个钩子用于处理组件的激活和停用。`onActivated` 在组件从缓存中被激活时触发，`onDeactivated` 在组件被缓存时触发。\n- **触发时机**：\n  - `onActivated`：组件被 `\u003Ckeep-alive>` 缓存并再次激活时触发。\n  - `onDeactivated`：组件被 `\u003Ckeep-alive>` 缓存时触发。\n\n```js\nimport { onActivated, onDeactivated } from 'vue';\n\nexport default {\n  setup() {\n    onActivated(() => {\n      console.log('组件已激活');\n    });\n\n    onDeactivated(() => {\n      console.log('组件已停用');\n    });\n  }\n}\n```\n\n#### `onErrorCaptured`\n\n- **作用**：在组件中捕获到子组件的错误时触发，可以进行错误处理。常用于错误边界设计。\n- **触发时机**：子组件发生错误时。\n\n```js\nimport { onErrorCaptured } from 'vue';\n\nexport default {\n  setup() {\n    onErrorCaptured((err, instance, info) => {\n      console.error('捕获到错误：', err);\n    });\n  }\n}\n```\n\n## Vue 3 生命周期触发顺序\n\nVue 3 生命周期的触发顺序如下：\n\n1. `beforeCreate` (Options API)\n2. `created` (Options API)\n3. `setup` (Composition API)\n4. `beforeMount`\n5. `onBeforeMount`\n6. `mounted`\n7. `onMounted`\n8. `beforeUpdate`\n9. `onBeforeUpdate`\n10. `updated`\n11. `onUpdated`\n12. `beforeUnmount`\n13. `onBeforeUnmount`\n14. `unmounted`\n15. `onUnmounted`\n\n## 应用场景示例\n\n### 1. `onMounted`：初始化第三方插件\n\n```js\nimport { onMounted } from 'vue';\n\nexport default {\n  setup() {\n    onMounted(() => {\n      \u002F\u002F 假设初始化一个第三方库\n      initializeThirdPartyLibrary();\n    });\n  }\n}\n```\n\n### 2. `onBeforeUnmount`：清除定时器\n\n```js\nimport { onBeforeUnmount, ref } from 'vue';\n\nexport default {\n  setup() {\n    const intervalId = ref(null);\n\n    const startTimer = () => {\n      intervalId.value = setInterval(() => {\n        console.log('计时中...');\n      }, 1000);\n    };\n\n    onBeforeUnmount(() => {\n      clearInterval(intervalId.value);\n      console.log('组件卸载，定时器已清除');\n    });\n\n    return { startTimer };\n  }\n}\n```\n\n### 3. `watchEffect` 和 `onUpdated`：监控 DOM 更新\n\n```js\nimport { ref, watchEffect, onUpdated } from 'vue';\n\nexport default {\n  setup() {\n    const count = ref(0);\n\n    watchEffect(() => {\n      console.log('响应式数据变化: ', count.value);\n    });\n\n    onUpdated(() => {\n      console.log('DOM 已更新');\n    });\n\n    const increment = () => {\n      count.value++;\n    };\n\n    return\n\n { count, increment };\n  }\n}\n```\n\n## 总结\n\nVue 3 提供了更加简洁、灵活的生命周期管理方式，尤其是 Composition API 中通过 `onX` 系列函数的调用，使得生命周期管理变得更加直观和简便。通过了解和合理使用这些生命周期钩子，可以让你的组件更加健壮、维护更容易。\n",{"id":55,"created_at":56,"updated_at":56,"code":57,"author_id":7,"book_id":4,"slug":58,"article_id":59,"title":60,"sort_order":55,"content":61,"status":12},98,"2024-09-17T02:04:22.737+08:00","Q18bjJ8x","teleport-和-fragment-98",511,"Teleport 和 Fragment","在 Vue 3 中，引入了两个新的功能组件：`Teleport` 和 `Fragment`，它们扩展了 Vue 模板的灵活性，帮助开发者更好地管理 DOM 元素结构和页面性能。下面将详细说明它们的用法及应用场景。\n\n## 1. **Vue 3 `Teleport`**\n\n`Teleport` 是 Vue 3 新增的一个功能组件，用于将其包裹的模板元素渲染到指定的 DOM 节点，而不是默认的当前组件的根 DOM 节点。通过 `Teleport`，我们可以将元素“传送”到页面中的任何地方。\n\n### **使用场景**\n\n- **模态框**：模态框通常不希望嵌套在组件内部，而是挂载在全局 DOM 节点，如 `\u003Cbody>`，以确保它不被父级样式影响，并且可以更好地管理层级。\n- **通知组件**：将提示、通知等浮动元素直接挂载到 `body`，避免层级复杂时的定位问题。\n\n### **使用方法**\n\n#### 基本用法\n\n`Teleport` 接受一个 `to` 属性，用于指定传送的目标 DOM 选择器。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前组件内容\u003C\u002Fh1>\n    \u003Cteleport to=\"body\">\n      \u003Cdiv class=\"modal\">\n        \u003Cp>这是一个模态框，直接挂载到 body 下\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002Fteleport>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'MyComponent'\n}\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.modal {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  background-color: white;\n  padding: 20px;\n  border: 1px solid #ccc;\n}\n\u003C\u002Fstyle>\n```\n\n在这个例子中，虽然模态框的模板写在组件内部，但是它最终会被渲染到 `\u003Cbody>` 中。\n\n#### 动态传送目标\n\n你还可以使用动态的 `to` 属性来决定传送的目标。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前组件内容\u003C\u002Fh1>\n    \u003Cteleport :to=\"dynamicTarget\">\n      \u003Cdiv class=\"modal\">\n        \u003Cp>这是一个动态目标传送的模态框\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002Fteleport>\n    \u003Cbutton @click=\"changeTarget\">改变传送目标\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data() {\n    return {\n      dynamicTarget: 'body'\n    };\n  },\n  methods: {\n    changeTarget() {\n      this.dynamicTarget = '#anotherTarget';\n    }\n  }\n}\n\u003C\u002Fscript>\n\n\u003C!-- 另一个目标区域 -->\n\u003Cdiv id=\"anotherTarget\">\u003C\u002Fdiv>\n\n```\n\n点击按钮后，模态框将从 `body` 传送到 `#anotherTarget`。\n\n#### 禁用传送\n\n你可以通过 `disabled` 属性来临时禁用传送功能。\n\n```vue\n\u003Cteleport to=\"body\" :disabled=\"true\">\n  \u003Cdiv>这段内容不会被传送\u003C\u002Fdiv>\n\u003C\u002Fteleport>\n```\n\n当 `disabled` 为 `true` 时，`Teleport` 会将内容渲染在组件的默认位置，而不是目标位置。\n\n### **总结**\n\n`Teleport` 提供了灵活的方式来管理复杂布局中的浮动层或全局组件，它让我们可以更轻松地控制 DOM 元素在页面上的位置，并避免与组件层次结构的耦合。\n\n---\n\n## 2. **Vue 3 `Fragment`**\n\n在 Vue 2 中，每个组件模板只能有一个根元素。为了满足这一要求，开发者通常需要在外层包裹一个无意义的 `div` 或 `span` 标签。这可能导致页面生成多余的 DOM 元素。`Fragment` 解决了这个问题，使得我们可以在不引入额外 DOM 元素的情况下返回多个根元素。\n\n### **使用场景**\n\n- **避免无用的包裹元素**：在一些场景下，为了满足 Vue 2 对单一根元素的要求，往往会出现不必要的 DOM 元素。`Fragment` 允许我们直接返回多个根节点，优化 HTML 结构。\n- **提高性能和代码可读性**：减少不必要的 DOM 结构层次可以使代码更加简洁，也有助于提升页面性能。\n\n### **使用方法**\n\n在 Vue 3 中，`Fragment` 是默认行为。当组件的模板中存在多个根元素时，Vue 会自动将其处理为 `Fragment`，无需额外引入任何 API。\n\n#### 例子\n\n在 Vue 2 中，必须有一个根元素：\n\n```vue\n\u003C!-- Vue 2 示例 -->\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>标题\u003C\u002Fh1>\n    \u003Cp>内容段落\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n在 Vue 3 中，你可以直接返回多个根元素：\n\n```vue\n\u003C!-- Vue 3 示例 -->\n\u003Ctemplate>\n  \u003Ch1>标题\u003C\u002Fh1>\n  \u003Cp>内容段落\u003C\u002Fp>\n\u003C\u002Ftemplate>\n```\n\n#### 动态列表\n\n当我们渲染动态列表时，`Fragment` 能避免我们额外使用无用的包裹元素。\n\n```vue\n\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"item in items\" :key=\"item.id\">\n      {{ item.name }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data() {\n    return {\n      items: [\n        { id: 1, name: 'Item 1' },\n        { id: 2, name: 'Item 2' }\n      ]\n    };\n  }\n}\n\u003C\u002Fscript>\n```\n\n在这个例子中，Vue 3 会自动将 `li` 元素集合包装成一个 `Fragment`，并且不会生成多余的外层元素。\n\n### **总结**\n\n`Fragment` 提供了在组件中返回多个根元素的能力，减少了不必要的 DOM 包裹元素，让我们的模板结构更加简洁、清晰，同时也优化了渲染性能。\n\n---\n\n## 总结\n\n- **`Teleport`**：用于将某个组件的模板内容“传送”到指定的 DOM 节点，常用于模态框、通知等不依赖当前组件结构的全局元素。\n- **`Fragment`**：允许组件返回多个根节点，解决了 Vue 2 中每个组件必须有一个根元素的问题，简化了 DOM 结构，提升了页面性能。\n\n通过 `Teleport` 和 `Fragment`，Vue 3 提供了更强大和灵活的布局管理能力，帮助开发者更好地构建页面结构。\n",{"id":63,"created_at":64,"updated_at":64,"code":65,"author_id":7,"book_id":4,"slug":66,"article_id":67,"title":68,"sort_order":63,"content":69,"status":12},99,"2024-09-17T02:10:45.435+08:00","kbXvrOXM","父子组件通信-99",512,"父子组件通信","在 Vue 3 中，`setup` 函数是 Vue 3 中的 Composition API 的核心，它提供了一种更灵活的方式来定义组件逻辑。`props` 和 `emit` 是父子组件通信的基础，通过 `setup` 函数，这些机制得到了更简化的实现。Vue 3 中的 `emits` 事件管理也变得更加结构化。\n\n### 一、`setup` 下的 `props` 和 `emit`\n\n在 `setup` 函数中，`props` 是以参数的形式传递给组件，而 `emit` 事件函数也是通过参数获取的。我们来看看如何使用它们。\n\n#### 1. 父组件传递 `props`\n\n在 Vue 3 中，父组件通过 `props` 向子组件传递数据的方式没有改变，仍然是在子组件标签上使用绑定的方式传递数据。\n\n**父组件代码：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>父组件\u003C\u002Fh1>\n    \u003CChildComponent :message=\"parentMessage\" :count=\"parentCount\" @child-event=\"handleChildEvent\">\u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n  data() {\n    return {\n      parentMessage: 'Hello from parent!',\n      parentCount: 10\n    };\n  },\n  methods: {\n    handleChildEvent(payload) {\n      console.log('父组件接收到的子组件事件数据:', payload);\n    }\n  }\n};\n\u003C\u002Fscript>\n```\n\n在这个例子中，父组件传递了 `message` 和 `count` 给子组件。\n\n#### 2. 子组件接收 `props` 和触发 `emit`\n\n在 `setup` 函数中，`props` 和 `emit` 通过参数接收。`emit` 可以触发自定义事件，将数据传递给父组件。\n\n**子组件代码：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cp>Message from parent: {{ message }}\u003C\u002Fp>\n    \u003Cp>Count from parent: {{ count }}\u003C\u002Fp>\n    \u003Cbutton @click=\"sendMessage\">向父组件发送事件\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  props: {\n    message: String,\n    count: Number,\n  },\n  setup(props, { emit }) {\n    const sendMessage = () => {\n      emit('child-event', 'Hello from Child');\n    };\n\n    return { props, sendMessage };\n  }\n});\n\u003C\u002Fscript>\n```\n\n在 `setup` 函数中，我们通过参数接收了 `props`，并定义了一个 `sendMessage` 函数，通过 `emit` 触发自定义事件 `child-event`，将数据传递给父组件。\n\n### 二、`emits` 事件管理\n\n在 Vue 3 中，`emits` 选项允许我们显式地定义子组件会触发哪些事件，这是一种增强的事件管理方式。\n\n#### 1. `emits` 选项\n\n在子组件中，我们可以通过 `emits` 选项来定义组件可以触发的事件。这样做的好处是，可以对事件进行更好的文档化和管理，也可以对事件触发进行校验。\n\n```vue\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  props: {\n    message: String,\n    count: Number,\n  },\n  emits: ['child-event'],  \u002F\u002F 定义子组件将会触发的事件\n  setup(props, { emit }) {\n    const sendMessage = () => {\n      emit('child-event', 'Hello from Child');  \u002F\u002F 触发事件\n    };\n\n    return { props, sendMessage };\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 2. 事件校验\n\n`emits` 选项不仅能定义事件，还可以为事件添加校验逻辑。我们可以通过函数校验事件的参数是否符合预期。\n\n```vue\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  props: {\n    message: String,\n    count: Number,\n  },\n  emits: {\n    'child-event': (payload) => {\n      return typeof payload === 'string';  \u002F\u002F 校验事件参数必须是字符串\n    }\n  },\n  setup(props, { emit }) {\n    const sendMessage = () => {\n      emit('child-event', 'Hello from Child');\n    };\n\n    return { props, sendMessage };\n  }\n});\n\u003C\u002Fscript>\n```\n\n在这个示例中，`emits` 选项中为 `child-event` 事件定义了一个校验函数，只有当事件参数是字符串时，事件才会被触发。\n\n### 三、`setup` 下的原理解析\n\n#### 1. `props` 原理\n\n在 `setup` 中接收的 `props` 是响应式的，这意味着当父组件传递的 `props` 数据变化时，子组件会自动更新。\n\n- 在 `setup` 中接收到的 `props` 是不可直接解构的，否则将失去响应式能力。如果需要解构，可以使用 `toRefs` 或 `toRef`。\n\n```javascript\nimport { toRefs } from 'vue';\n\nsetup(props) {\n  const { message, count } = toRefs(props);  \u002F\u002F 解构成响应式变量\n  return { message, count };\n}\n```\n\n#### 2. `emit` 原理\n\n`emit` 函数是 Vue 内置的事件触发机制，它允许组件触发自定义事件，并将数据传递给父组件。Vue 内部会根据 `emits` 选项确保事件是有效的并调用父组件的监听器。\n\n### 四、示例：父子组件通信\n\n我们通过一个简单的例子，展示 `props` 和 `emit` 在 `setup` 下的具体应用。\n\n#### **父组件：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>父组件\u003C\u002Fh1>\n    \u003Cp>Count: {{ parentCount }}\u003C\u002Fp>\n    \u003CChildComponent :count=\"parentCount\" @update-count=\"handleUpdateCount\">\u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n  data() {\n    return {\n      parentCount: 0\n    };\n  },\n  methods: {\n    handleUpdateCount(newCount) {\n      this.parentCount = newCount;\n    }\n  }\n};\n\u003C\u002Fscript>\n```\n\n#### **子组件：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cp>当前计数: {{ count }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">增加计数\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  props: {\n    count: Number,\n  },\n  emits: ['update-count'],\n  setup(props, { emit }) {\n    const increment = () => {\n      emit('update-count', props.count + 1);\n    };\n\n    return { increment };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 总结\n\n1. **`props`**：`setup` 函数中 `props` 是通过参数传递的，不能直接解构，否则会丧失响应性。\n2. **`emit`**：`emit` 函数可以触发事件，父组件通过监听子组件的自定义事件来实现双向数据交互。\n3. **`emits`**：在 Vue 3 中，可以通过 `emits` 选项来显式定义组件的事件，并且可以进行参数校验。\n\nVue 3 的 `setup` 和 `emits` 机制使组件通信变得更加简洁和灵活，同时也增强了代码的可读性和可维护性。\n",{"id":71,"created_at":72,"updated_at":72,"code":73,"author_id":7,"book_id":4,"slug":74,"article_id":75,"title":76,"sort_order":71,"content":77,"status":12},100,"2024-09-17T02:18:11.372+08:00","1xXEZ0Ka","插槽slots-100",513,"插槽（Slots）","在 Vue 3 中，`setup` 函数中处理插槽（Slots）与 Vue 2 中插槽的使用方式有一些不同，尤其是在处理具名插槽和作用域插槽时。Vue 3 通过 Composition API 和 `setup` 函数使插槽的管理更加灵活和简洁。\n\n本文将详细介绍如何在 Vue 3 中使用默认插槽、具名插槽和作用域插槽，并通过实例讲解如何在 `setup` 函数中使用这些插槽。\n\n### 一、Vue 3 插槽基础概念\n\n插槽（Slots）是 Vue 中用于在子组件中插入动态内容的机制。父组件可以在子组件的插槽中传递 HTML 模板片段或数据。\n\n#### 1. 默认插槽\n\n默认插槽用于在不具名的情况下，将父组件的内容插入到子组件的插槽位置。\n\n#### 2. 具名插槽\n\n具名插槽允许我们定义多个插槽，每个插槽都可以有一个名称，父组件可以选择向指定插槽传递内容。\n\n#### 3. 作用域插槽\n\n作用域插槽允许子组件将数据暴露给父组件，父组件可以根据这些数据来决定如何渲染插槽中的内容。\n\n### 二、默认插槽的使用\n\n默认插槽用于父组件向子组件传递内容，而不需要具名。\n\n#### 1. 父组件使用默认插槽\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>父组件\u003C\u002Fh1>\n    \u003CChildComponent>\n      \u003Cp>这是传递给子组件的内容\u003C\u002Fp>\n    \u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n};\n\u003C\u002Fscript>\n```\n\n#### 2. 子组件接收默认插槽\n\n在子组件中，我们可以通过 `\u003Cslot>` 标签来占位，表示父组件传递的内容将在此处被插入。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cslot>\u003C\u002Fslot> \u003C!-- 父组件传递的内容将插入到这里 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'ChildComponent',\n};\n\u003C\u002Fscript>\n```\n\n### 三、具名插槽的使用\n\n具名插槽允许父组件通过指定的插槽名称来传递不同的内容。\n\n#### 1. 父组件使用具名插槽\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>父组件\u003C\u002Fh1>\n    \u003CChildComponent>\n      \u003Ctemplate v-slot:header>\n        \u003Ch3>这是标题部分\u003C\u002Fh3>\n      \u003C\u002Ftemplate>\n      \u003Ctemplate v-slot:footer>\n        \u003Cp>这是页脚部分\u003C\u002Fp>\n      \u003C\u002Ftemplate>\n    \u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n};\n\u003C\u002Fscript>\n```\n\n#### 2. 子组件接收具名插槽\n\n子组件通过 `v-slot` 指令或 `\u003Cslot name>` 来接收不同名称的插槽内容。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cdiv class=\"header\">\n      \u003Cslot name=\"header\">\u003C\u002Fslot> \u003C!-- 具名插槽 \"header\" -->\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"footer\">\n      \u003Cslot name=\"footer\">\u003C\u002Fslot> \u003C!-- 具名插槽 \"footer\" -->\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'ChildComponent',\n};\n\u003C\u002Fscript>\n```\n\n### 四、作用域插槽的使用\n\n作用域插槽允许子组件将内部的数据暴露给父组件，父组件根据这些数据决定如何渲染插槽内容。\n\n#### 1. 父组件使用作用域插槽\n\n在父组件中，我们通过 `v-slot` 接收子组件暴露的参数，并可以动态渲染内容。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>父组件\u003C\u002Fh1>\n    \u003CChildComponent>\n      \u003Ctemplate v-slot:default=\"slotProps\">\n        \u003Cp>来自子组件的数据：{{ slotProps.info }}\u003C\u002Fp>\n      \u003C\u002Ftemplate>\n    \u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n};\n\u003C\u002Fscript>\n```\n\n#### 2. 子组件暴露数据给作用域插槽\n\n子组件通过 `\u003Cslot>` 标签暴露数据给父组件。在 Vue 3 的 `setup` 中，我们可以通过 `slots.default` 来使用插槽。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cslot :info=\"message\">\u003C\u002Fslot> \u003C!-- 暴露数据给父组件 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  setup() {\n    const message = 'Hello from Child Component';\n    return { message };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 五、在 `setup` 中使用插槽\n\n在 Vue 3 的 `setup` 函数中，插槽通过 `context.slots` 来访问。我们可以使用 `slots.default()` 来渲染默认插槽内容，或者通过 `slots.[name]()` 来渲染具名插槽。\n\n#### 1. 默认插槽在 `setup` 中的使用\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>子组件\u003C\u002Fh2>\n    \u003Cdiv>\n      \u003Cslot>\u003C\u002Fslot> \u003C!-- 这里会插入默认插槽内容 -->\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  setup(props, { slots }) {\n    return () => (\n      \u003Cdiv>\n        \u003Ch2>子组件\u003C\u002Fh2>\n        \u003Cdiv>{ slots.default ? slots.default() : '无内容' }\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    );\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 2. 具名插槽在 `setup` 中的使用\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cslot name=\"header\">\u003C\u002Fslot> \u003C!-- 渲染 header 插槽 -->\n    \u003Cdiv>主体内容\u003C\u002Fdiv>\n    \u003Cslot name=\"footer\">\u003C\u002Fslot> \u003C!-- 渲染 footer 插槽 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  setup(props, { slots }) {\n    return () => (\n      \u003Cdiv>\n        \u003Cdiv>{ slots.header ? slots.header() : '默认头部' }\u003C\u002Fdiv>\n        \u003Cdiv>主体内容\u003C\u002Fdiv>\n        \u003Cdiv>{ slots.footer ? slots.footer() : '默认尾部' }\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    );\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 六、作用域插槽在 `setup` 中的使用\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cslot :info=\"info\">\u003C\u002Fslot> \u003C!-- 将子组件数据暴露给父组件 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  setup() {\n    const info = '从子组件传递的作用域数据';\n    return { info };\n  }\n});\n\u003C\u002Fscript>\n```\n\n在父组件中，可以通过 `v-slot` 指令接收并使用子组件暴露的 `info` 数据：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CChildComponent v-slot=\"{ info }\">\n      \u003Cp>父组件接收到的作用域数据：{{ info }}\u003C\u002Fp>\n    \u003C\u002FChildComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport ChildComponent from '.\u002FChildComponent.vue';\n\nexport default {\n  components: { ChildComponent },\n};\n\u003C\u002Fscript>\n```\n\n### 总结\n\n1. **默认插槽**：通过 `\u003Cslot>` 标签使用，父组件可以向子组件传递内容。\n2. **具名插槽**：通过 `v-slot:[name]` 指令使用，允许在子组件内定义多个插槽位置。\n3. **作用域插槽**：子组件可以暴露数据给父组件，父组件根据暴露的数据动态渲染内容。\n4. **在 `setup` 中使用插槽**：通过 `context.slots` 获取插槽内容，结合 JSX 或渲染函数可以灵活渲染插槽。\n\nVue 3 的插槽机制相比 Vue 2 更加灵活，在 `setup` 中使用插槽和暴露数据使得组件通信更加简洁。\n",{"id":79,"created_at":80,"updated_at":80,"code":81,"author_id":7,"book_id":4,"slug":82,"article_id":83,"title":84,"sort_order":79,"content":85,"status":12},101,"2024-09-17T02:19:15.91+08:00","Mg871dKG","路由管理vue-router-4-101",514,"路由管理（Vue Router 4）","在 Vue 3 中，`setup` 函数结合 Vue Router 4 实现路由管理为项目带来更强的灵活性和清晰的逻辑结构。Vue Router 4 是 Vue 3 的官方路由库，支持各种现代化路由特性，如动态路由、路由守卫和路由懒加载。接下来，我们详细介绍如何在 Vue 3 中使用 Vue Router 4，包含动态路由、导航守卫的实现，并通过实例讲解实际场景的应用。\n\n### 一、Vue Router 4 的基础使用\n\n#### 1. 安装 Vue Router 4\n\n在 Vue 3 项目中使用 Vue Router 4 需要先安装依赖：\n\n```bash\nnpm install vue-router@4\n```\n\n#### 2. 基本路由配置\n\n首先，我们需要在项目中创建一个 `router\u002Findex.js` 文件，并定义路由规则：\n\n```js\nimport { createRouter, createWebHistory } from 'vue-router';\nimport Home from '..\u002Fviews\u002FHome.vue';\nimport About from '..\u002Fviews\u002FAbout.vue';\n\nconst routes = [\n  { path: '\u002F', name: 'Home', component: Home },\n  { path: '\u002Fabout', name: 'About', component: About },\n];\n\nconst router = createRouter({\n  history: createWebHistory(process.env.BASE_URL),\n  routes,\n});\n\nexport default router;\n```\n\n#### 3. 在主文件中注册路由\n\n在 `main.js` 文件中将路由器挂载到 Vue 应用中：\n\n```js\nimport { createApp } from 'vue';\nimport App from '.\u002FApp.vue';\nimport router from '.\u002Frouter'; \u002F\u002F 引入路由配置\n\nconst app = createApp(App);\n\napp.use(router); \u002F\u002F 注册路由\napp.mount('#app');\n```\n\n#### 4. 基础路由导航\n\n在 Vue 组件中通过 `router-link` 进行导航：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Crouter-link to=\"\u002F\">首页\u003C\u002Frouter-link>\n    \u003Crouter-link to=\"\u002Fabout\">关于\u003C\u002Frouter-link>\n    \u003Crouter-view>\u003C\u002Frouter-view> \u003C!-- 显示匹配的路由组件 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n### 二、动态路由\n\n动态路由允许我们定义具有可变部分的路径，常用于详情页等场景。\n\n#### 1. 动态路由定义\n\n在路由配置中，可以使用 `:param` 语法定义动态路由：\n\n```js\nconst routes = [\n  { path: '\u002F', name: 'Home', component: Home },\n  { path: '\u002Fuser\u002F:id', name: 'User', component: User }, \u002F\u002F 动态路由\n];\n```\n\n例如，当访问 `\u002Fuser\u002F123` 时，`:id` 的值将为 `123`。\n\n#### 2. 动态参数获取\n\n在动态路由匹配到的组件中，可以通过 `useRoute` 获取路由参数：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>用户 ID: {{ userId }}\u003C\u002Fh1>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useRoute } from 'vue-router';\n\nexport default defineComponent({\n  setup() {\n    const route = useRoute();\n    const userId = route.params.id; \u002F\u002F 获取动态参数\n\n    return { userId };\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 3. 动态路由的场景\n\n动态路由常用于用户详情页面或文章详情页面。例如，电商网站的商品详情页 `\u002Fproduct\u002F:productId`，通过 `productId` 动态获取对应商品的信息。\n\n### 三、路由守卫\n\n导航守卫用于控制路由切换前、切换后的逻辑。Vue Router 4 提供了全局、路由级别和组件内的导航守卫。\n\n#### 1. 全局守卫\n\n全局守卫可以用于处理所有路由导航行为。常见的应用场景是权限控制或全局登录拦截。\n\n- `beforeEach`：在每次路由跳转前触发\n- `afterEach`：在路由跳转后触发\n\n```js\nrouter.beforeEach((to, from, next) => {\n  const isAuthenticated = !!localStorage.getItem('token'); \u002F\u002F 简单判断是否登录\n  if (to.name !== 'Login' && !isAuthenticated) {\n    next({ name: 'Login' }); \u002F\u002F 未登录时跳转到登录页\n  } else {\n    next(); \u002F\u002F 否则继续跳转\n  }\n});\n```\n\n#### 2. 路由守卫示例\n\n```js\nconst routes = [\n  { path: '\u002F', name: 'Home', component: Home },\n  { path: '\u002Flogin', name: 'Login', component: Login },\n  { \n    path: '\u002Fdashboard', \n    name: 'Dashboard', \n    component: Dashboard,\n    meta: { requiresAuth: true } \u002F\u002F 添加元信息用于权限验证\n  }\n];\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n\n\u002F\u002F 导航守卫\nrouter.beforeEach((to, from, next) => {\n  const isAuthenticated = !!localStorage.getItem('token'); \u002F\u002F 判断登录状态\n  if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) {\n    next('\u002Flogin'); \u002F\u002F 未登录则跳转到登录页\n  } else {\n    next(); \u002F\u002F 放行\n  }\n});\n```\n\n#### 3. 组件内守卫\n\n组件内的导航守卫可以在组件生命周期中控制导航行为。主要有以下几个钩子：\n\n- `beforeRouteEnter`：在进入路由前调用\n- `beforeRouteUpdate`：在当前路由变化但复用组件时调用\n- `beforeRouteLeave`：在离开路由前调用\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>这是用户详情页\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  beforeRouteEnter(to, from, next) {\n    console.log('进入该路由之前');\n    next();\n  },\n  beforeRouteLeave(to, from, next) {\n    console.log('离开该路由之前');\n    next();\n  }\n};\n\u003C\u002Fscript>\n```\n\n#### 4. 路由守卫的场景\n\n- **登录拦截**：如果用户未登录，则拦截用户的操作，重定向到登录页面。\n- **权限控制**：根据用户的权限，决定是否允许用户访问某些路由，如后台管理页面。\n\n### 四、路由懒加载\n\n在大型项目中，懒加载路由有助于提高应用性能，尤其是首次加载的速度。Vue Router 4 支持使用动态导入来实现路由的懒加载。\n\n#### 1. 路由懒加载示例\n\n通过 `import()` 函数动态导入组件实现懒加载：\n\n```js\nconst routes = [\n  { \n    path: '\u002Fabout', \n    name: 'About', \n    component: () => import(\u002F* webpackChunkName: \"about\" *\u002F '..\u002Fviews\u002FAbout.vue') \n  }\n];\n```\n\n#### 2. 懒加载的场景\n\n路由懒加载通常用于大型 SPA（单页面应用）中，尤其是一些不常访问的页面，如用户设置页、帮助页等，避免加载过多的资源。\n\n### 五、动态添加路由\n\nVue Router 4 提供了 `addRoute` 方法，可以动态添加路由，通常用于权限管理或模块化应用中。\n\n#### 1. 动态添加路由示例\n\n```js\nconst newRoute = {\n  path: '\u002Fadmin',\n  name: 'Admin',\n  component: () => import('..\u002Fviews\u002FAdmin.vue')\n};\n\n\u002F\u002F 动态添加路由\nrouter.addRoute(newRoute);\n```\n\n#### 2. 场景\n\n动态路由在基于权限的路由系统中非常常见。比如，当用户登录后，根据其角色动态加载不同的路由模块。\n\n### 六、示例：完整的路由守卫与动态路由使用\n\n```js\nimport { createRouter, createWebHistory } from 'vue-router';\nimport Home from '..\u002Fviews\u002FHome.vue';\nimport Login from '..\u002Fviews\u002FLogin.vue';\nimport User from '..\u002Fviews\u002FUser.vue';\n\nconst routes = [\n  { path: '\u002F', name: 'Home', component: Home },\n  { path: '\u002Flogin', name: 'Login', component: Login },\n  { path: '\u002Fuser\u002F:id', name: 'User', component: User, meta: { requiresAuth: true } }\n];\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n\n\u002F\u002F 全局导航守卫\nrouter.beforeEach((to, from, next) => {\n  const isAuthenticated = !!localStorage.getItem('token');\n  if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) {\n    next({ name: 'Login' });\n  } else {\n    next();\n  }\n});\n\nexport default router;\n```\n\n### 总结\n\n1. **基础路由**：定义静态和动态路由，使用 `router-link` 进行导航。\n2. **动态路由**：在路径中使用 `:param` 来定义动态路由。\n3. **路由守卫**：通过全局守卫、路由级别守卫和组件内守卫来实现权限控制和路由拦截。\n4. **懒加载**：通过动态导入实现路由懒加载，优化应用性能。\n5.\n\n**动态添加路由**：通过 `addRoute` 方法实现基于权限的动态路由加载。\n",{"id":87,"created_at":88,"updated_at":88,"code":89,"author_id":7,"book_id":4,"slug":90,"article_id":91,"title":92,"sort_order":87,"content":93,"status":12},102,"2024-09-17T02:29:13.072+08:00","yj6y7QX9","状态管理vuex-102",515,"状态管理（vuex）","Vue 3 的 `setup` 函数与 Vuex 4 的结合，为状态管理提供了简洁且灵活的方案。Vuex 4 保持了与 Vuex 3 类似的 API，但针对 Vue 3 进行了优化。以下详细介绍 Vuex 4 在 Vue 3 `setup` 语法中的使用，并提供具体的示例说明。\n\n### 一、Vuex 的安装与基本配置\n\n#### 1. 安装 Vuex 4\n\n首先，需要在 Vue 3 项目中安装 Vuex 4：\n\n```bash\nnpm install vuex@next\n```\n\n#### 2. 创建 Vuex Store\n\n在项目中创建一个 `store\u002Findex.js` 文件，用于配置 Vuex Store：\n\n```js\nimport { createStore } from 'vuex';\n\nexport default createStore({\n  state: {\n    count: 0\n  },\n  mutations: {\n    increment(state) {\n      state.count++;\n    },\n    setCount(state, payload) {\n      state.count = payload;\n    }\n  },\n  actions: {\n    incrementAsync({ commit }) {\n      setTimeout(() => {\n        commit('increment');\n      }, 1000);\n    }\n  },\n  getters: {\n    doubleCount(state) {\n      return state.count * 2;\n    }\n  }\n});\n```\n\n### 二、在 Vue 3 中使用 Vuex\n\n#### 1. 在 `main.js` 中注册 Vuex\n\n在 `main.js` 中引入并注册 Vuex：\n\n```js\nimport { createApp } from 'vue';\nimport App from '.\u002FApp.vue';\nimport store from '.\u002Fstore'; \u002F\u002F 引入 Vuex store\n\nconst app = createApp(App);\napp.use(store); \u002F\u002F 使用 Vuex\napp.mount('#app');\n```\n\n#### 2. 在 `setup` 中使用 Vuex\n\n在 `setup` 中，Vuex Store 可以通过 `useStore` 函数直接访问。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前计数：{{ count }}\u003C\u002Fh1>\n    \u003Ch2>双倍计数：{{ doubleCount }}\u003C\u002Fh2>\n    \u003Cbutton @click=\"increment\">增加计数\u003C\u002Fbutton>\n    \u003Cbutton @click=\"incrementAsync\">异步增加计数\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useStore } from 'vuex';\n\nexport default defineComponent({\n  setup() {\n    const store = useStore(); \u002F\u002F 获取 store 实例\n    const count = store.state.count; \u002F\u002F 访问 state\n    const doubleCount = store.getters.doubleCount; \u002F\u002F 访问 getter\n\n    const increment = () => {\n      store.commit('increment'); \u002F\u002F 同步触发 mutation\n    };\n\n    const incrementAsync = () => {\n      store.dispatch('incrementAsync'); \u002F\u002F 异步触发 action\n    };\n\n    return {\n      count,\n      doubleCount,\n      increment,\n      incrementAsync\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 三、详细示例说明\n\n#### 1. State 和 Getters 的使用\n\nVuex 中的 `state` 用来存储应用的全局状态。在 `setup` 函数中，可以通过 `store.state` 来访问状态，同时可以通过 `store.getters` 来访问处理过的状态。\n\n**示例：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前数量：{{ count }}\u003C\u002Fh1>\n    \u003Ch2>双倍数量：{{ doubleCount }}\u003C\u002Fh2>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useStore } from 'vuex';\n\nexport default defineComponent({\n  setup() {\n    const store = useStore();\n    const count = store.state.count; \u002F\u002F 访问全局状态\n    const doubleCount = store.getters.doubleCount; \u002F\u002F 访问计算属性（getter）\n\n    return {\n      count,\n      doubleCount\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 2. Mutations 和 Actions 的使用\n\n`mutations` 是 Vuex 中唯一能改变状态的方式，而 `actions` 则用于处理异步逻辑。在 `setup` 中，使用 `store.commit` 触发 `mutations`，使用 `store.dispatch` 触发 `actions`。\n\n**示例：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前数量：{{ count }}\u003C\u002Fh1>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n    \u003Cbutton @click=\"incrementAsync\">异步增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useStore } from 'vuex';\n\nexport default defineComponent({\n  setup() {\n    const store = useStore();\n\n    const increment = () => {\n      store.commit('increment'); \u002F\u002F 同步触发 mutation\n    };\n\n    const incrementAsync = () => {\n      store.dispatch('incrementAsync'); \u002F\u002F 异步触发 action\n    };\n\n    return {\n      count: store.state.count,\n      increment,\n      incrementAsync\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 四、Vuex 模块化\n\n在大型应用中，状态管理的复杂性会随之增加，Vuex 提供了模块化的方式来组织代码。\n\n#### 1. 模块化 Vuex Store\n\n在 `store\u002Findex.js` 中，可以按模块组织 `state`、`mutations`、`actions` 和 `getters`：\n\n```js\nimport { createStore } from 'vuex';\n\nconst userModule = {\n  namespaced: true, \u002F\u002F 开启命名空间\n  state: {\n    username: 'admin'\n  },\n  mutations: {\n    setUsername(state, username) {\n      state.username = username;\n    }\n  },\n  actions: {\n    updateUsername({ commit }, username) {\n      commit('setUsername', username);\n    }\n  },\n  getters: {\n    userInitials(state) {\n      return state.username.charAt(0).toUpperCase();\n    }\n  }\n};\n\nconst store = createStore({\n  modules: {\n    user: userModule \u002F\u002F 注册模块\n  }\n});\n\nexport default store;\n```\n\n#### 2. 在组件中使用模块化 Store\n\n如果模块开启了命名空间，可以通过模块名访问状态和方法。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>用户名：{{ username }}\u003C\u002Fh1>\n    \u003Ch2>用户首字母：{{ userInitials }}\u003C\u002Fh2>\n    \u003Cinput v-model=\"newUsername\" placeholder=\"输入新用户名\" \u002F>\n    \u003Cbutton @click=\"updateUsername\">更新用户名\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent, ref } from 'vue';\nimport { useStore } from 'vuex';\n\nexport default defineComponent({\n  setup() {\n    const store = useStore();\n    const newUsername = ref('');\n\n    const updateUsername = () => {\n      store.dispatch('user\u002FupdateUsername', newUsername.value); \u002F\u002F 使用模块命名空间\n    };\n\n    return {\n      username: store.state.user.username,\n      userInitials: store.getters['user\u002FuserInitials'], \u002F\u002F 使用命名空间的 getter\n      newUsername,\n      updateUsername\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 五、Vuex 4 与 `setup` 的使用总结\n\n1. **State 和 Getters：** 通过 `store.state` 和 `store.getters` 访问全局状态和计算属性。\n2. **Mutations 和 Actions：** 使用 `store.commit` 触发同步 `mutations`，使用 `store.dispatch` 触发异步 `actions`。\n3. **模块化管理：** 使用模块化管理状态，通过 `namespaced` 实现命名空间，方便状态的划分和管理。\n4. **Vuex 与 `setup` 的结合：** 通过 `useStore` 在 `setup` 函数中访问 Vuex，保持代码简洁。\n\n### 六、Vuex 4 在实际场景中的应用\n\nVuex 适用于需要在不同组件之间共享状态的场景，尤其是在大型项目中，可以通过 Vuex 实现全局状态的管理，如用户认证状态、应用配置、购物车、权限控制等。通过 Vuex 的模块化和命名空间，可以将不同的业务逻辑模块化，增强代码的可维护性和可扩展性。\n",{"id":95,"created_at":96,"updated_at":96,"code":97,"author_id":7,"book_id":4,"slug":98,"article_id":99,"title":100,"sort_order":95,"content":101,"status":12},103,"2024-09-17T02:29:42.557+08:00","r2XrnYXO","状态管理pinia-103",516,"状态管理（Pinia）","Vue 3 的 `setup` 函数与 Pinia 是非常理想的搭档，Pinia 是 Vue 的新一代状态管理库，相比 Vuex，更加轻量和直观。Pinia 支持完整的 TypeScript 类型推断，并且它充分利用了 Vue 3 的 Composition API 特性。\n\n下面将详细介绍 Pinia 的状态管理，并提供详细示例说明。\n\n### 一、Pinia 的安装与基本配置\n\n#### 1. 安装 Pinia\n\n首先，在你的 Vue 3 项目中安装 Pinia：\n\n```bash\nnpm install pinia\n```\n\n#### 2. 创建 Pinia 实例\n\n在 `main.js` 中创建 Pinia 实例，并将其注册到 Vue 应用中：\n\n```js\nimport { createApp } from 'vue';\nimport { createPinia } from 'pinia';\nimport App from '.\u002FApp.vue';\n\nconst app = createApp(App);\nconst pinia = createPinia(); \u002F\u002F 创建 Pinia 实例\n\napp.use(pinia); \u002F\u002F 使用 Pinia\napp.mount('#app');\n```\n\n### 二、Pinia Store 的使用\n\nPinia 的 `store` 概念类似于 Vuex，但它更加简洁。下面是如何使用 Pinia 创建和使用 `store` 的详细步骤。\n\n#### 1. 创建 Store\n\n创建一个 `store` 文件夹，然后在其中创建 `counterStore.js` 文件，定义一个 `counter` 状态管理。\n\n```js\nimport { defineStore } from 'pinia';\n\n\u002F\u002F 定义 store\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0\n  }),\n  actions: {\n    increment() {\n      this.count++;\n    },\n    decrement() {\n      this.count--;\n    }\n  },\n  getters: {\n    doubleCount: (state) => state.count * 2\n  }\n});\n```\n\n- `state`：用于定义存储的数据。\n- `actions`：用于定义可以操作 `state` 的方法，支持同步和异步操作。\n- `getters`：用于计算衍生的状态。\n\n#### 2. 在组件中使用 Pinia Store\n\n在组件中，通过 `useCounterStore` 来访问和操作 `store`。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>计数：{{ count }}\u003C\u002Fh1>\n    \u003Ch2>双倍计数：{{ doubleCount }}\u003C\u002Fh2>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n    \u003Cbutton @click=\"decrement\">减少\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useCounterStore } from '..\u002Fstores\u002FcounterStore';\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore(); \u002F\u002F 使用 store\n\n    return {\n      count: counterStore.count,\n      doubleCount: counterStore.doubleCount,\n      increment: counterStore.increment,\n      decrement: counterStore.decrement\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n在 `setup` 函数中，通过 `useCounterStore` 获取到 `store` 实例，然后使用它的 `state`、`getters` 和 `actions`。\n\n### 三、详细示例说明\n\n#### 1. 访问和修改状态（State）\n\nPinia 的状态管理与 Vuex 类似，但在使用上更为简洁。在组件中直接使用 `store.state` 或通过解构 `store` 来访问 `state`，并可以通过 `actions` 来改变状态。\n\n**示例：**\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前计数：{{ count }}\u003C\u002Fh1>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useCounterStore } from '..\u002Fstores\u002FcounterStore';\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore();\n\n    return {\n      count: counterStore.count,\n      increment: counterStore.increment\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 2. 使用计算属性（Getters）\n\nPinia 的 `getters` 提供类似于计算属性的功能，可以通过计算当前状态来生成衍生数据。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前计数：{{ count }}\u003C\u002Fh1>\n    \u003Ch2>双倍计数：{{ doubleCount }}\u003C\u002Fh2>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useCounterStore } from '..\u002Fstores\u002FcounterStore';\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore();\n\n    return {\n      count: counterStore.count,\n      doubleCount: counterStore.doubleCount \u002F\u002F 使用 getters\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n#### 3. 异步 Actions\n\nPinia 的 `actions` 不仅可以处理同步逻辑，还可以处理异步操作。\n\n**示例：**\n\n```js\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0\n  }),\n  actions: {\n    async incrementAsync() {\n      await new Promise((resolve) => setTimeout(resolve, 1000));\n      this.count++;\n    }\n  }\n});\n```\n\n在组件中调用异步的 `action`：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>当前计数：{{ count }}\u003C\u002Fh1>\n    \u003Cbutton @click=\"incrementAsync\">异步增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent } from 'vue';\nimport { useCounterStore } from '..\u002Fstores\u002FcounterStore';\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore();\n\n    return {\n      count: counterStore.count,\n      incrementAsync: counterStore.incrementAsync\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 四、模块化 Store\n\nPinia 的模块化非常简单，类似于 Vuex 的模块化。在实际项目中，我们可以将 `store` 分割为多个模块，保持代码的清晰和维护性。\n\n**示例：用户 Store**\n\n```js\n\u002F\u002F stores\u002FuserStore.js\nimport { defineStore } from 'pinia';\n\nexport const useUserStore = defineStore('user', {\n  state: () => ({\n    name: 'John Doe',\n    age: 25\n  }),\n  actions: {\n    setName(newName) {\n      this.name = newName;\n    }\n  },\n  getters: {\n    capitalizedUserName(state) {\n      return state.name.toUpperCase();\n    }\n  }\n});\n```\n\n在组件中使用：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>用户姓名：{{ name }}\u003C\u002Fh1>\n    \u003Ch2>用户姓名（大写）：{{ capitalizedUserName }}\u003C\u002Fh2>\n    \u003Cinput v-model=\"newName\" placeholder=\"输入新姓名\" \u002F>\n    \u003Cbutton @click=\"setName(newName)\">更新姓名\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { defineComponent, ref } from 'vue';\nimport { useUserStore } from '..\u002Fstores\u002FuserStore';\n\nexport default defineComponent({\n  setup() {\n    const userStore = useUserStore();\n    const newName = ref('');\n\n    return {\n      name: userStore.name,\n      capitalizedUserName: userStore.capitalizedUserName,\n      newName,\n      setName: userStore.setName\n    };\n  }\n});\n\u003C\u002Fscript>\n```\n\n### 五、Pinia 与 Vuex 的比较\n\n1. **更轻量**：Pinia 的 API 更加简洁，它只提供了最核心的功能。\n2. **TypeScript 支持更好**：Pinia 内置了更好的类型推导支持。\n3. **Vue 3 特性支持**：Pinia 完全基于 Vue 3 的 Composition API 构建，结合 `setup` 使用非常自然。\n\n### 六、总结\n\n- **State 管理**：通过 `useStore` 函数可以方便地在组件中访问和管理状态。\n- **Getters**：类似于计算属性，用于计算衍生状态。\n- **Actions**：处理业务逻辑、修改状态，支持异步操作。\n- **模块化**：支持将 `store` 按照不同的业务模块划分，方便管理。\n\nPinia 提供了 Vue 3 项目中更加现代化、轻量且强大的状态管理方案，是 Vuex 的一个优秀替代品。\n",{"id":103,"created_at":104,"updated_at":104,"code":105,"author_id":7,"book_id":4,"slug":106,"article_id":107,"title":108,"sort_order":103,"content":109,"status":12},104,"2024-09-17T02:40:46.085+08:00","zJXM3Y6w","数据请求axios-与-vue-104",517,"数据请求（Axios 与 Vue）","在 Vue 3 `setup` 下使用 `axios` 时，可以将 `axios` 独立于 Vue 组件，放置在单独的文件或目录中，统一处理 API 请求。这样可以提高代码的可维护性和复用性。接下来，我们将详细介绍如何将 `axios` 独立出来进行全局配置、请求拦截器和响应拦截器的使用、同步与异步请求的处理，以及如何进行全局异常捕捉和处理。\n\n### 一、项目结构\n\n首先，我们为 `axios` 创建一个独立的文件夹，将 `axios` 的配置和 API 请求逻辑封装起来：\n\n```\n├── src\n│   ├── api\n│   │   ├── axiosConfig.js   # axios配置文件\n│   │   ├── userApi.js       # 用户相关API请求\n│   │   ├── postApi.js       # 文章相关API请求\n│   ├── components\n│   ├── views\n│   ├── App.vue\n│   └── main.js\n```\n\n### 二、`axiosConfig.js`：axios 全局配置与拦截器\n\n首先，我们在 `api\u002FaxiosConfig.js` 中统一配置 `axios`。这包括设置基础 URL、超时、请求拦截器、响应拦截器以及全局异常处理。\n\n```js\n\u002F\u002F api\u002FaxiosConfig.js\nimport axios from 'axios';\n\n\u002F\u002F 创建 axios 实例\nconst service = axios.create({\n  baseURL: 'https:\u002F\u002Fjsonplaceholder.typicode.com', \u002F\u002F 基础URL\n  timeout: 10000, \u002F\u002F 请求超时时间\n});\n\n\u002F\u002F 请求拦截器\nservice.interceptors.request.use(\n  (config) => {\n    \u002F\u002F 在请求之前，可以在这里加入 token 或其他认证信息\n    const token = localStorage.getItem('token');\n    if (token) {\n      config.headers['Authorization'] = `Bearer ${token}`;\n    }\n    return config;\n  },\n  (error) => {\n    \u002F\u002F 请求错误处理\n    console.error('请求拦截错误: ', error);\n    return Promise.reject(error);\n  }\n);\n\n\u002F\u002F 响应拦截器\nservice.interceptors.response.use(\n  (response) => {\n    \u002F\u002F 对响应数据做一些处理\n    return response.data;\n  },\n  (error) => {\n    \u002F\u002F 统一的错误处理\n    if (error.response) {\n      switch (error.response.status) {\n        case 401:\n          alert('未授权，请登录');\n          window.location.href = '\u002Flogin'; \u002F\u002F 跳转到登录页面\n          break;\n        case 404:\n          alert('请求地址未找到');\n          break;\n        case 500:\n          alert('服务器错误，请稍后重试');\n          break;\n        default:\n          alert(`发生错误：${error.message}`);\n      }\n    }\n    return Promise.reject(error);\n  }\n);\n\nexport default service;\n```\n\n### 三、封装 API 请求\n\n我们可以在 `api\u002F` 目录下创建多个文件来处理不同模块的 API 请求，示例如下：\n\n#### 1. 用户相关 API (`userApi.js`)\n\n```js\n\u002F\u002F api\u002FuserApi.js\nimport axios from '.\u002FaxiosConfig';\n\n\u002F\u002F 获取用户列表\nexport function getUsers() {\n  return axios.get('\u002Fusers');\n}\n\n\u002F\u002F 获取单个用户\nexport function getUserById(id) {\n  return axios.get(`\u002Fusers\u002F${id}`);\n}\n```\n\n#### 2. 文章相关 API (`postApi.js`)\n\n```js\n\u002F\u002F api\u002FpostApi.js\nimport axios from '.\u002FaxiosConfig';\n\n\u002F\u002F 获取文章列表\nexport function getPosts() {\n  return axios.get('\u002Fposts');\n}\n\n\u002F\u002F 获取单篇文章\nexport function getPostById(id) {\n  return axios.get(`\u002Fposts\u002F${id}`);\n}\n```\n\n### 四、在组件中使用封装的 API\n\n接下来，我们在 Vue 组件中使用这些封装好的 API。以用户数据的展示为例：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>用户列表\u003C\u002Fh1>\n    \u003Cul>\n      \u003Cli v-for=\"user in users\" :key=\"user.id\">{{ user.name }}\u003C\u002Fli>\n    \u003C\u002Ful>\n    \u003Cp v-if=\"error\">{{ error }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { ref, onMounted } from 'vue';\nimport { getUsers } from '..\u002Fapi\u002FuserApi'; \u002F\u002F 导入封装的API\n\nexport default {\n  setup() {\n    const users = ref([]);\n    const error = ref(null);\n\n    \u002F\u002F 在组件挂载时调用 API 获取用户列表\n    onMounted(async () => {\n      try {\n        users.value = await getUsers();\n      } catch (err) {\n        error.value = '获取用户列表失败';\n        console.error(err);\n      }\n    });\n\n    return { users, error };\n  }\n};\n\u003C\u002Fscript>\n```\n\n### 五、同步与异步请求的处理\n\n`axios` 默认是异步的，我们可以通过 `async\u002Fawait` 来处理异步请求，也可以在需要时处理同步请求。\n\n#### 1. 异步请求\n\n异步请求使用 `async\u002Fawait` 处理，代码更加简洁。\n\n```js\nasync function fetchData() {\n  try {\n    const response = await axios.get('\u002Fdata');\n    console.log('数据：', response);\n  } catch (error) {\n    console.error('请求失败：', error);\n  }\n}\n```\n\n#### 2. 同步请求\n\n虽然 `axios` 本质上是异步的，但我们可以通过 `async\u002Fawait` 实现同步请求的效果，即等待请求完成后再执行后续操作：\n\n```js\nasync function fetchDataSync() {\n  try {\n    const response = await axios.get('\u002Fdata');\n    console.log('同步数据：', response);\n  } catch (error) {\n    console.error('请求失败：', error);\n  }\n}\n```\n\n### 六、全局异常捕捉与处理\n\n为了在全局范围内捕捉异常，我们已经在 `axiosConfig.js` 中通过响应拦截器统一处理了错误。例如，遇到 401 未授权错误时，自动跳转到登录页面。\n\n可以在具体 API 请求的调用处进一步处理异常，确保所有请求的错误都能得到合适的处理：\n\n```js\nasync function fetchData() {\n  try {\n    const response = await getPosts();\n    console.log('文章数据：', response);\n  } catch (error) {\n    console.error('获取文章数据失败：', error);\n    \u002F\u002F 可以根据具体错误类型弹出不同提示\n    alert('获取数据失败，请稍后重试');\n  }\n}\n```\n\n### 七、总结\n\n- **全局配置**：将 `axios` 的基础配置和拦截器统一放在 `axiosConfig.js` 中，便于管理和维护。\n- **拦截器**：请求拦截器可用于添加 Token 等信息，响应拦截器可用于处理统一的错误逻辑。\n- **API 模块化**：将不同业务逻辑的 API 封装在不同的文件中，提高代码的可读性和复用性。\n- **同步与异步**：通过 `async\u002Fawait` 简化异步请求的代码结构。\n- **全局异常捕捉**：通过拦截器统一处理响应错误，确保异常不会导致应用崩溃。\n\n这种将 `axios` 独立封装的方式不仅可以提高代码复用性，还能方便对请求进行全局化管理，有助于大型项目的开发。\n",{"id":111,"created_at":112,"updated_at":112,"code":113,"author_id":7,"book_id":4,"slug":114,"article_id":115,"title":116,"sort_order":111,"content":117,"status":12},125,"2024-09-27T14:27:15.222+08:00","jMKeenKz","vue-devtools-125",538,"Vue DevTools","在 Vue 3 中，性能优化与调试是确保应用高效运行的重要环节。使用 Vue DevTools 可以帮助开发者更好地理解应用的状态，发现性能瓶颈，并进行调试。以下是关于 Vue 3 性能优化与调试的详细说明，尤其是如何使用 Vue DevTools 进行相关操作。\n\n### 1. Vue DevTools 安装\n\n首先，确保你已经安装了 Vue DevTools。可以从以下链接下载并安装：\n\n- [Vue DevTools Chrome 扩展](https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fvuejs-devtools)\n- [Vue DevTools Firefox 扩展](https:\u002F\u002Faddons.mozilla.org\u002Fen-US\u002Ffirefox\u002Faddon\u002Fvue-js-devtools\u002F)\n\n### 2. 使用 Vue DevTools\n\n#### 2.1 组件检查\n\n打开 Vue DevTools 后，你会看到以下几个主要面板：\n\n- **组件面板**：显示 Vue 应用中的所有组件及其层级结构。你可以查看每个组件的 props、data、computed、methods 和 watchers。\n- **Vuex 面板**：如果使用 Vuex，你可以在这个面板中查看状态、状态变更以及时间旅行调试功能。\n- **路由面板**：显示 Vue Router 的状态，包括当前路由和导航历史。\n\n在组件面板中，你可以：\n\n- 实时查看和修改组件的状态。\n- 观察组件的渲染情况，以找出性能瓶颈。\n\n#### 2.2 性能分析\n\nVue DevTools 提供了性能分析工具，可以帮助你识别性能问题：\n\n- **性能面板**：在这里，你可以记录组件的渲染性能，并查看每个组件的渲染时间、更新次数等信息。\n\n  1. 在 DevTools 中切换到“性能”面板。\n  2. 点击“开始记录”按钮，进行一些交互以生成性能记录。\n  3. 停止记录后，你可以查看性能快照，分析每个组件的渲染性能。\n\n#### 2.3 响应式系统调试\n\nVue 3 使用 Proxy 实现响应式系统。通过 Vue DevTools，你可以：\n\n- 查看响应式数据的变化情况。\n- 识别何时发生了重渲染，并了解触发重渲染的原因。\n\n### 3. 性能优化策略\n\n在使用 Vue 3 开发应用时，可以通过以下策略进行性能优化：\n\n#### 3.1 懒加载和异步组件\n\n使用 Vue 的异步组件特性，可以在需要时才加载组件，从而减少初始加载时间。\n\n```javascript\nconst AsyncComponent = defineAsyncComponent(() => import('.\u002FMyComponent.vue'));\n```\n\n#### 3.2 计算属性和侦听器\n\n利用计算属性和侦听器来优化数据处理逻辑，避免不必要的计算和渲染。\n\n#### 3.3 减少不必要的重渲染\n\n使用 `v-once` 或 `v-memo` 来缓存不需要频繁更新的部分，避免不必要的重渲染。\n\n#### 3.4 适当使用 `keep-alive`\n\n在路由切换时，使用 `keep-alive` 组件来缓存不需要重新渲染的组件状态。\n\n```html\n\u003Crouter-view v-slot=\"{ Component }\">\n  \u003Ckeep-alive>\n    \u003Ccomponent :is=\"Component\" \u002F>\n  \u003C\u002Fkeep-alive>\n\u003C\u002Frouter-view>\n```\n\n#### 3.5 使用事件节流和防抖\n\n对于频繁触发的事件（如滚动、输入等），使用节流和防抖技术来减少事件处理的次数。\n\n#### 3.6 代码分割和动态导入\n\n使用 Webpack 或 Vite 的代码分割功能，按需加载模块，从而减少初始加载的体积。\n\n### 4. 总结\n\n使用 Vue DevTools 是调试和优化 Vue 3 应用的重要手段。通过组件检查、性能分析和响应式系统调试，可以有效识别和解决性能瓶颈。同时，合理运用懒加载、计算属性、事件处理等优化策略，可以提升应用的整体性能。\n",{"id":119,"created_at":120,"updated_at":120,"code":121,"author_id":7,"book_id":4,"slug":122,"article_id":123,"title":124,"sort_order":119,"content":125,"status":12},126,"2024-09-27T14:28:05.456+08:00","0V6Aj082","vue-3-的性能提升-126",539,"Vue 3 的性能提升","Vue 3 在性能方面进行了多项优化，主要体现在虚拟 DOM、编译器优化、Tree Shaking 等方面。以下是对这些优化的详细介绍及示例代码。\n\n### 1. 虚拟 DOM 的优化\n\nVue 3 对虚拟 DOM 实现进行了重构，优化了性能：\n\n- **静态节点提升**：Vue 3 在渲染过程中，会将静态节点提取到渲染函数外部，避免重复渲染，提高性能。\n\n#### 示例\n\n```javascript\nconst { createApp } = Vue;\n\nconst App = {\n  render() {\n    return this.staticValue ? this.staticNode : this.dynamicNode;\n  },\n  data() {\n    return {\n      staticValue: true,\n      staticNode: '静态内容',\n      dynamicNode: '动态内容',\n    };\n  },\n  mounted() {\n    setTimeout(() => {\n      this.staticValue = false; \u002F\u002F 切换状态\n    }, 2000);\n  }\n};\n\ncreateApp(App).mount('#app');\n```\n\n### 2. 编译器优化\n\nVue 3 引入了新的编译器，提供了更高效的运行时性能：\n\n- **更小的运行时包**：Vue 3 的编译器在构建阶段就优化了模板，生成的代码更小。\n- **更快的渲染**：通过静态提升和静态树提升，减少了虚拟 DOM 的比较次数。\n\n#### 示例\n\n```javascript\n\u002F\u002F 使用 v-for 优化\nconst ListComponent = {\n  props: ['items'],\n  template: `\n    \u003Cul>\n      \u003Cli v-for=\"item in items\" :key=\"item.id\">{{ item.text }}\u003C\u002Fli>\n    \u003C\u002Ful>\n  `,\n};\n```\n\n### 3. Tree Shaking\n\nVue 3 支持 Tree Shaking，这意味着未使用的代码不会被打包进最终的输出文件中。这种优化可以显著减小应用的体积。\n\n#### 示例\n\n在使用 Vue 3 时，如果只需要某些功能，比如 `ref` 和 `computed`，可以仅导入这些功能：\n\n```javascript\nimport { createApp, ref, computed } from 'vue';\n\nconst App = {\n  setup() {\n    const count = ref(0);\n    const doubleCount = computed(() => count.value * 2);\n  \n    return { count, doubleCount };\n  },\n  template: `\n    \u003Cdiv>\n      \u003Cp>Count: {{ count }}\u003C\u002Fp>\n      \u003Cp>Double Count: {{ doubleCount }}\u003C\u002Fp>\n      \u003Cbutton @click=\"count++\">Increase\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  `,\n};\n\ncreateApp(App).mount('#app');\n```\n\n### 4. 渲染性能的增强\n\nVue 3 通过对重新渲染的优化，减少了不必要的 DOM 更新：\n\n- **组件的异步更新**：Vue 3 支持异步组件的渲染，提升渲染性能。\n\n#### 示例\n\n```javascript\nconst AsyncComponent = {\n  template: `\u003Cdiv>异步加载的组件\u003C\u002Fdiv>`\n};\n\nconst App = {\n  components: { AsyncComponent },\n  template: `\u003CAsyncComponent v-if=\"loadComponent\" \u002F> \u003Cbutton @click=\"loadComponent = !loadComponent\">切换组件\u003C\u002Fbutton`,\n  data() {\n    return { loadComponent: false };\n  }\n};\n\ncreateApp(App).mount('#app');\n```\n\n### 5. 总结\n\nVue 3 在虚拟 DOM 的重构、编译器优化和 Tree Shaking 等方面做了许多性能提升，这些改进使得 Vue 3 在运行效率和包体积上都有了显著提升。开发者可以利用这些新特性来构建更加高效的应用。通过示例代码，您可以看到如何使用这些优化特性来提升应用性能。\n",{"id":127,"created_at":128,"updated_at":128,"code":129,"author_id":7,"book_id":4,"slug":130,"article_id":131,"title":132,"sort_order":127,"content":133,"status":12},127,"2024-09-27T14:28:49.502+08:00","MN8aO0K2","创建一个简单的单页应用spa-127",540,"创建一个简单的单页应用（SPA）","下面是创建一个简单的单页应用（SPA）的步骤，结合 Vue 3 的核心特性，包括 Vue Router、Pinia（状态管理）和 Axios（HTTP 请求）。\n\n### 1. 项目初始化\n\n首先，使用 Vue CLI 创建一个新的 Vue 3 项目：\n\n```bash\nnpm install -g @vue\u002Fcli\nvue create vue-spa\ncd vue-spa\n```\n\n选择 Vue 3 配置。\n\n### 2. 安装依赖\n\n安装 Vue Router、Pinia 和 Axios：\n\n```bash\nnpm install vue-router pinia axios\n```\n\n### 3. 创建基本文件结构\n\n在 `src` 目录下，创建以下文件结构：\n\n```\nsrc\u002F\n├── api\u002F\n│   └── api.js\n├── components\u002F\n│   ├── Home.vue\n│   └── About.vue\n├── store\u002F\n│   └── store.js\n├── router\u002F\n│   └── index.js\n└── App.vue\n```\n\n### 4. 设置 Axios\n\n在 `src\u002Fapi\u002Fapi.js` 中配置 Axios：\n\n```javascript\nimport axios from 'axios';\n\nconst api = axios.create({\n  baseURL: 'https:\u002F\u002Fjsonplaceholder.typicode.com', \u002F\u002F 示例 API\n  timeout: 5000,\n});\n\nexport default api;\n```\n\n### 5. 配置 Pinia\n\n在 `src\u002Fstore\u002Fstore.js` 中配置 Pinia：\n\n```javascript\nimport { defineStore } from 'pinia';\nimport api from '..\u002Fapi\u002Fapi';\n\nexport const useMainStore = defineStore('main', {\n  state: () => ({\n    posts: [],\n  }),\n  actions: {\n    async fetchPosts() {\n      const response = await api.get('\u002Fposts');\n      this.posts = response.data;\n    },\n  },\n});\n```\n\n### 6. 配置 Vue Router\n\n在 `src\u002Frouter\u002Findex.js` 中配置路由：\n\n```javascript\nimport { createRouter, createWebHistory } from 'vue-router';\nimport Home from '..\u002Fcomponents\u002FHome.vue';\nimport About from '..\u002Fcomponents\u002FAbout.vue';\n\nconst routes = [\n  { path: '\u002F', component: Home },\n  { path: '\u002Fabout', component: About },\n];\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n\nexport default router;\n```\n\n### 7. 创建组件\n\n在 `src\u002Fcomponents\u002FHome.vue` 中创建主页组件：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>首页\u003C\u002Fh1>\n    \u003Cbutton @click=\"loadPosts\">加载帖子\u003C\u002Fbutton>\n    \u003Cul>\n      \u003Cli v-for=\"post in posts\" :key=\"post.id\">{{ post.title }}\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from 'vue';\nimport { useMainStore } from '..\u002Fstore\u002Fstore';\n\nconst store = useMainStore();\nconst posts = ref([]);\n\nconst loadPosts = async () => {\n  await store.fetchPosts();\n  posts.value = store.posts;\n};\n\u003C\u002Fscript>\n```\n\n在 `src\u002Fcomponents\u002FAbout.vue` 中创建关于页组件：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>关于页面\u003C\u002Fh1>\n    \u003Cp>这是一个简单的单页应用。\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n### 8. 更新 App.vue\n\n在 `src\u002FApp.vue` 中设置导航和路由视图：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv id=\"app\">\n    \u003Cnav>\n      \u003Crouter-link to=\"\u002F\">首页\u003C\u002Frouter-link>\n      \u003Crouter-link to=\"\u002Fabout\">关于\u003C\u002Frouter-link>\n    \u003C\u002Fnav>\n    \u003Crouter-view \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n### 9. 主入口文件\n\n在 `src\u002Fmain.js` 中配置 Pinia 和 Router：\n\n```javascript\nimport { createApp } from 'vue';\nimport App from '.\u002FApp.vue';\nimport router from '.\u002Frouter';\nimport { createPinia } from 'pinia';\n\nconst app = createApp(App);\napp.use(router);\napp.use(createPinia());\napp.mount('#app');\n```\n\n### 10. 启动应用\n\n最后，启动应用：\n\n```bash\nnpm run serve\n```\n\n### 11. 总结\n\n现在，你可以访问 `http:\u002F\u002Flocalhost:8080`，看到一个简单的单页应用，其中包含 Vue Router 实现的页面切换、Pinia 进行状态管理和 Axios 进行数据请求的功能。点击“加载帖子”按钮后，会从 API 加载帖子数据并在主页显示。你可以根据需要进一步扩展和优化此应用。\n",{"id":135,"created_at":136,"updated_at":136,"code":137,"author_id":7,"book_id":4,"slug":138,"article_id":139,"title":140,"sort_order":135,"content":141,"status":12},128,"2024-09-27T14:31:48.939+08:00","E784xY8Z","项目中的性能优化-128",541,"项目中的性能优化","在 Vue 3 项目中，性能优化是非常重要的，特别是在处理大型应用时。以下是关于如何使用异步组件、懒加载以及 Webpack 或 Vite 进行代码分割和打包优化的详细说明。\n\n### 1. 异步组件与懒加载\n\n异步组件允许你按需加载组件，从而减少初始加载时间。\n\n#### 示例\n\n在 Vue 3 中，可以通过 `defineAsyncComponent` 函数来定义异步组件：\n\n```javascript\n\u002F\u002F 在某个组件文件中\nimport { defineAsyncComponent } from 'vue';\n\nconst AsyncComponent = defineAsyncComponent(() =>\n  import('.\u002Fcomponents\u002FSomeLargeComponent.vue')\n);\n```\n\n在模板中使用这个异步组件：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>首页\u003C\u002Fh1>\n    \u003CAsyncComponent \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n### 2. 使用 Webpack 进行代码分割\n\n如果你使用 Webpack 作为构建工具，可以利用其动态导入功能进行代码分割。通过使用 `import()` 函数，Webpack 会自动生成一个新的代码块。\n\n#### 示例\n\n```javascript\n\u002F\u002F 在路由配置中使用异步组件\nconst routes = [\n  {\n    path: '\u002Fabout',\n    component: () => import(\u002F* webpackChunkName: \"about\" *\u002F '.\u002Fcomponents\u002FAbout.vue'),\n  },\n];\n```\n\n使用 `webpackChunkName` 注释可以自定义生成的 chunk 名称，有助于管理生成的文件。\n\n### 3. 使用 Vite 进行代码分割\n\nVite 是一个更快的构建工具，使用 Rollup 进行打包，支持动态导入和代码分割。你可以像在 Webpack 中一样使用动态导入。\n\n#### 示例\n\n```javascript\n\u002F\u002F 在路由配置中使用异步组件\nconst routes = [\n  {\n    path: '\u002Fabout',\n    component: () => import('.\u002Fcomponents\u002FAbout.vue'),\n  },\n];\n```\n\nVite 会自动处理代码分割，无需额外配置。\n\n### 4. 打包优化\n\n#### 4.1 代码压缩\n\n确保在生产环境下启用代码压缩。Webpack 和 Vite 默认会在生产模式下使用 Terser 进行压缩。\n\n```bash\n# 在 Vite 中，使用 `vite build` 进行打包时自动压缩\nnpm run build\n```\n\n#### 4.2 Tree Shaking\n\n使用 ES6 模块可以帮助实现 Tree Shaking，确保未使用的代码不会被打包。确保在项目中使用 ES6 的 `import` 和 `export` 语法。\n\n#### 4.3 按需加载第三方库\n\n如果使用大型第三方库，如 Element Plus 或 Ant Design Vue，确保只导入需要的组件以减小打包体积。\n\n```javascript\n\u002F\u002F 在 Vue 3 中按需引入 Element Plus 组件\nimport { ElButton } from 'element-plus';\n```\n\n### 5. 运行时性能\n\n除了代码分割，还可以通过以下方式提升运行时性能：\n\n- **懒加载路由**：结合 Vue Router 使用懒加载路由配置，以减少初始加载的资源。\n- **使用计算属性和侦听器**：避免不必要的计算，优化数据处理。\n- **组件缓存**：使用 `\u003Ckeep-alive>` 缓存不需要频繁更新的组件状态。\n\n### 6. 总结\n\n通过使用异步组件、懒加载、代码分割以及各种打包优化策略，可以显著提升 Vue 3 应用的性能。结合 Webpack 或 Vite 进行构建时的自动优化，将大大提高用户体验和应用响应速度。\n",{"id":143,"created_at":144,"updated_at":144,"code":145,"author_id":7,"book_id":4,"slug":146,"article_id":147,"title":148,"sort_order":143,"content":149,"status":12},129,"2024-09-29T12:41:15.779+08:00","xvXpMpKP","单元测试和端到端测试-129",542,"单元测试和端到端测试","在 Vue 3 应用中，进行单元测试和端到端测试是确保应用稳定性和可靠性的重要步骤。下面将详细讲述如何使用 Vue Test Utils 进行组件单元测试，并提供具体的测试示例。\n\n### 1. 安装必要的依赖\n\n首先，确保你已经安装了 Vue Test Utils 和测试运行器（如 Jest）：\n\n```bash\nnpm install --save-dev @vue\u002Ftest-utils jest\n```\n\n### 2. 配置 Jest\n\n在项目根目录下创建一个 `jest.config.js` 文件进行 Jest 配置：\n\n```javascript\nmodule.exports = {\n  moduleFileExtensions: ['js', 'json', 'vue'],\n  transform: {\n    '^.+\\\\.vue$': 'vue-jest',\n    '^.+\\\\.js$': 'babel-jest',\n  },\n  testEnvironment: 'jsdom',\n};\n```\n\n### 3. 创建组件\n\n假设我们有一个简单的 Vue 组件 `Counter.vue`，用于计数。\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>{{ count }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">增加\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from 'vue';\n\nconst count = ref(0);\n\nconst increment = () => {\n  count.value++;\n};\n\u003C\u002Fscript>\n```\n\n### 4. 编写单元测试\n\n在 `__tests__` 目录下创建一个测试文件 `Counter.spec.js`：\n\n```javascript\nimport { mount } from '@vue\u002Ftest-utils';\nimport Counter from '..\u002Fcomponents\u002FCounter.vue';\n\ndescribe('Counter.vue', () => {\n  it('渲染初始计数', () => {\n    const wrapper = mount(Counter);\n    expect(wrapper.text()).toContain('0'); \u002F\u002F 验证初始值\n  });\n\n  it('点击按钮后计数增加', async () => {\n    const wrapper = mount(Counter);\n    const button = wrapper.find('button');\n  \n    await button.trigger('click'); \u002F\u002F 模拟点击\n    expect(wrapper.text()).toContain('1'); \u002F\u002F 验证计数是否增加\n  });\n});\n```\n\n### 5. 运行测试\n\n可以通过以下命令运行测试：\n\n```bash\nnpx jest\n```\n\n### 6. 端到端测试\n\n对于端到端测试，通常会使用 Cypress。首先，安装 Cypress：\n\n```bash\nnpm install --save-dev cypress\n```\n\n### 7. 创建端到端测试\n\n在 `cypress\u002Fintegration` 目录下创建一个测试文件 `counter.spec.js`：\n\n```javascript\ndescribe('Counter App', () => {\n  it('应该增加计数', () => {\n    cy.visit('http:\u002F\u002Flocalhost:8080'); \u002F\u002F 修改为你的应用 URL\n    cy.contains('0'); \u002F\u002F 验证初始值\n    cy.get('button').click(); \u002F\u002F 点击按钮\n    cy.contains('1'); \u002F\u002F 验证计数增加\n  });\n});\n```\n\n### 8. 运行 Cypress\n\n可以通过以下命令启动 Cypress：\n\n```bash\nnpx cypress open\n```\n\n### 9. 总结\n\n通过以上步骤，你可以使用 Vue Test Utils 进行组件单元测试，并使用 Cypress 进行端到端测试。单元测试确保组件的功能和逻辑正确，而端到端测试则验证整个应用在用户交互下的行为。结合这两种测试方法，可以有效提高 Vue 3 应用的可靠性和稳定性。\n",{"id":151,"created_at":152,"updated_at":152,"code":153,"author_id":7,"book_id":4,"slug":154,"article_id":155,"title":156,"sort_order":151,"content":157,"status":12},130,"2024-09-29T12:43:15.309+08:00","bY8jAN6M","项目打包和部署-130",543,"项目打包和部署","以下是关于如何使用 Vite 或 Webpack 打包项目，并将其部署到服务器或静态托管平台（如 Vercel、Netlify）的详细说明。\n\n### 1. 使用 Vite 打包项目\n\n#### 1.1 打包项目\n\n在 Vite 项目中，使用以下命令进行生产打包：\n\n```bash\nnpm run build\n```\n\n这将生成一个 `dist` 目录，包含所有打包后的静态文件。\n\n#### 1.2 部署到 Vercel\n\n1. **注册并登录 Vercel**：\n\n   - 前往 [Vercel](https:\u002F\u002Fvercel.com\u002F) 注册并登录。\n2. **创建新项目**：\n\n   - 点击 \"New Project\"。\n   - 选择你的 GitHub、GitLab 或 Bitbucket 账号，连接你的代码库。\n3. **选择项目**：\n\n   - 选择你刚刚创建的 Vite 项目。\n4. **配置构建设置**：\n\n   - Vercel 会自动检测到 Vite 项目，通常构建命令为 `npm run build`，输出目录为 `dist`。\n5. **部署**：\n\n   - 点击 \"Deploy\" 按钮，Vercel 会开始构建和部署你的项目。\n6. **查看部署结果**：\n\n   - 部署完成后，你将获得一个链接，可以访问你的网站。\n\n### 2. 使用 Webpack 打包项目\n\n#### 2.1 打包项目\n\n在 Webpack 项目中，使用以下命令进行生产打包：\n\n```bash\nnpm run build\n```\n\n这将生成一个 `dist` 目录，包含所有打包后的静态文件。\n\n#### 2.2 部署到 Netlify\n\n1. **注册并登录 Netlify**：\n\n   - 前往 [Netlify](https:\u002F\u002Fwww.netlify.com\u002F) 注册并登录。\n2. **创建新站点**：\n\n   - 点击 \"New site from Git\"。\n3. **选择 Git 提供商**：\n\n   - 选择你的 GitHub、GitLab 或 Bitbucket 账号，连接你的代码库。\n4. **选择项目**：\n\n   - 选择你刚刚创建的 Webpack 项目。\n5. **配置构建设置**：\n\n   - 输入构建命令 `npm run build`，输出目录为 `dist`。\n6. **部署**：\n\n   - 点击 \"Deploy site\" 按钮，Netlify 会开始构建和部署你的项目。\n7. **查看部署结果**：\n\n   - 部署完成后，你将获得一个链接，可以访问你的网站。\n\n### 3. 手动部署到服务器\n\n如果你希望手动将项目部署到自托管的服务器，可以通过以下步骤进行：\n\n#### 3.1 打包项目\n\n与上述方法相同，使用 `npm run build` 命令打包项目。\n\n#### 3.2 上传到服务器\n\n使用 FTP、SCP 或其他工具将 `dist` 目录下的文件上传到服务器的相应目录。\n\n```bash\nscp -r dist\u002F* username@your-server-ip:\u002Fpath\u002Fto\u002Fyour\u002Fwebserver\n```\n\n#### 3.3 配置 Web 服务器\n\n确保你的 Web 服务器（如 Nginx、Apache）已配置为服务这些静态文件。\n\n**示例 Nginx 配置**：\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain.com;\n\n    location \u002F {\n        root \u002Fpath\u002Fto\u002Fyour\u002Fwebserver;\n        index index.html;\n        try_files $uri $uri\u002F \u002Findex.html;\n    }\n}\n```\n\n### 4. 总结\n\n使用 Vite 或 Webpack 打包项目后，可以轻松将其部署到 Vercel、Netlify 等静态托管平台，或者通过手动上传到自托管服务器。选择合适的部署方式可以根据项目需求和团队的工作流程进行调整。这样就可以让你的 Vue 3 应用上线，并供用户访问了！\n",{"id":159,"created_at":160,"updated_at":160,"code":161,"author_id":7,"book_id":4,"slug":162,"article_id":163,"title":164,"sort_order":159,"content":165,"status":12},131,"2024-09-29T12:53:04.581+08:00","xD819G8g","服务端渲染ssr-131",544,"服务端渲染（SSR）","服务端渲染（SSR）可以显著提高网站的 SEO 和首屏加载速度。使用 Nuxt.js 或 Vue 3 的 SSR 功能，可以方便地生成静态页面。以下是关于如何使用 Nuxt.js 进行 SSR 和静态页面生成的详细介绍。\n\n### 1. 使用 Nuxt.js 创建项目\n\n#### 1.1 安装 Nuxt.js\n\n使用 Nuxt.js 创建一个新的项目：\n\n```bash\nnpx create-nuxt-app my-nuxt-app\n```\n\n按照提示选择配置选项，例如使用 Vue 3、Axios、CSS 框架等。\n\n#### 1.2 启动开发服务器\n\n```bash\ncd my-nuxt-app\nnpm run dev\n```\n\n你可以在 `http:\u002F\u002Flocalhost:3000` 访问你的应用。\n\n### 2. SSR 和静态页面生成\n\n#### 2.1 配置 SSR\n\n在 `nuxt.config.js` 文件中，确保 `ssr` 属性设置为 `true`（默认情况下，Nuxt.js 会启用 SSR）：\n\n```javascript\nexport default {\n  ssr: true,\n};\n```\n\n#### 2.2 页面路由和组件\n\n在 `pages` 目录下创建页面组件，Nuxt.js 会自动生成路由。例如，创建一个 `index.vue` 文件：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>欢迎来到我的 Nuxt 应用\u003C\u002Fh1>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  asyncData() {\n    \u002F\u002F 可以在这里进行数据请求\n    return {};\n  },\n};\n\u003C\u002Fscript>\n```\n\n#### 2.3 数据获取\n\n使用 `asyncData` 方法，可以在服务端请求数据并在渲染页面前将数据注入到组件中。例如：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>{{ post.title }}\u003C\u002Fh1>\n    \u003Cp>{{ post.body }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  async asyncData({ $axios }) {\n    const { data } = await $axios.get('https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fposts\u002F1');\n    return { post: data };\n  },\n};\n\u003C\u002Fscript>\n```\n\n### 3. 生成静态页面\n\n#### 3.1 配置静态生成\n\n如果希望生成静态页面，可以在 `nuxt.config.js` 中配置 `target` 为 `static`：\n\n```javascript\nexport default {\n  target: 'static', \u002F\u002F 或 'server'\n};\n```\n\n#### 3.2 生成静态页面\n\n运行以下命令生成静态文件：\n\n```bash\nnpm run generate\n```\n\n生成的静态文件会被放在 `dist` 目录中，可以直接部署到静态托管服务。\n\n### 4. 部署\n\n#### 4.1 部署到 Vercel 或 Netlify\n\n静态文件可以直接部署到 Vercel 或 Netlify。你只需将 `dist` 目录上传到这些平台，或者通过 Git 进行自动部署。\n\n### 5. 提升 SEO 和首屏加载速度\n\n- **SEO 优化**：由于页面在服务端渲染，搜索引擎可以轻松抓取内容，从而提高 SEO 排名。\n- **首屏加载速度**：用户在加载页面时，可以立即看到内容，而不必等待 JavaScript 执行完成，提升用户体验。\n\n### 6. 总结\n\n通过使用 Nuxt.js 进行服务端渲染和静态页面生成，可以显著提高应用的 SEO 和首屏加载速度。其强大的数据获取和路由功能，使得构建高效的 Vue 应用变得更加简单。\n",{"id":167,"created_at":168,"updated_at":168,"code":169,"author_id":7,"book_id":4,"slug":170,"article_id":171,"title":172,"sort_order":167,"content":173,"status":12},132,"2024-09-29T12:54:01.791+08:00","JR8xRBKZ","使用-typescript-132",545,"使用 TypeScript","在 Vue 3 中结合 TypeScript，可以显著提升代码的类型安全性和可维护性。下面是如何在 Vue 3 项目中使用 TypeScript 的详细步骤和示例。\n\n### 1. 创建 TypeScript Vue 3 项目\n\n使用 Vue CLI 创建一个支持 TypeScript 的 Vue 3 项目：\n\n```bash\nnpm install -g @vue\u002Fcli\nvue create my-vue-ts-app\n```\n\n在创建过程中，选择 \"Manually select features\"，然后勾选 \"TypeScript\"。\n\n### 2. TypeScript 基本配置\n\n在项目创建完成后，查看 `tsconfig.json` 文件，确保基本配置合理。以下是一个示例配置：\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@\u002F*\": [\"src\u002F*\"]\n    }\n  },\n  \"include\": [\"src\u002F**\u002F*.ts\", \"src\u002F**\u002F*.d.ts\", \"src\u002F**\u002F*.tsx\", \"src\u002F**\u002F*.vue\"],\n  \"exclude\": [\"node_modules\"]\n}\n```\n\n### 3. 创建 TypeScript 组件\n\n在 `src\u002Fcomponents` 目录下创建一个 TypeScript 组件 `HelloWorld.vue`：\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>{{ message }}\u003C\u002Fh1>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript lang=\"ts\" setup>\nimport { ref } from 'vue';\n\nconst message: string = 'Hello, TypeScript with Vue 3!';\n\u003C\u002Fscript>\n```\n\n### 4. 使用 Props 和 Emit\n\n在 TypeScript 中定义组件的 Props 和 Emit 事件可以增强类型安全性。\n\n#### 4.1 定义 Props\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1>{{ title }}\u003C\u002Fh1>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript lang=\"ts\" setup>\nimport { defineProps } from 'vue';\n\nconst props = defineProps\u003C{\n  title: string;\n}>();\n\n\u002F\u002F 现在 title 的类型为 string\n\u003C\u002Fscript>\n```\n\n#### 4.2 定义 Emit 事件\n\n```vue\n\u003Ctemplate>\n  \u003Cbutton @click=\"handleClick\">点击我\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript lang=\"ts\" setup>\nimport { defineEmits } from 'vue';\n\nconst emit = defineEmits\u003C{\n  (e: 'click'): void;\n}>();\n\nconst handleClick = () => {\n  emit('click');\n};\n\u003C\u002Fscript>\n```\n\n### 5. Vue Router 和 TypeScript\n\n在使用 Vue Router 时，可以通过 TypeScript 定义路由类型。\n\n#### 5.1 路由类型定义\n\n```typescript\nimport { createRouter, createWebHistory } from 'vue-router';\nimport Home from '@\u002Fviews\u002FHome.vue';\n\nconst routes = [\n  {\n    path: '\u002F',\n    name: 'Home',\n    component: Home,\n  },\n];\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n\nexport default router;\n```\n\n### 6. Vuex 和 TypeScript\n\n使用 Vuex 时，也可以使用 TypeScript 提高类型安全。\n\n#### 6.1 Vuex Store 定义\n\n```typescript\nimport { createStore } from 'vuex';\n\ninterface State {\n  count: number;\n}\n\nconst store = createStore\u003CState>({\n  state: {\n    count: 0,\n  },\n  mutations: {\n    increment(state) {\n      state.count++;\n    },\n  },\n  actions: {\n    increment({ commit }) {\n      commit('increment');\n    },\n  },\n});\n\nexport default store;\n```\n\n### 7. 类型声明文件\n\n对于一些外部库或自定义模块，可以创建 `.d.ts` 文件来定义类型。\n\n#### 7.1 创建声明文件\n\n在 `src` 目录下创建 `shims-vue.d.ts` 文件：\n\n```typescript\ndeclare module '*.vue' {\n  import { DefineComponent } from 'vue';\n  const component: DefineComponent\u003C{}, {}, any>;\n  export default component;\n}\n```\n\n### 8. 总结\n\n通过结合 TypeScript 和 Vue 3，可以提升代码的类型安全性和可维护性。使用 TypeScript 的类型定义、Props、Emit 以及 Vuex 和 Vue Router 的类型增强，能够有效减少运行时错误，提高开发效率。这使得团队协作和代码维护变得更加轻松。\n",{"id":87,"created_at":88,"updated_at":88,"code":89,"author_id":7,"book_id":4,"slug":90,"article_id":91,"title":92,"sort_order":87,"content":93,"status":12}]