Vue 2 与 Vue 3 语法区别完整对比

大家好,我是鱼樱!!!

关注公众号【鱼樱AI实验室】持续每天分享更多前端和AI辅助前端编码新知识~~喜欢的就一起学反正开源至上,无所谓被诋毁被喷被质疑文章没有价值~

一个城市淘汰的自由职业-农村前端程序员(虽然不靠代码挣钱,写文章就是为爱发电),兼职远程上班目前!!!热心坚持分享多数人疑惑的到底vue2/vue3差异语法有哪些~~~

1. 核心 API 的变化

实例创建

Vue 2:

js 复制代码
 
import Vue from 'vue'
const app = new Vue({
  el: '#app',
  // 选项...
})

Vue 3:

js 复制代码
 
import { createApp } from 'vue'
const app = createApp({
  // 选项...
})
app.mount('#app')

全局 API

Vue 2:

js 复制代码
 
// 全局注册组件
Vue.component('my-component', {
  // 选项...
})

// 全局指令
Vue.directive('my-directive', {
  // 钩子函数...
})

// 全局混入
Vue.mixin({
  // 选项...
})

// 全局使用插件
Vue.use(MyPlugin)

Vue 3:

js 复制代码
 
// 应用实例注册组件
app.component('my-component', {
  // 选项...
})

// 应用实例注册指令
app.directive('my-directive', {
  // 钩子函数...
})

// 应用实例混入
app.mixin({
  // 选项...
})

// 应用实例使用插件
app.use(MyPlugin)

2. 组合式 API(Composition API)

基本用法

Vue 2:

js 复制代码
 
<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  mounted() {
    console.log('组件已挂载')
  }
}
</script>

Vue 3:

js 复制代码
 
<script setup>
import { ref, computed, onMounted } from 'vue'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)

function increment() {
  count.value++
}

onMounted(() => {
  console.log('组件已挂载')
})
</script>

响应式系统

Vue 2:

js 复制代码
 
export default {
  data() {
    return {
      user: {
        name: 'John',
        age: 30
      }
    }
  },
  methods: {
    updateUser() {
      // 直接修改对象属性是响应式的
      this.user.name = 'Jane'
      
      // 添加新属性需要 Vue.set 才能保持响应式
      this.$set(this.user, 'address', 'New York')
    }
  }
}

Vue 3:

js 复制代码
 
import { reactive } from 'vue'

const user = reactive({
  name: 'John',
  age: 30
})

// 直接修改对象属性是响应式的
user.name = 'Jane'
// 直接添加新属性也是响应式的
user.address = 'New York'

3. script setup 语法糖

Vue 3 独有:

js 复制代码
 
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'

// 导入的组件自动注册
// props 和 emits
defineProps({
  title: String
})

defineEmits(['update', 'delete'])

// 暴露给父组件的公共属性和方法
defineExpose({
  someMethod() {
    // ...
  }
})

// 局部变量和函数自动可用于模板
const count = ref(0)
function increment() {
  count.value++
}
</script>

<template>
  <div>
    <MyComponent />
    <button @click="increment">{{ count }}</button>
  </div>
</template>

4. 生命周期钩子

命名变化

Vue 2 Vue 3
beforeCreate setup()
created setup()
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
errorCaptured onErrorCaptured
- onRenderTracked
- onRenderTriggered
- onActivated
- onDeactivated
- onServerPrefetch

使用对比

Vue 2:

js 复制代码
 
export default {
  created() {
    console.log('组件创建')
  },
  mounted() {
    console.log('组件挂载')
  },
  beforeDestroy() {
    console.log('组件即将销毁')
  }
}

Vue 3 选项式 API:

js 复制代码
 
export default {
  created() {
    console.log('组件创建')
  },
  mounted() {
    console.log('组件挂载')
  },
  beforeUnmount() { // 注意名称变化
    console.log('组件即将卸载')
  }
}

Vue 3 组合式 API:

js 复制代码
 
import { onMounted, onBeforeUnmount } from 'vue'

