Vue2 是一套渐进式 JavaScript 前端框架,以简洁易用、灵活高效的特点深受开发者喜爱,本手册从入门到进阶,全面覆盖 Vue2 核心知识点与实战技巧,适合零基础入门及前端开发者巩固提升。

一、环境搭建与项目创建

1. 两种使用方式

(1)快速上手:CDN 引入(无需工程化,适合快速原型开发)

直接在 HTML 文件中引入 Vue2 CDN 链接,无需额外配置,开箱即用。
html
 
预览
 
 
 
 
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Vue2 快速入门</title>
  <!-- 引入 Vue2 生产环境 CDN(体积更小) -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.min.js"></script>
</head>
<body>
  <div id="app">{{ message }}</div>

  <script>
    // 创建 Vue 实例
    new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue2!'
      }
    })
  </script>
</body>
</html>
 

(2)工程化开发:Vue CLI(推荐,适合实际项目)

Vue CLI 是 Vue 官方提供的脚手架工具,用于快速搭建标准化 Vue 项目,内置 webpack 配置、热更新、打包优化等功能。
步骤 1:安装 Node.js(前置依赖)
Vue CLI 依赖 Node.js 环境,需先安装 Node.js(推荐 v14.x/v16.x 版本,兼容 Vue2),下载地址:https://nodejs.org/zh-cn/ 安装完成后,通过命令行验证:
bash
 
运行
 
 
 
 
# 验证 Node 版本
node -v
# 验证 npm 版本(Node 自带 npm)
npm -v
 
步骤 2:安装 Vue CLI 3.x/4.x(兼容 Vue2)
bash
 
运行
 
 
 
 
# 全局安装 Vue CLI
npm install -g @vue/cli
# 验证安装是否成功
vue --version
 
步骤 3:创建 Vue2 项目
bash
 
运行
 
 
 
 
# 1. 创建项目(my-vue2-project 为项目名称,可自定义)
vue create my-vue2-project

# 2. 选择项目配置
# (1)上下箭头切换选项,回车确认,选择 "Manually select features"(手动选择功能)
# (2)勾选需要的功能:Babel、Router、Vuex、CSS Pre-processors 等(空格勾选/取消)
# (3)选择 Vue 版本:选择 "2.x"
# (4)其余配置按需选择(是否使用 history 路由、CSS 预处理器类型、配置文件存放方式等)
# (5)是否保存为预设模板:按需选择(n 即可)
 
步骤 4:启动项目
bash
 
运行
 
 
 
 
# 进入项目目录
cd my-vue2-project
# 启动开发服务器
npm run serve
 
启动成功后,访问终端提示的地址(默认:http://localhost:8080),即可看到 Vue2 项目默认页面。

2. Vue2 项目目录结构解析

plaintext
 
 
 
 
 
my-vue2-project/
├── node_modules/       # 项目依赖包(npm install 生成)
├── public/             # 静态资源根目录(不会被 webpack 编译,直接复制到打包目录)
│   ├── favicon.ico     # 网站图标
│   └── index.html      # 项目入口 HTML(唯一的 HTML 文件,单页应用核心)
├── src/                # 项目源代码目录(核心开发目录)
│   ├── assets/         # 静态资源目录(会被 webpack 编译处理,如图片、样式文件)
│   ├── components/     # 全局公共组件目录(可复用组件,如 Button、Card 等)
│   ├── router/         # 路由配置目录(Vue Router 相关配置)
│   ├── store/          # 状态管理目录(Vuex 相关配置)
│   ├── views/          # 页面组件目录(对应路由页面,如 Home、About 等)
│   ├── App.vue         # 根组件(所有页面/组件的父组件)
│   └── main.js         # 项目入口 JS 文件(创建 Vue 实例,挂载根组件)
├── package.json        # 项目配置文件(依赖、脚本命令等)
├── babel.config.js     # Babel 配置文件(ES6+ 转 ES5 兼容低版本浏览器)
└── README.md           # 项目说明文档
 

二、Vue2 核心基础

1. Vue 实例

Vue2 应用的核心是 Vue 实例,所有功能都围绕实例展开。

(1)创建 Vue 实例

javascript
 
运行
 
 
 
 
// main.js 中创建根实例
import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app', // 挂载目标:对应 public/index.html 中的 #app 元素
  render: h => h(App) // 渲染根组件 App(Vue2 工程化项目推荐用法)
  // 等价于:
  // render: function(h) {
  //   return h(App)
  // }
})

// 非工程化环境(CDN)创建实例
new Vue({
  el: '#app',
  data: {},
  methods: {},
  computed: {},
  watch: {}
  // 其他选项
})
 

(2)核心实例选项

