Vue2 与 Vue3 超全基础知识点汇总

23 阅读10分钟

本文涵盖Vue2/Vue3 所有核心基础 API,每个知识点都配完整可运行代码 + 逐行注释,0 基础也能看懂。全文从入门到实战,对比两代 Vue 差异。

1. Vue 基础认知

1.1 核心定义

Vue 是一套用于构建用户界面的渐进式 JavaScript 框架,核心是数据驱动视图,数据变→视图自动变,无需手动操作 DOM。

1.2 Vue2 vs Vue3 核心区别

对比项Vue2Vue3
响应式原理Object.definePropertyES6 Proxy(无监听缺陷)
代码风格选项式 API(Options API)组合式 API(Composition API)+ 兼容选项式
TS 支持差,需手动配置原生 TS,类型推导完美
构建工具Vue-CLI(Webpack)Vite(极速启动)
体积全量打包,体积大按需引入,Tree-Shaking 优化
性能普通渲染性能提升 55%,内存减少 33%

2. 项目创建与目录结构

2.1 Vue2 项目创建

bash 运行

# 1. 全局安装Vue脚手架
npm install -g @vue/cli

# 2. 创建项目
vue create vue2-project

# 3. 运行项目
cd vue2-project
npm run serve

2.2 Vue3 项目创建(推荐 Vite)

bash 运行

# 1. Vite创建Vue3项目
npm create vite@latest vue3-project -- --template vue

# 2. 安装依赖
cd vue3-project
npm install

# 3. 运行项目
npm run dev

3. 入口文件 main.js 完整写法

3.1 Vue2 入口文件

javascript 运行

// 引入Vue核心库
import Vue from 'vue'
// 引入根组件
import App from './App.vue'
// 关闭生产环境提示
Vue.config.productionTip = false

// 创建Vue实例,挂载根组件到#app
new Vue({
  // 渲染函数
  render: h => h(App)
}).$mount('#app') // 挂载到public/index.html的#app节点

3.2 Vue3 入口文件

javascript 运行

// 引入createApp创建应用实例
import { createApp } from 'vue'
// 引入根组件
import App from './App.vue'
// 引入样式
import './style.css'

// 1. 创建应用实例
const app = createApp(App)
// 2. 挂载到DOM节点
app.mount('#app')

// 拓展:全局配置(Vue3无全局污染)
// app.config.globalProperties.$msg = '全局变量'

4. 模板核心语法(全指令详解)

Vue 模板语法基于 HTML,所有指令以 v- 开头,数据变视图自动更新

4.1 文本插值 {{}}

<!-- Vue2 和 Vue3 模板插值用法完全一致 -->
<template>
  <div>
    <!-- 直接渲染变量 -->
    <h1>{{ msg }}</h1>
    <!-- 渲染表达式 -->
    <p>{{ num + 1 }}</p>
    <p>{{ isShow ? '显示' : '隐藏' }}</p>
  </div>
</template>

<!-- Vue2 -->
<script>
export default {
  data() {
    return {
      msg: 'Hello Vue2',
      num: 10,
      isShow: true
    }
  }
}
</script>

<!-- Vue3 -->
<script setup>
import { ref } from 'vue'
const msg = ref('Hello Vue3')
const num = ref(10)
const isShow = ref(true)
</script>

4.2 v-html 渲染 HTML 标签

{{}} 会转义标签,v-html 可解析原生 HTML

<template>
  <div v-html="htmlStr"></div>
</template>

<!-- Vue2 -->
<script>
export default {
  data() {
    return { htmlStr: '<span style="color:red">红色文字</span>' }
  }
}
</script>

<!-- Vue3 -->
<script setup>
import { ref } from 'vue'
const htmlStr = ref('<span style="color:red">红色文字</span>')
</script>

⚠️ 安全警告:仅在信任的内容上使用 v-html,防止 XSS 攻击!

4.3 v-bind 绑定属性(简写:)

动态绑定标签属性(src、class、style、disabled 等)