export default {
  setup() {
    // created 钩子的代码直接在 setup 中执行
    console.log('组件创建')
    
    onMounted(() => {
      console.log('组件挂载')
    })
    
    onBeforeUnmount(() => {
      console.log('组件即将卸载')
    })
  }
}

5. 多根节点组件 (Fragment)

Vue 2: 必须有一个单根节点

html 复制代码
 
<template>
  <div>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

Vue 3: 支持多根节点

html 复制代码
 
<template>
  <header>...</header>
  <main>...</main>
  <footer>...</footer>
</template>

6. Teleport 组件

Vue 3 独有:

html 复制代码
 
<template>
  <div>
    <!-- 正常内容 -->
    
    <!-- 传送到 body 末尾 -->
    <teleport to="body">
      <div class="modal">
        <!-- 模态框内容 -->
      </div>
    </teleport>
  </div>
</template>

7. 自定义指令

Vue 2:

js 复制代码
 
Vue.directive('highlight', {
  bind(el, binding) {
    el.style.backgroundColor = binding.value
  },
  inserted(el) {
    // ...
  },
  update(el, binding) {
    el.style.backgroundColor = binding.value
  },
  componentUpdated() {
    // ...
  },
  unbind() {
    // ...
  }
})

Vue 3:

js 复制代码
 
app.directive('highlight', {
  // 与 Vue 2 相比钩子函数被重命名
  beforeMount(el, binding) {
    el.style.backgroundColor = binding.value
  },
  mounted() {
    // ...
  },
  beforeUpdate() {
    // ...
  },
  updated(el, binding) {
    el.style.backgroundColor = binding.value
  },
  beforeUnmount() {
    // ...
  },
  unmounted() {
    // ...
  }
})

8. 事件 API

Vue 2:

js 复制代码
 
// 事件总线
Vue.prototype.$bus = new Vue()

// 组件 A
this.$bus.$emit('custom-event', payload)

// 组件 B
created() {
  this.$bus.$on('custom-event', this.handleEvent)
},
beforeDestroy() {
  this.$bus.$off('custom-event', this.handleEvent)
}

Vue 3: 移除了事件总线,推荐使用外部库或 mitt/tiny-emitter

js 复制代码
 
// 安装: npm install mitt
import mitt from 'mitt'
const emitter = mitt()

// 创建全局实例
app.config.globalProperties.emitter = emitter

// 组件 A
emitter.emit('custom-event', payload)

// 组件 B
import { getCurrentInstance, onUnmounted } from 'vue'

setup() {
  const { proxy } = getCurrentInstance()
  
  const handleEvent = (payload) => {
    // ...
  }
  
  proxy.emitter.on('custom-event', handleEvent)
  
  onUnmounted(() => {
    proxy.emitter.off('custom-event', handleEvent)
  })
}

9. 过滤器

Vue 2:

html 复制代码
 
<template>
  <div>{{ price | formatPrice }}</div>
</template>

<script>
export default {
  filters: {
    formatPrice(value) {
      return '¥' + value.toFixed(2)
    }
  }
}
</script>

Vue 3: 过滤器已被移除,推荐使用方法或计算属性

html 复制代码
 
<template>
  <div>{{ formatPrice(price) }}</div>
</template>

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

const price = ref(100)

function formatPrice(value) {
  return '¥' + value.toFixed(2)
}
</script>

10. 异步组件

Vue 2:

js 复制代码
 
// 全局注册
Vue.component('async-component', () => import('./AsyncComponent.vue'))

// 局部注册
export default {
  components: {
    AsyncComponent: () => import('./AsyncComponent.vue')
  }
}

Vue 3:

js 复制代码
 
import { defineAsyncComponent } from 'vue'

// 基本用法
const AsyncComp = defineAsyncComponent(() => 
  import('./AsyncComponent.vue')
)

// 高级用法
const AsyncCompWithOptions = defineAsyncComponent({
  loader: () => import('./AsyncComponent.vue'),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,
  timeout: 3000
})

11. 响应式引用

Vue 2:

js 复制代码
 
export default {
  data() {
    return {
      count: 0,
      user: { name: 'John' }
    }
  },
  methods: {
    updateUser() {
      // 直接修改嵌套对象属性
      this.user.name = 'Jane'
      
      // 替换整个对象
      this.user = { name: 'Mike' }
    }
  }
}