选项名 作用 说明
el 挂载目标 指定 Vue 实例挂载的 DOM 元素,值为选择器字符串(如 #app)或 DOM 对象
data 响应式数据 存储组件的状态数据,工程化组件中必须是函数(避免组件复用数据污染),非根实例(CDN)可直接是对象
methods 方法 存储组件的业务逻辑方法,可通过 this 访问 data 中的数据
computed 计算属性 基于现有数据推导的派生数据,有缓存,依赖数据变化时才重新计算
watch 侦听器 监听 data 或 computed 数据的变化,执行异步或复杂逻辑

(3)实例生命周期钩子

Vue 实例从创建到销毁的全过程称为生命周期,提供了一系列钩子函数,允许我们在不同阶段执行自定义逻辑。
钩子函数 执行时机 常用场景
beforeCreate 实例创建前(数据代理、事件监听未初始化) 极少使用,可做全局事件监听初始化(非 Vue 相关)
created 实例创建完成(数据代理、事件监听已初始化) 发起异步请求(如接口获取数据)、初始化数据
beforeMount 挂载前(模板已编译,未渲染到 DOM) 极少使用,可修改数据(不会触发额外渲染)
mounted 挂载完成(模板已渲染到真实 DOM) 操作 DOM 元素(如初始化第三方插件)、获取 DOM 尺寸
beforeUpdate 数据更新前(数据已变,DOM 未更新) 获取更新前的 DOM 状态
updated 数据更新后(DOM 已同步更新) 操作更新后的 DOM
beforeDestroy 实例销毁前(实例仍可用) 清除定时器、取消全局事件监听、销毁第三方插件实例
destroyed 实例销毁后(实例属性、方法不可用) 极少使用,做最终清理工作
示例:
vue
 
 
 
 
 
<template>
  <div id="app">{{ message }}</div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue2'
    }
  },
  beforeCreate() {
    console.log('beforeCreate:', this.message) // undefined(数据未初始化)
  },
  created() {
    console.log('created:', this.message) // Hello Vue2(数据已初始化)
    // 发起异步请求
    // this.fetchData()
  },
  mounted() {
    console.log('mounted:', document.getElementById('app').innerText) // Hello Vue2(DOM 已渲染)
  },
  beforeDestroy() {
    console.log('beforeDestroy:实例即将销毁')
    // 清除定时器
    // clearInterval(this.timer)
  }
}
</script>
 

2. 模板语法

Vue2 使用基于 HTML 的模板语法,将实例数据与 DOM 绑定,无需手动操作 DOM,核心分为插值表达式和指令两类。

(1)插值表达式(Mustache 语法:{{ }})

用于将 Vue 实例的数据渲染到 DOM 中,支持简单表达式计算。
vue
 
 
 
 
 
<template>
  <div>
    <!-- 渲染普通数据 -->
    <p>{{ name }}</p>
    <!-- 支持简单表达式(算术、三元运算等,不支持语句) -->
    <p>{{ age + 1 }}</p>
    <p>{{ isShow ? '显示' : '隐藏' }}</p>
    <!-- 渲染对象属性 -->
    <p>{{ user.username }}</p>
    <!-- 不渲染 HTML 标签(会原样输出) -->
    <p>{{ htmlStr }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: 'Vue2 学习手册',
      age: 18,
      isShow: true,
      user: { username: 'zhangsan' },
      htmlStr: '<h1>这是一段HTML</h1>'
    }
  }
}
</script>
 

(2)核心指令

指令是带有 v- 前缀的特殊 HTML 属性,用于实现数据绑定、DOM 操作、事件绑定等功能,Vue2 核心指令如下:
指令 作用 示例
v-text 渲染文本(等价于 {{ }},不会原样输出 HTML) <p v-text="name"></p>
v-html 渲染 HTML 内容(存在 XSS 风险,慎用) <p v-html="htmlStr"></p>
v-show 基于布尔值切换元素显示 / 隐藏(通过 display: none 实现,初始渲染成本低) <div v-show="isShow">显示/隐藏</div>
v-if 基于布尔值条件渲染元素(直接添加 / 删除 DOM 节点,切换成本高) <div v-if="isShow">条件渲染</div>
v-else 配合 v-if 使用,实现二选一渲染 <div v-if="isShow">A</div><div v-else>B</div>
v-else-if 配合 v-if 使用,实现多条件渲染 <div v-if="type === 'A'">A</div><div v-else-if="type === 'B'">B</div>
v-for 遍历数组 / 对象渲染列表,必须配合 key 属性(唯一标识,提升性能) <li v-for="(item, index) in list" :key="item.id">{{ index }} - {{ item.name }}</li>
v-bind 动态绑定 HTML 属性(可简写为 :) <a :href="url">链接</a>、<img :src="imgSrc" :alt="name">
v-on 绑定事件监听器(可简写为 @) <button @click="handleClick">点击</button>、<input @change="handleChange">
v-model 双向数据绑定(仅支持表单元素,实现视图与数据同步更新) <input v-model="username" type="text">、<select v-model="city"></select>
v-once 只渲染一次数据,后续数据变化不再更新 DOM <p v-once>{{ name }}</p>
v-cloak 解决插值表达式闪烁问题(配合 CSS [v-cloak] { display: none; } 使用) <p v-cloak>{{ message }}</p>
关键指令示例:
vue
 
 
 
 
 