<template>
  <!-- 完整写法 -->
  <img v-bind:src="imgUrl" />
  <!-- 简写: (最常用) -->
  <img :src="imgUrl" />
  <!-- 绑定class -->
  <div :class="{ active: isActive }">class绑定</div>
</template>

<!-- Vue2 -->
<script>
export default {
  data() {
    return {
      imgUrl: 'https://xxx.jpg',
      isActive: true
    }
  }
}
</script>

4.4 v-on 绑定事件(简写 @)

绑定点击、输入、鼠标等事件

<template>
  <!-- 完整写法 -->
  <button v-on:click="handleClick">点击</button>
  <!-- 简写@ (最常用) -->
  <button @click="handleClick">点击</button>
  <!-- 传参 -->
  <button @click="handleClickParams(10)">传参点击</button>
</template>

<!-- Vue2 -->
<script>
export default {
  methods: {
    handleClick() { alert('Vue2点击事件') },
    handleClickParams(num) { alert('参数:' + num) }
  }
}
</script>

4.5 v-model 双向绑定(表单专用)

表单元素(input/textarea/select)数据双向同步

vue2

<template>
  <input v-model="inputVal" placeholder="请输入" />
  <p>输入内容:{{ inputVal }}</p>
</template>

<!-- Vue2 -->
<script>
export default {
  data() { return { inputVal: '' } }
}
</script>

vue3 统一语法,支持多 v-model,废弃 .sync

<!-- 父组件 -->
<Child v-model:msg="msg" v-model:age="age" />

<!-- 子组件 -->
<script setup>
const props = defineProps(['msg', 'age'])
const emit = defineEmits(['update:msg', 'update:age'])
</script>

4.6 v-for 列表渲染(必须加 key)

循环渲染数组 / 对象,key 必须唯一,不能用 index

<template>
  <ul>
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

<!-- Vue2 -->
<script>
export default {
  data() {
    return { list: [{id:1,name:'苹果'},{id:2,name:'香蕉'}] }
  }
}
</script>

4.7 v-if /v-else/v-else-if 条件渲染

控制元素创建 / 销毁,切换开销大

<template>
  <div v-if="score >= 90">优秀</div>
  <div v-else-if="score >= 60">及格</div>
  <div v-else">不及格</div>
</template>

4.8 v-show 条件显示

控制元素显示 / 隐藏(display:none),切换开销小

<template>
  <div v-show="isShow">v-show显示内容</div>
</template>

4.9 其他指令

  • v-once:只渲染一次,后续数据更新不重新渲染
  • v-pre:跳过编译,直接显示原始内容
  • v-cloak:解决插值表达式闪烁问题

5. 响应式数据 API 全解(最核心)

响应式:数据改变 → 视图自动刷新,无需操作 DOM

5.1 Vue2 响应式 API

5.1.1 data 定义响应式数据

data 必须是函数,返回对象,防止组件复用数据污染

<script>
export default {
  // data是函数,返回对象
  data() {
    return {
      // 基本数据类型
      msg: 'Vue2',
      num: 0,
      // 引用数据类型
      user: { name: '张三', age: 18 },
      list: [1,2,3]
    }
  }
}
</script>

5.1.2 Vue2 响应式缺陷 & 修复 API

Vue2 用 Object.defineProperty无法监听数组下标修改、对象新增属性

<script>
export default {
  data() { return { user: {}, list: [1,2] } },
  mounted() {
    // 1. 对象新增属性 → 视图不更新
    this.user.name = '张三' // 无效
    // 修复:this.$set
    this.$set(this.user, 'name', '张三')

    // 2. 数组下标修改 → 视图不更新
    this.list[0] = 100 // 无效
    // 修复:this.$set
    this.$set(this.list, 0, 100)

    // 3. 删除对象属性 → 视图不更新
    // 修复:this.$delete
    this.$delete(this.user, 'name')
  }
}
</script>

5.2 Vue3 响应式 API(组合式)

Vue3 用 Proxy,无任何响应式缺陷,所有操作都能监听

5.2.1 ref 定义基本数据类型

用于:字符串、数字、布尔、null、undefined

<script setup>
// 1. 引入ref
import { ref } from 'vue'