Vue 3:

js 复制代码
 
import { ref, reactive } from 'vue'

// 基本类型使用 ref
const count = ref(0)
// 访问和修改需要 .value
console.log(count.value)
count.value++

// 对象使用 reactive
const user = reactive({ name: 'John' })
// 直接访问和修改,不需要 .value
console.log(user.name)
user.name = 'Jane'

12. 组件间通信

Props 和事件

Vue 2:

html 复制代码
 
<!-- 父组件 -->
<template>
  <child-component 
    :message="message" 
    @update="handleUpdate"
  />
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello'
    }
  },
  methods: {
    handleUpdate(value) {
      this.message = value
    }
  }
}
</script>

<!-- 子组件 -->
<script>
export default {
  props: {
    message: String
  },
  methods: {
    updateMessage(value) {
      this.$emit('update', value)
    }
  }
}
</script>

Vue 3:

html 复制代码
 
<!-- 父组件 -->
<template>
  <child-component 
    :message="message" 
    @update="handleUpdate"
  />
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const message = ref('Hello')
const handleUpdate = (value) => {
  message.value = value
}
</script>

<!-- 子组件 -->
<script setup>
const props = defineProps({
  message: String
})

const emit = defineEmits(['update'])

function updateMessage(value) {
  emit('update', value)
}
</script>

Provide/Inject

Vue 2:

js 复制代码
 
// 父组件
export default {
  provide() {
    return {
      theme: 'dark',
      user: this.user // 注意: 非响应式传递
    }
  },
  data() {
    return {
      user: { name: 'John' }
    }
  }
}

// 子组件
export default {
  inject: ['theme', 'user']
}

Vue 3:

js 复制代码
 
// 父组件
import { provide, ref, reactive } from 'vue'

export default {
  setup() {
    const theme = ref('dark')
    const user = reactive({ name: 'John' })
    
    provide('theme', theme) // 响应式传递
    provide('user', user)  // 响应式传递
  }
}

// 子组件
import { inject } from 'vue'

export default {
  setup() {
    const theme = inject('theme')
    const user = inject('user')
    
    // theme.value 可以访问传递的值
    // user.name 直接访问
  }
}

13. 渲染函数和JSX

Vue 2:

js 复制代码
 
export default {
  render(h) {
    return h('div', {
      attrs: {
        id: 'app'
      },
      on: {
        click: this.handleClick
      }
    }, [
      h('span', 'Hello Vue 2')
    ])
  },
  methods: {
    handleClick() {
      console.log('clicked')
    }
  }
}

Vue 3:

js 复制代码
 
import { h } from 'vue'

export default {
  setup() {
    const handleClick = () => {
      console.log('clicked')
    }
    
    return () => h('div', {
      id: 'app',
      onClick: handleClick
    }, [
      h('span', 'Hello Vue 3')
    ])
  }
}

14. v-model 变化

Vue 2:

js 复制代码
 
<!-- 在组件上使用 v-model -->
<custom-input v-model="searchText"></custom-input>

<!-- 等价于 -->
<custom-input
  :value="searchText"
  @input="searchText = $event"
></custom-input>

<!-- CustomInput.vue -->
<script>
export default {
  props: {
    value: String
  },
  methods: {
    updateValue(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

Vue 3:

js 复制代码
 
<!-- 在组件上使用 v-model -->
<custom-input v-model="searchText"></custom-input>

<!-- 等价于 -->
<custom-input
  :modelValue="searchText"
  @update:modelValue="searchText = $event"
></custom-input>

<!-- 多个 v-model -->
<CustomInput
  v-model:text="searchText"
  v-model:active="isActive"
/>

<!-- CustomInput.vue -->
<script setup>
const props = defineProps({
  modelValue: String
})

const emit = defineEmits(['update:modelValue'])

function updateValue(e) {
  emit('update:modelValue', e.target.value)
}
</script>

15. Suspense 组件(Vue 3 新增)

html 复制代码
 
<template>
  <Suspense>
    <template #default>
      <async-component />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

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

const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))
</script>

16. 组合式函数 (Composables)

Vue 3 独有的模式:

js 复制代码
 
// useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  const double = computed(() => count.value * 2)
  
  function increment() {
    count.value++
  }
  
  return {
    count,
    double,
    increment
  }
}