<template>
  <div>
    <!-- v-bind 简写 -->
    <a :href="link">Vue 官网</a>
    <!-- v-on 简写 & 传递参数 -->
    <button @click="handleBtnClick('参数1')">点击按钮</button>
    <!-- v-for 遍历数组 -->
    <ul>
      <li v-for="(item, index) in fruitList" :key="item.id">
        {{ index + 1 }} - {{ item.name }}
      </li>
    </ul>
    <!-- v-model 双向绑定 -->
    <input v-model="inputValue" placeholder="请输入内容">
    <p>输入的内容:{{ inputValue }}</p>
    <!-- v-show 与 v-if 区别 -->
    <div v-show="isShow" style="background: red;">v-show 切换</div>
    <div v-if="isShow" style="background: blue;">v-if 切换</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      link: 'https://v2.vuejs.org/',
      fruitList: [
        { id: 1, name: '苹果' },
        { id: 2, name: '香蕉' },
        { id: 3, name: '橙子' }
      ],
      inputValue: '',
      isShow: true
    }
  },
  methods: {
    handleBtnClick(params) {
      console.log('按钮被点击', params)
      this.isShow = !this.isShow // 切换显示状态
    }
  }
}
</script>

<style>
/* 解决 v-cloak 闪烁问题 */
[v-cloak] {
  display: none;
}
</style>
 

3. 计算属性(computed)与侦听器(watch)

(1)计算属性(computed)

基于现有数据派生新数据,具有缓存特性:只有依赖的数据源发生变化时,才会重新计算,否则直接返回缓存结果,适合简单派生逻辑。
vue
 
 
 
 
 
<template>
  <div>
    <p>姓:<input v-model="firstName"></p>
    <p>名:<input v-model="lastName"></p>
    <p>全名:{{ fullName }}</p>
    <p>全名(方法):{{ getFullName() }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: '张',
      lastName: '三'
    }
  },
  // 计算属性
  computed: {
    // 只读计算属性(常用)
    fullName() {
      // 依赖 firstName 和 lastName,只有两者变化时,才会重新计算
      return this.firstName + this.lastName
    },
    // 可读写计算属性(极少使用)
    // fullName: {
    //   get() {
    //     return this.firstName + this.lastName
    //   },
    //   set(value) {
    //     this.firstName = value[0]
    //     this.lastName = value.slice(1)
    //   }
    // }
  },
  methods: {
    // 方法:每次调用都会重新执行,无缓存
    getFullName() {
      return this.firstName + this.lastName
    }
  }
}
</script>
 

(2)侦听器(watch)

监听 data 或 computed 数据的变化,执行异步操作或复杂业务逻辑,无缓存,数据变化即触发。
vue
 
 
 
 
 
<template>
  <div>
    <p>输入框:<input v-model="searchValue"></p>
    <p>搜索结果:{{ searchResult }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      searchValue: '',
      searchResult: ''
    }
  },
  // 侦听器
  watch: {
    // 基础监听(监听简单数据类型)
    searchValue(newVal, oldVal) {
      console.log('新值:', newVal, '旧值:', oldVal)
      // 模拟异步搜索(如接口请求)
      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        this.searchResult = `模拟搜索结果:${newVal}`
      }, 500) // 防抖:500ms 内输入不触发,避免频繁请求
    },
    // 深度监听(监听对象/数组内部属性变化)
    // user: {
    //   deep: true, // 开启深度监听
    //   handler(newVal, oldVal) {
    //     console.log('用户信息变化:', newVal)
    //   }
    // },
    // 立即执行(组件创建时立即触发一次监听)
    // searchValue: {
    //   handler(newVal) {
    //     this.searchResult = `模拟搜索结果:${newVal}`
    //   },
    //   immediate: true
    // }
  },
  beforeDestroy() {
    // 清除定时器
    clearTimeout(this.timer)
  }
}
</script>
 

(3)computed 与 watch 区别