// 2. 定义响应式数据
const msg = ref('Hello Vue3') // 字符串
const num = ref(0) // 数字
const isShow = ref(true) // 布尔

// 3. JS中修改值必须加 .value
const changeMsg = () => {
  msg.value = '修改后的Vue3'
  num.value++
}
</script>

<template>
  <!-- 模板中自动解包,无需.value -->
  <p>{{ msg }}</p>
  <p>{{ num }}</p>
  <button @click="changeMsg">修改数据</button>
</template>

5.2.2 reactive 定义引用数据类型

用于:对象、数组、Map、Set

<script setup>
import { reactive } from 'vue'

// 定义对象
const user = reactive({ name: '李四', age: 20 })
// 定义数组
const list = reactive(['苹果', '香蕉'])

// 修改数据:直接修改,无需.value
const updateUser = () => {
  user.age = 21 // 直接改
  list[0] = '葡萄' // 直接改数组下标,无缺陷
}
</script>

5.2.3 toRefs 解构 reactive 数据

reactive 解构后会丢失响应式,用 toRefs 修复

<script setup>
import { reactive, toRefs } from 'vue'
const user = reactive({ name: '王五', age: 25 })

// 错误:解构后无响应式
// const { name, age } = user

// 正确:toRefs 保持响应式
const { name, age } = toRefs(user)

// 修改数据
const changeAge = () => {
  age.value = 26
}
</script>

5.2.4 toRef 单独抽取一个属性

<script setup>
import { reactive, toRef } from 'vue'
const user = reactive({ name: '赵六' })
// 抽取单个属性
const name = toRef(user, 'name')
</script>

5.2.5 其他响应式 API

  • unref:如果是 ref 返回.value,否则返回本身
  • shallowRef:浅响应式,只监听.value 修改
  • shallowReactive:浅响应式,只监听第一层属性

6. 方法(methods)与事件处理

6.1 Vue2 定义方法

所有方法放在 methods 选项中

<template>
  <button @click="add">点击+1</button>
  <p>数字:{{ num }}</p>
</template>

<script>
export default {
  data() { return { num: 0 } },
  // 方法存放位置
  methods: {
    add() {
      // 通过this访问data数据
      this.num++
    }
  }
}
</script>

6.2 Vue3 定义方法

直接在 <script setup> 中定义函数,模板直接用

<template>
  <button @click="add">点击+1</button>
  <p>数字:{{ num }}</p>
</template>

<script setup>
import { ref } from 'vue'
const num = ref(0)

// 直接定义函数
const add = () => {
  num.value++
}
</script>

6.3 事件修饰符

<!-- 阻止冒泡 -->
<button @click.stop="handle">.stop</button>
<!-- 阻止默认行为 -->
<a @click.prevent="handle">.prevent</a>
<!-- 只触发一次 -->
<button @click.once="handle">.once</button>

7. 生命周期钩子完整对比 + 使用

生命周期:Vue 实例从创建→挂载→更新→销毁的全过程

7.1 完整生命周期对应表

Vue2 钩子Vue3 钩子执行时机
beforeCreatesetup创建前(无 this)
createdsetup创建后(可访问数据)
beforeMountonBeforeMount挂载前
mountedonMounted挂载完成(操作 DOM、发请求)
beforeUpdateonBeforeUpdate更新前
updatedonUpdated更新完成
beforeDestroyonBeforeUnmount销毁前
destroyedonUnmounted销毁完成

7.2 Vue2 生命周期使用

<script>
export default {
  data() { return { msg: 'Vue2生命周期' } },
  // 挂载完成,最常用
  mounted() {
    console.log('DOM渲染完成,可发请求')
  },
  // 销毁前
  beforeDestroy() {
    console.log('组件销毁,清除定时器')
  }
}
</script>

7.3 Vue3 生命周期使用

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const msg = ref('Vue3生命周期')

// 挂载完成
onMounted(() => {
  console.log('DOM渲染完成')
})

// 组件销毁
onUnmounted(() => {
  console.log('组件销毁')
})
</script>

