[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-config":3,"article-vue基础和vue2学习笔记":22,"comments-vue基础和vue2学习笔记":50,"footer-socials":51},{"site_title":4,"site_subtitle":5,"home_intro":6,"avatar":7,"seo_keywords":8,"seo_description":9,"site_subtitle_highlight":10,"home_description":11,"about_name":12,"about_intro":13,"code_comment_2":14,"code_log":15,"code_skills":16,"code_goal":17,"code_comment_1":18,"meteor_density":19,"meteor_max_count":19,"meteor_enabled":20,"meteor_speed":21},"ShineGoldYao","架构代码，","全栈开发者 \u002F 开源爱好者 \u002F 技术探索者","https:\u002F\u002Fforuda.gitee.com\u002Favatar\u002F1762402862010015318\u002F16382196_yaoxingjin_1762402861.png!avatar200","ShiGoldYao,技术博客,全栈开发","专注于前沿技术分享与开源项目展示的个人技术博客","书写未来。","大家好，我是 ShiGoldYao。一名全栈学习与技术爱好者。在这里，我分享关于现代 Web 开发、技术框架学习记录以及极客生活的深度思考。","ShiGoldYao","我是一名全栈技术学习者，对构建高性能、可扩展的现代 Web 应用充满热情。过去一年里，我从初识互联网前后端开发，到逐步沉淀技术体系，始终保持着对前端、后端与工程化的持续探索。\n\n我坚信 “代码如诗”。除了日常学习与项目实践，我也会花大量时间关注开源社区，尝试摸索 WebAssembly、Rust 等前沿技术在浏览器端的更多可能，不断挑战性能与体验的边界。\n\n生活里的我并不只有代码：闲暇时会打打永劫无间，享受博弈与操作的快感；也喜欢打乒乓球，在运动中放松自己；当然，最幸福的时光，还是和女朋友一起慢慢生活、认真恋爱。","\u002F\u002F 🚀 开启学习之旅","","Vue , TypeScript , Nest , Mysql","成为优秀的前端全栈工程师","\u002F\u002F 欢迎来到我的技术世界","3","true","5",{"id":23,"title":24,"slug":25,"coverUrl":15,"summary":15,"content":26,"htmlContent":27,"categoryId":28,"viewCount":29,"likeCount":30,"isTop":31,"isPublish":30,"seoKeywords":15,"seoDescription":15,"publishTime":32,"createTime":33,"updateTime":34,"deleteTime":35,"category":36,"tags":42},"8","Vue基础和Vue2学习笔记","vue基础和vue2学习笔记","# Vue基础和Vue2学习笔记\n\n## 目录\n1. [Vue简介](#vue简介)\n2. [Vue基础语法](#vue基础语法)\n3. [Vue2核心概念](#vue2核心概念)\n4. [Vue2 vs Vue3 主要差异](#vue2-vs-vue3-主要差异)\n5. [Vue2常用指令](#vue2常用指令)\n6. [Vue2生命周期](#vue2生命周期)\n7. [Vue2组件通信](#vue2组件通信)\n8. [Vue2路由](#vue2路由)\n9. [Vue2状态管理](#vue2状态管理)\n\n---\n\n## Vue简介\n\n### 什么是Vue\n- Vue (读音 \u002Fvjuː\u002F，类似于 view) 是一套用于构建用户界面的渐进式框架\n- 与其它大型框架不同的是，Vue 被设计为可以自底向上逐层应用\n- Vue 的核心库只关注视图层，易于上手，便于与第三方库或既有项目整合\n\n### Vue特点\n1. **渐进式框架**：可以逐步采用，不必一次性全部使用\n2. **响应式数据绑定**：数据变化自动更新视图\n3. **组件化开发**：可复用的组件系统\n4. **虚拟DOM**：提高渲染性能\n5. **指令系统**：简化DOM操作\n\n---\n\n## Vue基础语法\n\n### 1. Vue实例创建\n\n```javascript\n\u002F\u002F Vue2 方式\nnew Vue({\n  el: '#app',\n  data: {\n    message: 'Hello Vue!'\n  }\n})\n\n\u002F\u002F Vue3 方式（对比）\nconst { createApp } = Vue\ncreateApp({\n  data() {\n    return {\n      message: 'Hello Vue3!'\n    }\n  }\n}).mount('#app')\n```\n\n### 2. 模板语法\n\n#### 插值\n```html\n\u003C!-- 文本插值 -->\n\u003Cspan>Message: {{ message }}\u003C\u002Fspan>\n\n\u003C!-- 使用 v-once 指令，只执行一次插值 -->\n\u003Cspan v-once>{{ message }}\u003C\u002Fspan>\n\n\u003C!-- HTML 插值（注意XSS风险） -->\n\u003Cdiv v-html=\"rawHtml\">\u003C\u002Fdiv>\n```\n\n#### 属性绑定\n```html\n\u003C!-- v-bind 缩写为 : -->\n\u003Cdiv v-bind:id=\"dynamicId\">\u003C\u002Fdiv>\n\u003Cdiv :id=\"dynamicId\">\u003C\u002Fdiv>\n\n\u003C!-- 动态属性名 -->\n\u003Cbutton :[key]=\"value\">\u003C\u002Fbutton>\n```\n\n#### 事件绑定\n```html\n\u003C!-- v-on 缩写为 @ -->\n\u003Cbutton v-on:click=\"doSomething\">点击\u003C\u002Fbutton>\n\u003Cbutton @click=\"doSomething\">点击\u003C\u002Fbutton>\n\n\u003C!-- 动态事件名 -->\n\u003Cbutton @[event]=\"doSomething\">\u003C\u002Fbutton>\n```\n\n---\n\n## Vue2核心概念\n\n### 1. 数据响应式原理\n\nVue2使用Object.defineProperty实现响应式：\n\n```javascript\n\u002F\u002F Vue2 响应式原理简化版\nfunction defineReactive(obj, key, val) {\n  const dep = new Dep()\n  Object.defineProperty(obj, key, {\n    enumerable: true,\n    configurable: true,\n    get() {\n      Dep.target && dep.addSub(Dep.target)\n      return val\n    },\n    set(newVal) {\n      if (newVal === val) return\n      val = newVal\n      dep.notify()\n    }\n  })\n}\n```\n\n**注意**：Vue2的响应式有限制\n- 无法检测对象属性的添加\u002F删除\n- 无法检测通过索引修改数组项\n- 解决方案：使用Vue.set()或Vue.delete()\n\n```javascript\n\u002F\u002F 添加响应式属性\nVue.set(vm.user, 'age', 25)\nvm.$set(vm.user, 'age', 25)\n\n\u002F\u002F 删除响应式属性\nVue.delete(vm.user, 'age')\nvm.$delete(vm.user, 'age')\n\n\u002F\u002F 数组修改\nVue.set(vm.items, indexOfItem, newValue)\nvm.items.splice(indexOfItem, 1, newValue)\n```\n\n### 2. 计算属性\n\n```javascript\nnew Vue({\n  data: {\n    firstName: 'Foo',\n    lastName: 'Bar'\n  },\n  computed: {\n    fullName: {\n      get() {\n        return this.firstName + ' ' + this.lastName\n      },\n      set(newValue) {\n        const names = newValue.split(' ')\n        this.firstName = names[0]\n        this.lastName = names[names.length - 1]\n      }\n    }\n  }\n})\n```\n\n**计算属性 vs 方法**\n- 计算属性有缓存，依赖不变不会重新计算\n- 方法每次调用都会重新执行\n\n### 3. 侦听器\n\n```javascript\nnew Vue({\n  data: {\n    question: ''\n  },\n  watch: {\n    question(newVal, oldVal) {\n      this.answer = 'Waiting for you to stop typing...'\n    },\n    deep: true,\n    immediate: true\n  }\n})\n```\n\n---\n\n## Vue2常用指令\n\n### 1. 条件渲染\n\n```html\n\u003C!-- v-if：条件性渲染，元素会被销毁和重建 -->\n\u003Cdiv v-if=\"type === 'A'\">A\u003C\u002Fdiv>\n\u003Cdiv v-else-if=\"type === 'B'\">B\u003C\u002Fdiv>\n\u003Cdiv v-else>C\u003C\u002Fdiv>\n\n\u003C!-- v-show：只是切换CSS display属性 -->\n\u003Cdiv v-show=\"ok\">显示内容\u003C\u002Fdiv>\n```\n\n**v-if vs v-show**\n- v-if 有更高的切换开销（需要销毁\u002F重建）\n- v-show 有更高的初始渲染开销（始终渲染）\n- 需要频繁切换用 v-show，条件很少改变用 v-if\n\n### 2. 列表渲染\n\n```html\n\u003C!-- 基本用法 -->\n\u003Cli v-for=\"item in items\">{{ item.message }}\u003C\u002Fli>\n\n\u003C!-- 带索引 -->\n\u003Cli v-for=\"(item, index) in items\">{{ index }} - {{ item.message }}\u003C\u002Fli>\n\n\u003C!-- 遍历对象 -->\n\u003Cdiv v-for=\"(value, key, index) in object\">\n  {{ index }}. {{ key }}: {{ value }}\n\u003C\u002Fdiv>\n\n\u003C!-- 使用 key 提高性能 -->\n\u003Cdiv v-for=\"item in items\" :key=\"item.id\">\n  {{ item.text }}\n\u003C\u002Fdiv>\n```\n\n### 3. 表单输入绑定\n\n```html\n\u003C!-- 文本 -->\n\u003Cinput v-model=\"message\">\n\n\u003C!-- 复选框 -->\n\u003Cinput type=\"checkbox\" v-model=\"checked\">\n\n\u003C!-- 多个复选框绑定到数组 -->\n\u003Cinput type=\"checkbox\" v-model=\"checkedNames\" value=\"Jack\">\n\u003Cinput type=\"checkbox\" v-model=\"checkedNames\" value=\"John\">\n\n\u003C!-- 单选按钮 -->\n\u003Cinput type=\"radio\" v-model=\"pick\" value=\"One\">\n\n\u003C!-- 选择框 -->\n\u003Cselect v-model=\"selected\">\n  \u003Coption v-for=\"option in options\" :value=\"option.value\">\n    {{ option.text }}\n  \u003C\u002Foption>\n\u003C\u002Fselect>\n\n\u003C!-- 修饰符 -->\n\u003Cinput v-model.lazy=\"msg\">    \u003C!-- change事件触发 -->\n\u003Cinput v-model.number=\"age\">   \u003C!-- 转为数字 -->\n\u003Cinput v-model.trim=\"msg\">     \u003C!-- 去除首尾空格 -->\n```\n\n### 4. 其他常用指令\n\n```html\n\u003C!-- v-text：更新元素的textContent -->\n\u003Cspan v-text=\"msg\">\u003C\u002Fspan>\n\n\u003C!-- v-cloak：保持在元素上直到关联实例结束编译 -->\n\u003Cdiv v-cloak>{{ message }}\u003C\u002Fdiv>\n\n\u003C!-- v-pre：跳过编译 -->\n\u003Cspan v-pre>{{ this will not be compiled }}\u003C\u002Fspan>\n\n\u003C!-- v-once：只渲染一次 -->\n\u003Cspan v-once>{{ message }}\u003C\u002Fspan>\n```\n\n---\n\n## Vue2生命周期\n\n```javascript\nnew Vue({\n  beforeCreate() {\n    \u002F\u002F 实例初始化之后，数据观测和事件配置之前调用\n  },\n  created() {\n    \u002F\u002F 实例创建完成，属性已绑定，但DOM还未生成\n    \u002F\u002F 常用于初始化数据\n  },\n  beforeMount() {\n    \u002F\u002F 挂载开始之前被调用\n  },\n  mounted() {\n    \u002F\u002F el被新创建的vm.$el替换，挂载成功\n    \u002F\u002F 常用于访问DOM、初始化第三方库\n  },\n  beforeUpdate() {\n    \u002F\u002F 数据更新时调用，发生在虚拟DOM重新渲染之前\n  },\n  updated() {\n    \u002F\u002F 数据更改导致的DOM重新渲染完成\n  },\n  beforeDestroy() {\n    \u002F\u002F 实例销毁之前调用\n    \u002F\u002F 常用于清理定时器、解绑事件\n  },\n  destroyed() {\n    \u002F\u002F 实例销毁后调用\n  }\n})\n```\n\n**生命周期对比 Vue2 vs Vue3**\n```javascript\n\u002F\u002F Vue2\nbeforeCreate, created, beforeMount, mounted, \nbeforeUpdate, updated, beforeDestroy, destroyed\n\n\u002F\u002F Vue3\nbeforeCreate, created, beforeMount, mounted, \nbeforeUpdate, updated, beforeUnmount, unmounted\n```\n\n---\n\n## Vue2组件通信\n\n### 1. 父子组件通信\n\n```javascript\n\u002F\u002F 父组件\n\u003Ctemplate>\n  \u003Cchild-component \n    :message=\"parentMsg\"\n    @send-msg=\"handleMsg\"\n  \u002F>\n\u003C\u002Ftemplate>\n\n\u002F\u002F 子组件\nVue.component('child-component', {\n  props: ['message'],\n  methods: {\n    sendToParent() {\n      this.$emit('send-msg', '来自子组件的消息')\n    }\n  }\n})\n```\n\n### 2. Props\n\n```javascript\n\u002F\u002F 简单数组形式\nprops: ['title', 'likes']\n\n\u002F\u002F 对象形式（推荐）\nprops: {\n  title: String,\n  likes: Number,\n  isPublished: Boolean,\n  commentIds: Array,\n  author: Object,\n  callback: Function,\n  contactsPromise: Promise\n}\n\n\u002F\u002F 带验证\nprops: {\n  propA: Number,\n  propB: [String, Number],\n  propC: {\n    type: String,\n    required: true\n  },\n  propD: {\n    type: Number,\n    default: 100\n  },\n  propE: {\n    type: Object,\n    default: function() {\n      return { message: 'hello' }\n    }\n  },\n  propF: {\n    validator: function(value) {\n      return ['success', 'warning', 'danger'].indexOf(value) !== -1\n    }\n  }\n}\n```\n\n### 3. 自定义事件\n\n```javascript\n\u002F\u002F 子组件\nthis.$emit('event-name', payload)\nthis.$emit('increment', { count: this.count })\n\n\u002F\u002F 父组件\n\u003Cchild-component @increment=\"handleIncrement\" \u002F>\n```\n\n### 4. 插槽\n\n```html\n\u003C!-- 默认插槽 -->\n\u003Cchild-component>\n  \u003Cp>这是插槽内容\u003C\u002Fp>\n\u003C\u002Fchild-component>\n\n\u003C!-- 具名插槽 -->\n\u003Cdiv class=\"container\">\n  \u003Cheader>\n    \u003Cslot name=\"header\">\u003C\u002Fslot>\n  \u003C\u002Fheader>\n  \u003Cmain>\n    \u003Cslot>\u003C\u002Fslot>\n  \u003C\u002Fmain>\n  \u003Cfooter>\n    \u003Cslot name=\"footer\">\u003C\u002Fslot>\n  \u003C\u002Ffooter>\n\u003C\u002Fdiv>\n\n\u003C!-- 作用域插槽 -->\n\u003Cchild-component>\n  \u003Ctemplate v-slot:default=\"slotProps\">\n    {{ slotProps.user.firstName }}\n  \u003C\u002Ftemplate>\n\u003C\u002Fchild-component>\n```\n\n### 5. 其他通信方式\n\n```javascript\n\u002F\u002F $refs：访问子组件\n\u003Cchild-component ref=\"child\">\u003C\u002Fchild-component>\nthis.$refs.child.childMethod()\n\n\u002F\u002F $parent \u002F $children：访问父\u002F子组件\nthis.$parent.parentMethod()\n\n\u002F\u002F provide \u002F inject：跨层级通信\n\u002F\u002F 祖先组件\nprovide() {\n  return {\n    theme: 'dark'\n  }\n}\n\n\u002F\u002F 后代组件\ninject: ['theme']\n\n\u002F\u002F Event Bus：事件总线\nconst EventBus = new Vue()\nEventBus.$emit('event', data)\nEventBus.$on('event', callback)\n```\n\n---\n\n## Vue2路由\n\n### 1. 基本配置\n\n```javascript\nimport Vue from 'vue'\nimport VueRouter from 'vue-router'\n\nVue.use(VueRouter)\n\nconst routes = [\n  { path: '\u002F', component: Home },\n  { path: '\u002Fabout', component: About },\n  { path: '\u002Fuser\u002F:id', component: User }\n]\n\nconst router = new VueRouter({\n  routes,\n  mode: 'history'  \u002F\u002F 或 'hash'\n})\n\nnew Vue({\n  router\n}).$mount('#app')\n```\n\n### 2. 路由导航\n\n```html\n\u003C!-- router-link -->\n\u003Crouter-link to=\"\u002F\">首页\u003C\u002Frouter-link>\n\u003Crouter-link :to=\"{ path: '\u002Fuser\u002F123' }\">用户\u003C\u002Frouter-link>\n\u003Crouter-link :to=\"{ name: 'user', params: { id: 123 } }\">用户\u003C\u002Frouter-link>\n\n\u003C!-- router-view -->\n\u003Crouter-view>\u003C\u002Frouter-view>\n```\n\n```javascript\n\u002F\u002F 编程式导航\nthis.$router.push('\u002F')\nthis.$router.push({ path: '\u002Fuser\u002F123' })\nthis.$router.push({ name: 'user', params: { id: 123 } })\nthis.$router.go(-1)  \u002F\u002F 后退\nthis.$router.replace('\u002F')  \u002F\u002F 替换当前路由\n```\n\n### 3. 路由传参\n\n```javascript\n\u002F\u002F params参数（路由需要配置）\n{ path: '\u002Fuser\u002F:id', component: User }\nthis.$route.params.id\n\n\u002F\u002F query参数\nthis.$route.query.name\n\n\u002F\u002F 动态路由匹配\n{ path: '\u002Fuser\u002F:id', component: User }\n{ path: '\u002Fuser\u002F:id\u002Fpost\u002F:postId', component: Post }\n```\n\n### 4. 路由守卫\n\n```javascript\n\u002F\u002F 全局前置守卫\nrouter.beforeEach((to, from, next) => {\n  if (to.meta.requiresAuth) {\n    if (isAuthenticated()) {\n      next()\n    } else {\n      next('\u002Flogin')\n    }\n  } else {\n    next()\n  }\n})\n\n\u002F\u002F 全局后置钩子\nrouter.afterEach((to, from) => {\n  \u002F\u002F ...\n})\n\n\u002F\u002F 路由独享守卫\n{\n  path: '\u002Fadmin',\n  component: Admin,\n  beforeEnter: (to, from, next) => {\n    \u002F\u002F ...\n  }\n}\n\n\u002F\u002F 组件内守卫\nbeforeRouteEnter(to, from, next) {\n  next(vm => {\n    \u002F\u002F 通过vm访问组件实例\n  })\n}\nbeforeRouteUpdate(to, from, next) { }\nbeforeRouteLeave(to, from, next) { }\n```\n\n---\n\n## Vue2状态管理\n\n### 1. Vuex基本概念\n\n```javascript\nimport Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\nconst store = new Vuex.Store({\n  state: {\n    count: 0,\n    user: null\n  },\n  mutations: {\n    increment(state) {\n      state.count++\n    },\n    setUser(state, user) {\n      state.user = user\n    }\n  },\n  actions: {\n    incrementAsync({ commit }) {\n      setTimeout(() => {\n        commit('increment')\n      }, 1000)\n    },\n    login({ commit }, credentials) {\n      return api.login(credentials).then(user => {\n        commit('setUser', user)\n      })\n    }\n  },\n  getters: {\n    doubleCount: state => state.count * 2,\n    isLoggedIn: state => !!state.user\n  }\n})\n\nnew Vue({\n  store\n})\n```\n\n### 2. 在组件中使用\n\n```javascript\n\u002F\u002F 访问state\nthis.$store.state.count\n\n\u002F\u002F 调用mutation\nthis.$store.commit('increment')\nthis.$store.commit('setUser', { name: 'John' })\n\n\u002F\u002F 分发action\nthis.$store.dispatch('incrementAsync')\nthis.$store.dispatch('login', credentials)\n\n\u002F\u002F 使用getter\nthis.$store.getters.doubleCount\n```\n\n### 3. 辅助函数\n\n```javascript\nimport { mapState, mapMutations, mapActions, mapGetters } from 'vuex'\n\nexport default {\n  computed: {\n    ...mapState(['count', 'user']),\n    ...mapState({\n      myCount: state => state.count\n    }),\n    ...mapGetters(['doubleCount', 'isLoggedIn'])\n  },\n  methods: {\n    ...mapMutations(['increment', 'setUser']),\n    ...mapActions(['incrementAsync', 'login'])\n  }\n}\n```\n\n### 4. 模块化\n\n```javascript\nconst moduleA = {\n  state: { count: 0 },\n  mutations: { increment(state) { state.count++ } },\n  actions: { incrementAsync({ commit }) { commit('increment') } },\n  getters: { doubleCount: state => state.count * 2 }\n}\n\nconst store = new Vuex.Store({\n  modules: {\n    a: moduleA\n  }\n})\n\n\u002F\u002F 访问模块state\nthis.$store.state.a.count\n\n\u002F\u002F 命名空间模块\nconst moduleB = {\n  namespaced: true,\n  state: { count: 0 },\n  mutations: { increment(state) { state.count++ } }\n}\n\n\u002F\u002F 访问命名空间模块\nthis.$store.commit('b\u002Fincrement')\n```\n\n---\n\n## Vue2 vs Vue3 主要差异\n\n### 1. 创建应用方式\n\n```javascript\n\u002F\u002F Vue2\nnew Vue({\n  el: '#app',\n  data: { message: 'Hello' }\n})\n\n\u002F\u002F Vue3\nconst { createApp } = Vue\ncreateApp({\n  data() { return { message: 'Hello' } }\n}).mount('#app')\n```\n\n### 2. 响应式系统\n\n```javascript\n\u002F\u002F Vue2 - Object.defineProperty\n\u002F\u002F 限制：无法检测对象属性添加\u002F删除、数组索引修改\n\n\u002F\u002F Vue3 - Proxy\n\u002F\u002F 优势：支持所有数据类型、性能更好\nimport { reactive, ref } from 'vue'\n\nconst state = reactive({ count: 0 })\nconst count = ref(0)\n```\n\n### 3. 组合式API\n\n```javascript\n\u002F\u002F Vue2 - Options API\nexport default {\n  data() { return { count: 0 } },\n  methods: { increment() { this.count++ } },\n  computed: { double() { return this.count * 2 } }\n}\n\n\u002F\u002F Vue3 - Composition API\nimport { ref, computed } from 'vue'\n\nexport default {\n  setup() {\n    const count = ref(0)\n    const double = computed(() => count.value * 2)\n    const increment = () => count.value++\n    \n    return { count, double, increment }\n  }\n}\n```\n\n### 4. 生命周期钩子\n\n```javascript\n\u002F\u002F Vue2\ncreated, mounted, updated, destroyed\n\n\u002F\u002F Vue3\nonBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted\n```\n\n### 5. v-model\n\n```html\n\u003C!-- Vue2 -->\n\u003Cinput v-model=\"message\">\n\n\u003C!-- Vue3 - 支持多个v-model -->\n\u003CUserName \n  v-model:first-name=\"first\"\n  v-model:last-name=\"last\"\n\u002F>\n```\n\n### 6. 过渡动画\n\n```javascript\n\u002F\u002F Vue2\n\u003Ctransition name=\"fade\">\n  \u003Cdiv v-if=\"show\">内容\u003C\u002Fdiv>\n\u003C\u002Ftransition>\n\n\u002F\u002F Vue3 - 支持多个根节点\n\u003Ctransition>\n  \u003Cdiv v-if=\"show\">内容1\u003C\u002Fdiv>\n  \u003Cdiv v-else>内容2\u003C\u002Fdiv>\n\u003C\u002Ftransition>\n```\n\n---\n\n## Vue2最佳实践\n\n### 1. 组件命名\n- 使用PascalCase命名组件\n- 组件注册时使用kebab-case\n\n```javascript\nVue.component('MyComponent', { })\n\u003Cmy-component>\u003C\u002Fmy-component>\n```\n\n### 2. 数据初始化\n- data必须是函数，返回对象\n\n```javascript\ndata() {\n  return {\n    message: 'Hello'\n  }\n}\n```\n\n### 3. Props验证\n- 始终进行props类型验证\n- 提供默认值\n\n### 4. 事件命名\n- 使用kebab-case\n- 始终使用$emit触发\n\n```javascript\nthis.$emit('update-value', newValue)\n```\n\n### 5. 样式作用域\n- 使用scoped避免样式污染\n\n```html\n\u003Cstyle scoped>\n\u002F* 组件私有样式 *\u002F\n\u003C\u002Fstyle>\n```\n\n### 6. 性能优化\n- 合理使用v-if和v-show\n- 列表渲染使用key\n- 大列表使用虚拟滚动\n- 避免不必要的响应式数据\n\n---\n\n## 学习建议\n\n1. **理解响应式原理**：Vue2的Object.defineProperty限制很重要\n2. **掌握Options API**：Vue2主要使用Options API\n3. **熟悉生命周期**：理解每个生命周期的作用\n4. **组件通信**：掌握props、$emit、$refs等通信方式\n5. **路由和状态管理**：Vue Router和Vuex是必学内容\n6. **对比Vue3**：了解差异，便于理解Vue3的改进\n\n---\n\n## 参考资源\n\n- Vue2官方文档：https:\u002F\u002Fv2.cn.vuejs.org\u002F\n- Vue Router文档：https:\u002F\u002Frouter.vuejs.org\u002Fzh\u002F\n- Vuex文档：https:\u002F\u002Fvuex.vuejs.org\u002Fzh\u002F\n",null,"1",10,1,0,"2026-04-07T16:13:44.190Z","2026-04-07T16:13:44.193Z","2026-06-11T04:41:21.863Z","0",{"id":28,"categoryName":37,"slug":38,"description":39,"sort":31,"isEnable":30,"createTime":40,"updateTime":41,"deleteTime":31},"前端开发","frontend-engineering","2222","2026-04-03T02:36:11.945Z","2026-04-07T16:38:46.496Z",[43],{"articleId":23,"tagId":28,"createTime":33,"tag":44},{"id":28,"tagName":45,"slug":46,"themeColor":47,"description":48,"createTime":49,"updateTime":49,"deleteTime":35},"Vue2","Vue2-study","#00ff44","记录学习Vue2","2026-04-03T02:41:51.845Z",[],[]]