特性 computed watch
缓存 有缓存,依赖不变不重新计算 无缓存,数据变化即触发
用途 简单数据派生(同步操作) 复杂业务逻辑、异步操作
返回值 必须有返回值 无返回值,通过修改 data 实现视图更新
写法 简洁,声明式 灵活,命令式

三、Vue2 组件系统

组件是 Vue2 的核心特性之一,用于将页面拆分为可复用、独立的功能模块,提高开发效率和代码可维护性。

1. 组件的分类与注册

(1)组件分类

(2)全局组件注册

javascript
 
运行
 
 
 
 
// main.js 中全局注册
import Vue from 'vue'
import App from './App.vue'
// 导入自定义组件
import MyButton from './components/MyButton.vue'

// 全局注册组件(第一个参数:组件名称,第二个参数:组件对象)
Vue.component('MyButton', MyButton)

new Vue({
  render: h => h(App)
}).$mount('#app')
 

(3)局部组件注册

vue
 
 
 
 
 
<!-- 父组件 App.vue -->
<template>
  <div>
    <!-- 使用局部组件 -->
    <MyCard></MyCard>
  </div>
</template>

<script>
// 导入局部组件
import MyCard from './components/MyCard.vue'

export default {
  // 注册局部组件
  components: {
    MyCard // 等价于 MyCard: MyCard(ES6 简写)
  }
}
</script>
 

(4)组件编写规范(单文件组件 .vue)

Vue2 工程化项目中,组件以 .vue 后缀的单文件形式存在,包含 <template>(模板)、<script>(逻辑)、<style>(样式)三部分。
vue
 
 
 
 
 
<!-- components/MyButton.vue -->
<template>
  <!-- 组件模板必须有且只有一个根元素 -->
  <button class="my-button" @click="handleClick">
    {{ buttonText }}
  </button>
</template>

<script>
export default {
  // 组件名称(推荐 PascalCase 命名,使用时可 kebab-case)
  name: 'MyButton',
  // 组件数据:必须是函数(避免多实例共享数据)
  data() {
    return {
      buttonText: '自定义按钮'
    }
  },
  methods: {
    handleClick() {
      console.log('按钮被点击')
      // 触发自定义事件
      this.$emit('btn-click')
    }
  }
}
</script>