8. 计算属性 computed(缓存特性)

8.1 作用

处理复杂逻辑,有缓存,依赖数据不变时不会重新计算,比 methods 性能高

8.2 Vue2 computed

<template>
  <p>全名:{{ fullName }}</p>
</template>

<script>
export default {
  data() {
    return { firstName: '张', lastName: '三' }
  },
  // 计算属性
  computed: {
    // 只读计算属性
    fullName() {
      return this.firstName + this.lastName
    },
    // 读写计算属性(get+set)
    fullName2: {
      get() { return this.firstName + this.lastName },
      set(val) {
        const arr = val.split(' ')
        this.firstName = arr[0]
        this.lastName = arr[1]
      }
    }
  }
}
</script>

8.3 Vue3 computed

<script setup>
import { ref, computed } from 'vue'
const firstName = ref('李')
const lastName = ref('四')

// 只读计算属性
const fullName = computed(() => {
  return firstName.value + lastName.value
})

// 读写计算属性
const fullName2 = computed({
  get() { return firstName.value + lastName.value },
  set(val) {
    const arr = val.split(' ')
    firstName.value = arr[0]
    lastName.value = arr[1]
  }
})
</script>

9. 侦听器 watch /watchEffect(监听数据变化)

9.1 watch 监听指定数据(Vue2)

<script>
export default {
  data() { return { num: 0, user: { age: 18 } } },
  watch: {
    // 监听基本类型
    num(newVal, oldVal) {
      console.log('新值:', newVal, '旧值:', oldVal)
    },
    // 深度监听对象(必须加deep:true)
    user: {
      handler(newVal) { console.log('user变化:', newVal) },
      deep: true,
      immediate: true // 立即执行一次
    }
  }
}
</script>

9.2 watch 监听指定数据(Vue3)

<script setup>
import { ref, reactive, watch } from 'vue'
const num = ref(0)
const user = reactive({ age: 18 })

// 监听基本类型
watch(num, (newVal, oldVal) => {
  console.log(newVal, oldVal)
})

// 深度监听对象
watch(user, (newVal) => {
  console.log(newVal)
}, { deep: true, immediate: true })
</script>

9.3 watchEffect 自动监听(Vue3 专属)

无需指定依赖,自动收集,代码更简洁

<script setup>
import { ref, watchEffect } from 'vue'
const num = ref(0)

// 只要num变化,自动执行
watchEffect(() => {
  console.log('num变化:', num.value)
})
</script>

10. 组件基础定义与使用

组件:可复用的 Vue 实例,一个组件就是一个.vue 文件

10.1 Vue2 组件使用

<!-- 父组件 App.vue -->
<template>
  <div>
    <!-- 使用子组件 -->
    <Child />
  </div>
</template>

<script>
// 1. 引入子组件
import Child from './components/Child.vue'
export default {
  // 2. 注册组件
  components: { Child }
}
</script>

<!-- 子组件 Child.vue -->
<template>
  <div>我是子组件</div>
</template>

10.2 Vue3 组件使用

<script setup>引入即注册,无需手动注册

<!-- 父组件 -->
<template>
  <Child />
</template>

<script setup>
// 引入直接用,无需注册
import Child from './components/Child.vue'
</script>

11. 组件通信 8 种方式(全场景)

11.1 父传子 props(最常用)

Vue2 父传子

<!-- 父组件 -->
<Child :msg="父组件数据" :user="user" />
<script>
import Child from './Child.vue'
export default {
  components: { Child },
  data() { return { msg: '父组件传给子组件', user: { name: '父' } } }
}
</script>

<!-- 子组件 -->
<script>
export default {
  // 接收父组件数据
  props: {
    msg: String,
    user: Object
  }
}
</script>

Vue3 父传子

<!-- 父组件 -->
<Child :msg="父组件数据" />
<script setup>
import Child from './Child.vue'
const msg = ref('父组件数据')
</script>

<!-- 子组件 -->
<script setup>
import { defineProps } from 'vue'
// 定义props,接收数据
const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
})
// 模板中直接用{{ msg }}
</script>