// 在组件中使用
import { useCounter } from './useCounter'

export default {
  setup() {
    const { count, double, increment } = useCounter(10)
    
    return {
      count,
      double,
      increment
    }
  }
}

// 在 script setup 中使用
import { useCounter } from './useCounter'

const { count, double, increment } = useCounter(10)

17. TypeScript 支持

Vue 3 相较于 Vue 2 有更强的 TypeScript 支持:

js 复制代码
 
// Vue 3 中使用 TypeScript 的 defineComponent
import { defineComponent, ref, PropType } from 'vue'

interface User {
  id: number
  name: string
}

export default defineComponent({
  props: {
    user: {
      type: Object as PropType<User>,
      required: true
    }
  },
  setup(props) {
    const count = ref<number>(0)
    return { count }
  }
})

// script setup 中使用 TypeScript
<script setup lang="ts">
import { ref } from 'vue'

interface User {
  id: number
  name: string
}

const props = defineProps<{
  user: User
  optional?: string
}>()

const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 带默认值的 props
withDefaults(defineProps<{
  size?: number
  message?: string
}>(), {
  size: 0,
  message: 'hello'
})
</script>

18. 主要破坏性变更

  1. v-if 与 v-for 优先级变更

    • Vue 2: v-for 优先于 v-if
    • Vue 3: v-if 优先于 v-for
  2. 全局 API 不再污染全局空间

  3. 移除了 Vue.set()/Vue.delete()this.delete()

  4. 移除了过滤器 (filters)

  5. 移除了 off, $once 事件 API

  6. 移除了 $children API

  7. 移除了 .native 修饰符

    • Vue 2: <my-component @click.native="onClick" />
    • Vue 3: 在组件中使用 emits 选项声明所有事件

19. 总结对比

特性 Vue 2 Vue 3
核心架构 基于选项式 API 支持组合式 API 和选项式 API
全局 API 直接修改 Vue,全局污染 应用实例 API,不污染全局
响应式系统 Object.defineProperty Proxy 实现,性能更好
组件根节点 必须有单一根节点 支持多根节点
TypeScript 支持 有限 完全支持,内置类型
渲染性能 较好 显著提升,静态树提升等优化
包体积 较大 更小,支持 tree-shaking
setup 函数 不支持 新增,更好的逻辑组织方式
v-model 使用 value/input 使用 modelValue/update:modelValue
自定义指令 API 生命周期钩子名基于组件 生命周期钩子名统一
异步组件 简单函数 使用 defineAsyncComponent
过滤器 支持 移除
事件总线 内置 移除,推荐使用外部库

Vue 3 相比 Vue 2 带来了更好的性能、更灵活的 API 以及更好的 TypeScript 支持,同时保留了 Vue 2 的易用性,为开发提供了更多选择。

相关推荐
A阳俊yi2 分钟前
SpringMVC中有关请求参数的问题(映射路径,传递不同的参数)
java·前端·javascript
鱼樱前端31 分钟前
Vue3 + TypeScript + Better-Scroll 极简上拉下拉组件
前端·javascript·vue.js
Trae首席推荐官37 分钟前
Trae 功能上新:支持 Remote-SSH 和自定义模型配置
前端·后端·trae
影子信息38 分钟前
element tree树形结构默认展开全部
前端·javascript·vue.js
Riesenzahn38 分钟前
说说你对CSS中@layer的了解
前端·javascript
甜点cc39 分钟前
前端每个组件外面套一层el-form,这样好吗?
前端·javascript·vue.js
用户40937927136840 分钟前
组件化开发
前端
关山月41 分钟前
JavaScript 内部机制:理解核心原理 ✨
前端
Riesenzahn42 分钟前
说说你对CSS中@container的了解
前端·javascript
Eliauk__42 分钟前
Vue 中 Computed 和 Watch 的深入解析与底层实现
前端·javascript·面试