<style scoped>
/* scoped:样式仅作用于当前组件,避免样式污染 */
.my-button {
  padding: 8px 16px;
  background: #42b983;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>
 

2. 组件通信

组件间通信是 Vue2 开发的核心难点,根据组件关系(父子、兄弟、跨级),对应不同的通信方案。

(1)父组件 → 子组件:props 传递

props 是子组件接收父组件数据的专用属性,支持类型校验、默认值等配置。
vue
 
 
 
 
 
<!-- 父组件 Parent.vue -->
<template>
  <div>
    <!-- 传递静态数据 -->
    <MyChild title="父组件传递的静态标题"></MyChild>
    <!-- 传递动态数据(v-bind 简写 :) -->
    <MyChild :msg="parentMsg" :user="parentUser"></MyChild>
  </div>
</template>

<script>
import MyChild from './MyChild.vue'
export default {
  components: { MyChild },
  data() {
    return {
      parentMsg: '父组件的动态消息',
      parentUser: { id: 1, name: 'zhangsan' }
    }
  }
}
</script>
 
vue
 
 
 
 
 
<!-- 子组件 MyChild.vue -->
<template>
  <div>
    <p>{{ title }}</p>
    <p>{{ msg }}</p>
    <p>{{ user.name }}</p>
  </div>
</template>

<script>
export default {
  name: 'MyChild',
  // 方式1:简单数组形式(不推荐,无校验)
  // props: ['title', 'msg', 'user']

  // 方式2:对象形式(推荐,支持类型校验、默认值、必传项)
  props: {
    title: {
      type: String, // 数据类型
      default: '默认标题' // 默认值
    },
    msg: {
      type: String,
      required: true // 必传项
    },
    user: {
      type: Object,
      default: () => ({}) // 对象/数组默认值必须是函数
    }
  }
}
</script>
 

(2)子组件 → 父组件:$emit 触发自定义事件

子组件通过 this.$emit('自定义事件名', 传递参数) 触发事件,父组件通过 @自定义事件名 监听并接收参数。
vue
 
 
 
 
 
<!-- 子组件 MyChild.vue -->
<template>
  <button @click="handleSendData">向父组件传递数据</button>
</template>

<script>
export default {
  data() {
    return {
      childData: '子组件的内部数据'
    }
  },
  methods: {
    handleSendData() {
      // 触发自定义事件,并传递参数
      this.$emit('child-send', this.childData, '额外参数')
    }
  }
}
</script>
 
vue
 
 
 
 
 
<!-- 父组件 Parent.vue -->
<template>
  <div>
    <MyChild @child-send="handleChildSend"></MyChild>
    <p>接收子组件数据:{{ childMsg }}</p>
  </div>
</template>

<script>
import MyChild from './MyChild.vue'
export default {
  components: { MyChild },
  data() {
    return {
      childMsg: ''
    }
  },
  methods: {
    // 接收子组件传递的参数
    handleChildSend(data, extraData) {
      console.log('额外参数:', extraData)
      this.childMsg = data
    }
  }
}
</script>
 

(3)兄弟组件通信:事件总线(Event Bus)

通过创建一个空的 Vue 实例作为事件总线,实现兄弟组件间的通信,适用于小型项目。
javascript
 
运行
 
 
 
 
// utils/bus.js(创建事件总线)
import Vue from 'vue'
// 导出空的 Vue 实例
export default new Vue()
 
vue
 
 
 
 
 
<!-- 兄弟组件 A:发送数据 -->
<template>
  <button @click="sendMsgToB">向兄弟组件 B 发消息</button>
</template>

<script>
import bus from '../utils/bus'
export default {
  data() {
    return {
      brotherAMsg: '来自组件 A 的消息'
    }
  },
  methods: {
    sendMsgToB() {
      // 通过事件总线触发自定义事件
      bus.$emit('brother-msg', this.brotherAMsg)
    }
  }
}
</script>
 
vue
 
 
 
 
 
<!-- 兄弟组件 B:接收数据 -->
<template>
  <p>接收组件 A 的消息:{{ msgFromA }}</p>
</template>

<script>
import bus from '../utils/bus'
export default {
  data() {
    return {
      msgFromA: ''
    }
  },
  mounted() {
    // 通过事件总线监听自定义事件
    this.busListener = bus.$on('brother-msg', (data) => {
      this.msgFromA = data
    })
  },
  beforeDestroy() {
    // 销毁事件监听,避免内存泄漏
    bus.$off('brother-msg', this.busListener)
  }
}
</script>
 

(4)跨级组件通信

3. 插槽(Slot)

插槽用于实现组件的内容分发,允许父组件向子组件插入自定义内容,提高组件的灵活性和复用性。

(1)默认插槽

子组件预留默认插槽位置,父组件可直接在子组件标签内插入内容。
vue
 
 
 
 
 
<!-- 子组件 MySlot.vue -->
<template>
  <div class="slot-container">
    <h3>子组件标题</h3>
    <!-- 默认插槽:父组件插入的内容会显示在这里 -->
    <slot></slot>
  </div>
</template>
 
vue
 
 
 
 
 
<!-- 父组件使用 -->
<template>
  <MySlot>
    <!-- 插入自定义内容 -->
    <p>这是父组件插入到默认插槽的内容</p>
  </MySlot>
</template>
 

(2)具名插槽

子组件预留多个命名插槽,父组件通过 slot 属性(Vue2.6 前)或 v-slot 指令(Vue2.6+,推荐,简写 #)指定插入的插槽位置。
vue
 
 
 
 
 
<!-- 子组件 MyNamedSlot.vue -->
<template>
  <div class="named-slot-container">
    <header>
      <!-- 具名插槽:name 为 header -->
      <slot name="header"></slot>
    </header>
    <main>
      <!-- 默认插槽(name 为 default) -->
      <slot></slot>
    </main>
    <footer>
      <!-- 具名插槽:name 为 footer -->
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
 
vue
 
 
 
 
 
<!-- 父组件使用 -->
<template>
  <MyNamedSlot>
    <!-- 具名插槽:v-slot:header 简写 #header -->
    <template #header>
      <h1>父组件插入的头部内容</h1>
    </template>

    <!-- 默认插槽:无需指定名称 -->
    <p>父组件插入的主体内容</p>

    <!-- 具名插槽:v-slot:footer 简写 #footer -->
    <template #footer>
      <p>父组件插入的底部内容</p>
    </template>
  </MyNamedSlot>
</template>
 

(3)作用域插槽

子组件向插槽传递数据,父组件在插入内容时可接收并使用这些数据(实现子组件数据向父组件插槽内容传递)。
vue
 
 
 
 
 
<!-- 子组件 MyScopeSlot.vue -->
<template>
  <div class="scope-slot-container">
    <h3>作用域插槽</h3>
    <!-- 子组件通过属性向插槽传递数据 -->
    <slot :user="user" :list="fruitList"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: { id: 1, name: 'zhangsan' },
      fruitList: ['苹果', '香蕉', '橙子']
    }
  }
}
</script>
 
vue
 
 
 
 
 