11.2 子传父 $emit /defineEmits

Vue2 子传父

<!-- 子组件 -->
<button @click="sendToParent">发送给父组件</button>
<script>
export default {
  methods: {
    sendToParent() {
      // 触发自定义事件,传参
      this.$emit('sendMsg', '子组件数据')
    }
  }
}
</script>

<!-- 父组件 -->
<Child @sendMsg="handleReceive" />
<script>
export default {
  methods: {
    handleReceive(val) { console.log('接收子组件数据:', val) }
  }
}
</script>

Vue3 子传父

<!-- 子组件 -->
<script setup>
import { defineEmits } from 'vue'
// 定义自定义事件
const emit = defineEmits(['sendMsg'])
const send = () => {
  emit('sendMsg', 'Vue3子组件数据')
}
</script>

<!-- 父组件 -->
<Child @sendMsg="handleReceive" />

11.3 其他通信方式

1、兄弟组件通信:Vue2 → EventBus;Vue3 → mitt / Pinia

2、跨级组件通信:provide /inject

适用场景:多层嵌套组件(如祖父→父→孙→曾孙),无需逐层透传props,由祖先组件提供数据,后代组件直接注入使用。


Vue2 vs Vue3 核心差异

特性Vue2Vue3
响应式默认非响应式,需手动用computed/ref包装原生支持响应式,直接传递ref/reactive即可
API 位置选项式 API(provide/inject选项)组合式 API(setup中用provide/inject函数)
TS 支持强(类型自动推导)

Vue2 示例

javascript 运行

// 祖先组件(App.vue)
export default {
  provide() {
    return {
      // 传递响应式数据,必须用computed包装
      appName: computed(() => this.appName),
      userInfo: { name: '张三', age: 30 }
    }
  },
  data() {
    return {
      appName: 'Vue2 应用'
    }
  }
}

// 后代组件(任意层级,如孙组件)
export default {
  inject: ['appName', 'userInfo'],
  mounted() {
    console.log('注入的appName:', this.appName.value) // computed需.value
    console.log('注入的userInfo:', this.userInfo)
  }
}