<!-- 父组件使用 -->
<template>
  <MyScopeSlot>
    <!-- 接收子组件插槽传递的数据:v-slot="slotProps" -->
    <template #default="slotProps">
      <p>用户名:{{ slotProps.user.name }}</p>
      <p>水果列表:{{ slotProps.list.join(', ') }}</p>
    </template>

    <!-- 解构赋值简化写法(推荐) -->
    <!-- <template #default="{ user, list }">
      <p>用户名:{{ user.name }}</p>
      <p>水果列表:{{ list.join(', ') }}</p>
    </template> -->
  </MyScopeSlot>
</template>
 

四、Vue Router 3.x(Vue2 配套路由)

Vue Router 是 Vue2 官方路由管理器,用于实现单页应用(SPA)的页面跳转与路由管理。

1. 安装与基本配置

(1)安装 Vue Router 3.x

bash
 
运行
 
 
 
 
# 项目内安装(Vue2 对应 Vue Router 3.x)
npm install vue-router@3 --save
 

(2)路由配置(router/index.js)

javascript
 
运行
 
 
 
 
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入页面组件
import Home from '../views/Home.vue'
import About from '../views/About.vue'

// 注册 Vue Router 插件
Vue.use(VueRouter)

// 定义路由规则
const routes = [
  // 重定向:默认访问 / 时跳转到 /home
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home', // 路由路径
    name: 'Home', // 路由名称
    component: Home // 对应页面组件
  },
  {
    path: '/about',
    name: 'About',
    component: About
  }
]

// 创建路由实例
const router = new VueRouter({
  mode: 'history', // 路由模式:history(无 # 号)/ hash(有 # 号,默认)
  base: process.env.BASE_URL, // 基础路径
  routes // 路由规则(等价于 routes: routes)
})

export default router
 

(3)挂载路由到 Vue 实例(main.js)

javascript
 
运行
 
 
 
 
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router' // 导入路由实例

new Vue({
  router, // 挂载路由(等价于 router: router)
  render: h => h(App)
}).$mount('#app')
 

(4)路由出口与导航

vue
 
 
 
 
 
<!-- App.vue(根组件) -->
<template>
  <div id="app">
    <!-- 路由导航:<router-link> 替代 a 标签,无刷新跳转 -->
    <div class="nav">
      <router-link to="/home">首页</router-link>
      <router-link to="/about">关于我们</router-link>
    </div>
    <!-- 路由出口:匹配的路由组件会渲染在这里 -->
    <router-view></router-view>
  </div>
</template>

<style>
/* 路由激活状态样式(默认类名:router-link-active) */
.router-link-active {
  color: #42b983;
  text-decoration: none;
  font-weight: bold;
}
</style>
 

2. 路由进阶用法

(1)动态路由(传递参数)

用于匹配动态变化的路由路径,如详情页 /user/1、/user/2。
javascript
 
运行
 
 
 
 
// router/index.js 新增动态路由
{
  path: '/user/:id', // :id 为动态参数(可多个:/user/:id/:name)
  name: 'User',
  component: () => import('../views/User.vue') // 懒加载组件
}
 
vue
 
 
 
 
 
<!-- User.vue 接收动态参数 -->
<template>
  <div>
    <h3>用户详情页</h3>
    <p>用户ID:{{ $route.params.id }}</p>
  </div>
</template>

<script>
export default {
  mounted() {
    // 也可通过 this.$route.params 获取动态参数
    console.log('用户ID:', this.$route.params.id)
  }
}
</script>
 

(2)路由传参的两种方式

(3)嵌套路由

用于实现页面内的层级路由,如首页下的子菜单页面。
javascript
 
运行
 
 
 
 
// router/index.js 配置嵌套路由
{
  path: '/home',
  name: 'Home',
  component: Home,
  // 嵌套路由规则(children 数组)
  children: [
    {
      path: 'news', // 子路由路径:无需加 /,访问路径为 /home/news
      name: 'HomeNews',
      component: () => import('../views/HomeNews.vue')
    },
    {
      path: 'product', // 访问路径为 /home/product
      name: 'HomeProduct',
      component: () => import('../views/HomeProduct.vue')
    }
  ]
}
 
vue
 
 
 
 
 
<!-- Home.vue 中添加子路由出口 -->
<template>
  <div>
    <h3>首页</h3>
    <!-- 子路由导航 -->
    <div class="sub-nav">
      <router-link to="/home/news">新闻</router-link>
      <router-link to="/home/product">产品</router-link>
    </div>
    <!-- 子路由出口:子路由组件渲染在这里 -->
    <router-view></router-view>
  </div>
</template>
 

(4)路由守卫

路由守卫用于控制路由的跳转权限(如登录验证、页面权限控制),分为全局守卫、路由独享守卫、组件内守卫。
全局守卫(router/index.js)
javascript
 