Vue3 示例(组合式 API + <script setup>

<!-- 祖先组件 App.vue -->
<script setup>
import { provide, ref, reactive } from 'vue'

// 响应式数据
const appName = ref('Vue3 应用')
const userInfo = reactive({ name: '张三', age: 30 })

// 提供数据,直接传递响应式变量
provide('appName', appName)
provide('userInfo', userInfo)
</script>

<!-- 后代组件(任意层级) -->
<script setup>
import { inject } from 'vue'

// 注入数据,第二个参数为默认值(可选)
const appName = inject('appName', '默认应用名')
const userInfo = inject('userInfo')

console.log('注入的appName:', appName.value) // ref需.value
console.log('注入的userInfo:', userInfo)
</script>

3、ref / $refs:父组件获取子组件实例

适用场景:父组件需要直接调用子组件的方法、访问子组件的 data,或操作子组件的 DOM 元素。


Vue2 vs Vue3 核心差异

表格

特性Vue2Vue3
获取实例this.$refs.child选项式同 Vue2;组合式需在onMounted后通过ref获取
<script setup>支持无此语法子组件必须用defineExpose显式暴露属性 / 方法
DOM 访问this.$refs.dom同 Vue2,组合式需ref绑定

Vue2 示例

<!-- 子组件 Child.vue -->
<template>
  <div>子组件</div>
</template>
<script>
export default {
  data() {
    return {
      childMsg: '我是子组件数据'
    }
  },
  methods: {
    childMethod() {
      console.log('子组件方法被调用')
      return '子组件返回值'
    }
  }
}
</script>

<!-- 父组件 Parent.vue -->
<template>
  <Child ref="childComp" />
  <div ref="domBox">父组件DOM</div>
</template>
<script>
import Child from './Child.vue'
export default {
  components: { Child },
  mounted() {
    // 获取子组件实例
    const childInstance = this.$refs.childComp
    console.log('子组件数据:', childInstance.childMsg)
    // 调用子组件方法
    const res = childInstance.childMethod()
    console.log('子组件方法返回值:', res)

    // 获取DOM元素
    const dom = this.$refs.domBox
    console.log('DOM元素:', dom)
  }
}
</script>

Vue3 示例(<script setup>

<!-- 子组件 Child.vue -->
<template>
  <div>子组件</div>
</template>
<script setup>
import { ref } from 'vue'
const childMsg = ref('我是子组件数据')
const childMethod = () => {
  console.log('子组件方法被调用')
  return '子组件返回值'
}

// 【关键】<script setup>默认闭包,必须显式暴露给父组件!
defineExpose({
  childMsg,
  childMethod
})
</script>

<!-- 父组件 Parent.vue -->
<template>
  <Child ref="childComp" />
  <div ref="domBox">父组件DOM</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'

// 声明ref,绑定到子组件/DOM
const childComp = ref(null)
const domBox = ref(null)

onMounted(() => {
  // 必须在onMounted后才能获取到实例/DOM!
  console.log('子组件实例:', childComp.value)
  console.log('子组件数据:', childComp.value.childMsg)
  const res = childComp.value.childMethod()
  console.log('子组件方法返回值:', res)

  // 获取DOM
  console.log('DOM元素:', domBox.value)
})
</script>

4、全局状态管理:Vuex(Vue2) / Pinia(Vue3)

适用场景:中大型项目,任意关系组件共享全局状态,需要统一管理状态读写、异步操作。


Vue2 方案:Vuex(Vue2 官方状态管理)

Vuex 核心概念:State(状态)、Mutations(同步修改)、Actions(异步操作)、Getters(计算属性)、Modules(模块化)。

javascript 运行

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
    userInfo: null
  },
  mutations: {
    // 同步修改state(唯一修改state的方式)
    increment(state, payload = 1) {
      state.count += payload
    },
    setUserInfo(state, user) {
      state.userInfo = user
    }
  },
  actions: {
    // 异步操作,提交mutation
    async fetchUserInfo({ commit }) {
      const res = await fetch('/api/user')
      const user = await res.json()
      commit('setUserInfo', user)
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
})

// 组件中使用
export default {
  computed: {
    count() {
      return this.$store.state.count
    },
    doubleCount() {
      return this.$store.getters.doubleCount
    }
  },
  methods: {
    addCount() {
      this.$store.commit('increment', 2) // 提交mutation
    },
    getUserInfo() {
      this.$store.dispatch('fetchUserInfo') // 触发action
    }
  }
}

Vue3 方案:Pinia(Vue3 官方推荐,替代 Vuex)

Pinia 是 Vue3 的下一代状态管理,相比 Vuex:

  • 去掉Mutations,直接在actions中修改状态(同步 / 异步都支持)
  • 自动模块化,无需手动注册
  • 完美支持 TS,组合式 API 友好
  • 体积仅~1KB,DevTools 支持更好

javascript 运行

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    userInfo: null
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    // 同步/异步直接写,无需mutation
    increment(payload = 1) {
      this.count += payload
    },
    async fetchUserInfo() {
      const res = await fetch('/api/user')
      const user = await res.json()
      this.userInfo = user
    }
  }
})

// 组件中使用(<script setup>)
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

const counterStore = useCounterStore()
// 解构保持响应式(必须用storeToRefs)
const { count, doubleCount, userInfo } = storeToRefs(counterStore)

// 直接调用action
const addCount = () => counterStore.increment(2)
const getUserInfo = () => counterStore.fetchUserInfo()
</script>

5、parent/parent / children:父子直接访问(不推荐)


12. 插槽 Slot(默认 / 具名 / 作用域)

插槽:父组件向子组件传递 HTML 结构

12.1 默认插槽

<!-- 子组件 -->
<slot>默认内容</slot>
<!-- 父组件 -->
<Child>我是插入的内容</Child>

12.2 具名插槽(多个插槽)

Vue2

<!-- 子组件 -->
<slot name="header"></slot>
<!-- 父组件 -->
<template slot="header">头部内容</template>