运行
 
 
 
 
// 全局前置守卫:路由跳转前触发
router.beforeEach((to, from, next) => {
  // to:即将进入的路由对象
  // from:即将离开的路由对象
  // next:放行函数(必须调用,否则路由无法跳转)
  console.log('全局前置守卫', to, from)

  // 登录验证:访问需要权限的页面(如 /about)时,判断是否登录
  const isLogin = localStorage.getItem('isLogin') // 假设登录状态存在 localStorage
  if (to.path === '/about' && !isLogin) {
    // 未登录,跳转到登录页
    next('/login')
  } else {
    // 已登录或访问无需权限的页面,放行
    next()
  }
})

// 全局后置守卫:路由跳转后触发(无需调用 next)
router.afterEach((to, from) => {
  console.log('全局后置守卫', to, from)
  // 如:修改页面标题
  document.title = to.name || 'Vue2 路由示例'
})
 

五、Vuex 3.x(Vue2 配套状态管理)

Vuex 是 Vue2 官方的全局状态管理库,用于统一管理组件共享的数据(如用户信息、全局配置),实现组件间高效通信。

1. Vuex 核心概念

Vuex 采用单一状态树(Single Source of Truth),核心包含 5 个部分:

2. 安装与基本配置

(1)安装 Vuex 3.x

bash
 
运行
 
 
 
 
npm install vuex@3 --save
 

(2)Vuex 配置(store/index.js)

javascript
 
运行
 
 
 
 
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 1. 全局状态
  state: {
    userInfo: null, // 用户信息
    count: 0 // 测试计数器
  },

  // 2. 派生状态(计算属性)
  getters: {
    // 获取用户名称
    userName: state => state.userInfo?.name || '',
    // 获取计数器翻倍值
    doubleCount: state => state.count * 2
  },

  // 3. 同步修改状态(唯一方式)
  mutations: {
    // 设置用户信息
    SET_USER_INFO(state, payload) {
      state.userInfo = payload
    },
    // 增加计数器
    INCREMENT(state, num = 1) {
      state.count += num
    },
    // 减少计数器
    DECREMENT(state) {
      state.count--
    }
  },

  // 4. 异步操作
  actions: {
    // 模拟异步登录(如接口请求)
    async login({ commit }, userData) {
      // 模拟接口请求延迟
      await new Promise(resolve => setTimeout(resolve, 1000))
      // 登录成功后,commit Mutation 修改 State
      commit('SET_USER_INFO', userData)
      return true // 返回登录结果
    },
    // 异步修改计数器
    async incrementAsync({ commit }, num) {
      await new Promise(resolve => setTimeout(resolve, 1000))
      commit('INCREMENT', num)
    }
  },

  // 5. 模块拆分(大型项目使用)
  modules: {
    // 示例:user 模块
    // user: {
    //   namespaced: true, // 开启命名空间(避免模块间命名冲突)
    //   state: {},
    //   getters: {},
    //   mutations: {},
    //   actions: {}
    // }
  }
})
 

(3)挂载 Vuex 到 Vue 实例(main.js)

javascript
 
运行
 
 
 
 
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' // 导入 Vuex 实例

new Vue({
  router,
  store, // 挂载 Vuex(等价于 store: store)
  render: h => h(App)
}).$mount('#app')
 

3. Vuex 使用方式

(1)获取 State 与 Getter

vue
 
 
 
 
 
<template>
  <div>
    <p>用户名称:{{ userName }}</p>
    <p>计数器:{{ count }}</p>
    <p>计数器翻倍:{{ doubleCount }}</p>
  </div>
</template>

<script>
export default {
  // 方式1:通过 this.$store 直接访问
  mounted() {
    console.log('用户信息:', this.$store.state.userInfo)
    console.log('计数器翻倍:', this.$store.getters.doubleCount)
  },

  // 方式2:通过 mapState、mapGetters 辅助函数(推荐,简化代码)
  computed: {
    // 映射 State
    ...Vuex.mapState(['count', 'userInfo']),
    // 映射 Getter
    ...Vuex.mapGetters(['userName', 'doubleCount'])
  }
}
</script>
 

(2)触发 Mutation(同步修改 State)

vue
 
 
 
 
 
<template>
  <div>
    <button @click="handleIncrement">增加计数器</button>
    <button @click="handleDecrement">减少计数器</button>
  </div>
</template>

<script>
import { mapMutations } from 'vuex' // 导入辅助函数

export default {
  methods: {
    // 方式1:通过 this.$store.commit 触发
    handleIncrement() {
      this.$store.commit('INCREMENT', 2) // 传递参数
    },
    handleDecrement() {
      this.$store.commit('DECREMENT')
    },

    // 方式2:通过 mapMutations 辅助函数
    ...mapMutations(['INCREMENT', 'DECREMENT']),
    // 调用:this.INCREMENT(2)、this.DECREMENT()
  }
}
</script>
 

(3)触发 Action(异步操作)

vue
 
 
 
 
 
<template>
  <div>
    <button @click="handleLogin">模拟登录</button>
    <button @click="handleIncrementAsync">异步增加计数器</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex' // 导入辅助函数

export default {
  methods: {
    // 方式1:通过 this.$store.dispatch 触发
    async handleLogin() {
      const res = await this.$store.dispatch('login', { name: 'zhangsan', age: 18 })
      if (res) {
        console.log('登录成功')
      }
    },
    async handleIncrementAsync() {
      await this.$store.dispatch('incrementAsync', 3)
    },

    // 方式2:通过 mapActions 辅助函数
    ...mapActions(['login', 'incrementAsync']),
    // 调用:this.login(userData)、this.incrementAsync(num)
  }
}
</script>
 

六、Vue2 高级特性

1. 自定义指令

自定义指令用于封装 DOM 操作相关的逻辑,分为全局自定义指令和局部自定义指令。
javascript
 
运行
 
 
 
 
// main.js 全局自定义指令
// 示例:v-focus(自动聚焦输入框)
Vue.directive('focus', {
  // 指令绑定到元素并插入 DOM 时触发
  inserted(el) {
    el.focus() // 操作 DOM 元素
  },
  // 指令所在组件更新时触发
  update(el) {
    el.focus()
  }
})
 
vue
 
 
 
 
 
<!-- 局部自定义指令 -->
<template>
  <div>
    <!-- 使用自定义指令 -->
    <input v-focus type="text">
    <input v-color="'red'" type="text">
  </div>
</template>

<script>
export default {
  // 局部自定义指令
  directives: {
    color: {
      // el:指令绑定的 DOM 元素
      // binding:指令相关信息(value:指令值)
      inserted(el, binding) {
        el.style.color = binding.value
      }
    }
  }
}
</script>
 

2. 混入(Mixin)

混入用于提取多个组件的公共逻辑,实现代码复用,分为全局混入和局部混入。
javascript
 
运行
 
 
 
 
// mixins/commonMixin.js(公共混入)
export default {
  data() {
    return {
      mixinMsg: '这是混入的公共数据'
    }
  },
  methods: {
    mixinMethod() {
      console.log('这是混入的公共方法')
    }
  },
  mounted() {
    console.log('混入的 mounted 钩子')
  }
}
 
vue
 
 
 
 
 
<!-- 组件中使用局部混入 -->
<template>
  <div>{{ mixinMsg }}</div>
</template>

<script>
import commonMixin from '../mixins/commonMixin'

export default {
  // 局部混入(数组形式,可多个)
  mixins: [commonMixin],
  mounted() {
    this.mixinMethod() // 调用混入的方法
    console.log('组件自身的 mounted 钩子')
  }
}
</script>
 

3. 过滤器(Filter)

过滤器用于格式化数据(如日期格式化、字符串截取),分为全局过滤器和局部过滤器,可在插值表达式或 v-bind 中使用(通过 | 管道符调用)。
javascript
 
运行
 
 
 
 
// main.js 全局过滤器
// 示例:日期格式化
Vue.filter('formatDate', (value) => {
  if (!value) return ''
  const date = new Date(value)
  return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
})
 
vue
 
 
 
 
 
<!-- 局部过滤器 -->
<template>
  <div>
    <!-- 使用全局过滤器 -->
    <p>格式化日期:{{ time | formatDate }}</p>
    <!-- 使用局部过滤器 -->
    <p>字符串大写:{{ str | toUpper }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      time: new Date(),
      str: 'vue2'
    }
  },
  // 局部过滤器
  filters: {
    toUpper(value) {
      if (!value) return ''
      return value.toUpperCase()
    }
  }
}
</script>
 

七、Vue2 项目优化与部署

1. 项目优化

2. 项目部署

八、常见问题与注意事项

  1. data 必须是函数:组件中 data 必须返回函数,避免多实例共享数据
  2. v-for 必须加 key:使用唯一标识(如 id)作为 key,避免 DOM 复用导致的问题
  3. 避免直接修改 props:子组件不能直接修改父组件传递的 props,需通过 $emit 通知父组件修改
  4. 避免在模板中使用复杂表达式:复杂逻辑应抽离到 computed 或 methods 中
  5. 及时销毁定时器 / 事件监听:在 beforeDestroy 钩子中清除定时器、取消事件监听,避免内存泄漏
  6. Vuex 状态修改:只能通过 Mutation 修改 State,避免直接修改导致状态不可追溯