Vue3(v-slot 简写 #)

<!-- 子组件 -->
<slot name="header"></slot>
<!-- 父组件 -->
<template #header>头部内容</template>

12.3 作用域插槽(子传数据给插槽)

<!-- 子组件 -->
<slot :user="user"></slot>
<script setup>
const user = reactive({ name: '插槽数据' })
</script>

<!-- 父组件 -->
<template #default="scope">
  {{ scope.user.name }}
</template>

13. 自定义指令 Directive

13.1 Vue2 自定义指令

javascript 运行

// 全局指令
Vue.directive('focus', {
  inserted(el) { el.focus() }
})

// 局部指令
export default {
  directives: {
    focus: { inserted(el) { el.focus() } }
  }
}

13.2 Vue3 自定义指令

javascript 运行

// 全局指令
app.directive('focus', {
  mounted(el) { el.focus() }
})

// 局部指令
<script setup>
const vFocus = { mounted: (el) => el.focus() }
</script>
<template> <input v-focus /> </template>

14. 过滤器 Filter(Vue2 有 / Vue3 废弃)

Vue2 过滤器

<template>
  <p>{{ msg | reverse }}</p>
</template>
<script>
export default {
  data() { return { msg: 'abc' } },
  filters: {
    reverse(val) { return val.split('').reverse().join('') }
  }
}
</script>

Vue3 替代方案

计算属性函数替代过滤器

<script setup>
import { ref, computed } from 'vue'
const msg = ref('abc')
const reverseMsg = computed(() => msg.value.split('').reverse().join(''))
</script>

15. 混入 Mixin(Vue2 有 / Vue3 废弃)

Vue2 用于复用代码,Vue3 用组合式函数替代

// mixin.js
export default {
  data() { return { mixinMsg: 'mixin数据' } },
  methods: { mixinFun() { console.log('mixin方法') } }
}

// 组件使用
import myMixin from './mixin.js'
export default { mixins: [myMixin] }

16. 路由 Vue-Router(完整配置 + 使用)

16.1 Vue2 + Vue-Router@3

javascript 运行

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)

const routes = [
  { path: '/', component: Home }
]
const router = new VueRouter({ routes })
export default router

// main.js 引入
import router from './router'
new Vue({ router }).$mount('#app')

16.2 Vue3 + Vue-Router@4

javascript 运行

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
  { path: '/', component: Home }
]
const router = createRouter({
  history: createWebHistory(),
  routes
})
export default router

// 组件使用
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter() // 跳转
const route = useRoute() // 获取参数
// 跳转
const goHome = () => router.push('/')
</script>

17. Vue3 新增高级 API

17.1 Teleport 传送门

将组件渲染到指定 DOM,常用于弹窗

<teleport to="body">
  <div class="modal">弹窗内容</div>
</teleport>

17.2 Suspense 异步组件

<Suspense>
  <template #default>
    <AsyncComponent />
  </template>
  <template #fallback>
    <div>加载中...</div>
  </template>
</Suspense>

17.3 defineComponent 类型推导

<script setup>
import { defineComponent } from 'vue'
const MyComponent = defineComponent({/*...*/})
</script>

18. Vue2 废弃 API 汇总

  1. $on / $off / $once(EventBus 废弃)
  2. filter 过滤器
  3. mixin 混入
  4. $children / $destroy
  5. 旧版插槽语法 slot / slot-scope
  6. .sync 修饰符

19. 新手常见问题与注意事项

  1. Vue3 ref 修改变量必须加 .value,模板中不用
  2. v-for 必须加唯一 key,不要用 index
  3. v-if 和 v-for 不要同标签使用
  4. Vue2 对象 / 数组修改用 $set,Vue3 直接修改
  5. 计算属性有缓存,methods 无缓存
  6. 组件名要大驼峰,避免和 HTML 标签冲突

文末总结

  1. Vue2 以选项式 API为主,适合小型项目,响应式有缺陷;
  2. Vue3 以组合式 API为主,适合中大型项目,响应式完美,TS 友好;
  3. 新手优先学 Vue3 + Vite + Pinia,这是未来主流技术栈。