1. 性能优化重大改进
1.1 全新的响应式系统
从 Object.defineProperty 到 Proxy
// Vue2 基于 Object.defineProperty
const obj = {}
Object.defineProperty(obj, 'name', {
get() {
console.log('获取name')
return this._name
},
set(value) {
console.log('设置name:', value)
this._name = value
}
})
// Vue3 基于 Proxy
const reactiveObj = new Proxy(obj, {
get(target, key, receiver) {
console.log('获取属性:', key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('设置属性:', key, value)
return Reflect.set(target, key, value, receiver)
}
})
响应式 API 对比
// Vue2 Options API
export default {
data() {
return {
user: {
name: 'John',
address: {
city: 'Beijing'
}
}
}
},
mounted() {
// Vue2 无法检测以下变化:
this.user.age = 25 // ❌ 新增属性
this.user.address.postcode = '100000' // ❌ 深层嵌套
this.items[0] = newItem // ❌ 数组索引赋值
}
}
// Vue3 Composition API
import { reactive } from 'vue'
const user = reactive({
name: 'John',
address: {
city: 'Beijing'
}
})
// Vue3 可以检测所有变化:
user.age = 25 // ✅ 新增属性
user.address.postcode = '100000' // ✅ 深层嵌套
1.2 编译时优化
Patch Flag 优化
// 模板
<div>
<span>静态内容</span>
<span :class="dynamicClass">{{ dynamicText }}</span>
<span :id="dynamicId">{{ dynamicText }}</span>
</div>
// Vue2 编译结果 - 全量 Diff
function render() {
return _c('div', [
_c('span', [_v("静态内容")]),
_c('span', { class: _vm.dynamicClass }, [_v(_vm.dynamicText)]),
_c('span', { attrs: { id: _vm.dynamicId } }, [_v(_vm.dynamicText)])
])
}
// Vue3 编译结果 - 带 Patch Flag
import { createElementVNode as _createElementVNode, ... } from "vue"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("span", null, "静态内容", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createElementVNode("span", {
class: _ctx.dynamicClass
}, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */),
_createElementVNode("span", {
id: _ctx.dynamicId
}, _toDisplayString(_ctx.dynamicText), 9 /* TEXT, PROPS */, ["id"])
]))
}
Tree-shaking 支持
// Vue3 模块化架构,支持 Tree-shaking
import { createApp, ref, computed } from 'vue'
// 只引入使用的 API,未使用的如 v-model 等会被摇树优化掉
// Vue2 全量引入
import Vue from 'vue' // 无论用不用,全部引入
2. Composition API 深度解析
2.1 响应式基础 API
ref 与 reactive 的智能选择
import { ref, reactive, isRef, unref, toRef, toRefs } from 'vue'
// ref - 适合基本类型和对象引用替换
const count = ref(0)
const userRef = ref({ name: 'John', age: 25 })
// reactive - 适合复杂对象,不需要 .value
const user = reactive({
name: 'John',
profile: { age: 25 }
})
// 工具函数
console.log(isRef(count)) // true
console.log(unref(count)) // 0 (如果是 ref 返回 .value,否则返回本身)
// 响应式对象转 ref
const nameRef = toRef(user, 'name')
// 解构保持响应式
const { name, profile } = toRefs(user)
响应式工具函数进阶
import { shallowRef, triggerRef, customRef } from 'vue'
// shallowRef - 浅层响应式
const shallowObj = shallowRef({ deep: { data: 'value' } })
shallowObj.value.deep.data = 'new' // ❌ 不会触发更新
shallowObj.value = { ...shallowObj.value } // ✅ 需要替换整个对象
// triggerRef - 强制触发更新
function updateShallow() {
shallowObj.value.deep.data = 'new'
triggerRef(shallowObj) // 手动触发更新
}
// customRef - 自定义响应式
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
const debouncedText = useDebouncedRef('hello')
2.2 高级响应式模式
只读代理和浅层代理
import { readonly, shallowReactive, markRaw } from 'vue'
// readonly - 创建只读代理
const original = reactive({ count: 0 })
const copy = readonly(original)
original.count++ // ✅ 正常工作
copy.count++ // ❌ 警告,不允许修改
// shallowReactive - 浅层响应式
const shallow = shallowReactive({
nested: { count: 0 } // nested 不是响应式的
})
// markRaw - 标记为不可代理
const nonReactive = markRaw({ shouldNotBeReactive: true })
const state = reactive({
nested: nonReactive // nested 不会被转换为响应式
})
3. 组件系统革新
3.1 碎片(Fragments)支持
<!-- Vue2 需要根元素 -->
<template>
<div>
<header></header>
<main></main>
<footer></footer>
</div>
</template>
<!-- Vue3 支持多根节点 -->
<template>
<header></header>
<main></main>
<footer></footer>
</template>
<script setup>
// 多根节点组件需要显式定义属性继承
defineOptions({
inheritAttrs: false
})
</script>
3.2 异步组件增强
import { defineAsyncComponent, Suspense } from 'vue'
// 基础异步组件
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
// 带加载状态的异步组件
const AsyncWithLoading = defineAsyncComponent({
loader: () => import('./components/HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200, // 延迟显示 loading
timeout: 3000 // 超时时间
})
// Suspense 组件使用
<template>
<Suspense>
<template #default>
<AsyncComp />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
3.3 Teleport 组件
<template>
<div class="app">
<button @click="showModal = true">打开模态框</button>
<!-- 将内容渲染到 body 末尾 -->
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="modal-content">
<h2>模态框标题</h2>
<p>这个模态框会被渲染到 body 末尾</p>
<button @click="showModal = false">关闭</button>
</div>
</div>
</Teleport>
<!-- 多个 Teleport 到同一目标 -->
<Teleport to="#modals">
<div v-if="notification" class="notification">
{{ notification }}
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
const notification = ref('')
</script>
4. 新的组件定义方式
4.1 <script setup> 语法糖
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ count }}</p>
<button @click="increment">增加</button>
<ChildComponent :message="message" @update="handleUpdate" />
</div>
</template>
<script setup lang="ts">
// 导入的组件自动注册
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'
// 响应式数据
const title = ref('Vue3 新特性')
const count = ref(0)
const message = ref('Hello from parent')
// 方法
const increment = () => count.value++
// 事件处理
const handleUpdate = (newValue: string) => {
message.value = newValue
}
// 生命周期
onMounted(() => {
console.log('组件挂载完成')
})
// 定义属性
defineProps<{
initialCount?: number
}>()
// 定义事件
defineEmits<{
(e: 'countChange', value: number): void
}>()
// 暴露给父组件的内容
defineExpose({
count,
increment
})
</script>
4.2 组合式函数最佳实践
// composables/usePagination.ts
import { ref, computed, watch } from 'vue'
interface UsePaginationOptions {
initialPage?: number
pageSize?: number
total: number
}
export function usePagination(options: UsePaginationOptions) {
const {
initialPage = 1,
pageSize = 10,
total
} = options
const currentPage = ref(initialPage)
const pageSizeRef = ref(pageSize)
// 计算属性
const totalPages = computed(() =>
Math.ceil(total / pageSizeRef.value)
)
const offset = computed(() =>
(currentPage.value - 1) * pageSizeRef.value
)
const hasPrevious = computed(() => currentPage.value > 1)
const hasNext = computed(() => currentPage.value < totalPages.value)
// 方法
const next = () => {
if (hasNext.value) currentPage.value++
}
const previous = () => {
if (hasPrevious.value) currentPage.value--
}
const goTo = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
}
}
const setPageSize = (size: number) => {
pageSizeRef.value = size
// 重置到第一页,防止越界
if (currentPage.value > totalPages.value) {
currentPage.value = 1
}
}
// 监听变化
watch([currentPage, pageSizeRef], () => {
// 可以在这里触发数据加载
})
return {
// 状态
currentPage,
pageSize: pageSizeRef,
// 计算属性
totalPages,
offset,
hasPrevious,
hasNext,
// 方法
next,
previous,
goTo,
setPageSize
}
}
5. 性能优化 API
5.1 v-memo 指令
<template>
<div>
<!-- 只有依赖项变化时才重新渲染 -->
<div v-memo="[user.id, user.name]">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<p>{{ user.bio }}</p> <!-- bio 变化不会触发重新渲染 -->
</div>
<!-- 列表优化 -->
<div
v-for="item in largeList"
:key="item.id"
v-memo="[item.id, item.name, selectedId === item.id]"
>
{{ item.name }}
<button
:class="{ active: selectedId === item.id }"
@click="selectItem(item.id)"
>
选择
</button>
</div>
</div>
</template>
<script setup>
const user = ref({
id: 1,
name: 'John',
email: 'john@example.com',
bio: '...很长的简介...'
})
const largeList = ref([...]) // 大型列表
const selectedId = ref(null)
const selectItem = (id) => {
selectedId.value = id
}
</script>
6. 全局 API 变化
6.1 创建应用实例
// Vue2
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
// Vue3
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 全局配置
app.config.globalProperties.$http = axios
app.config.errorHandler = (err, instance, info) => {
// 错误处理
}
// 全局组件、指令、插件
app.component('MyComponent', MyComponent)
app.directive('focus', FocusDirective)
app.use(router)
app.use(store)
app.mount('#app')
6.2 下一代构建工具 Vite
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router'],
utils: ['lodash', 'axios']
}
}
}
}
})
7. TypeScript 集成改进
7.1 更好的类型推断
// 组件 Props 类型定义
interface Props {
title: string
count?: number
items?: string[]
onUpdate?: (value: string) => void
}
// 方式1: 使用 defineProps 泛型
const props = defineProps<Props>()
// 方式2: 运行时声明 + 类型
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
} as const)
// 事件类型定义
const emit = defineEmits<{
(e: 'update:title', value: string): void
(e: 'change', value: number): void
}>()
// 模板引用类型
const inputRef = ref<HTMLInputElement | null>(null)
const childCompRef = ref<InstanceType<typeof ChildComponent> | null>(null)
8. 迁移策略和兼容性
8.1 渐进式迁移
// Vue3 中兼容 Vue2 语法
const app = createApp({
// 仍然支持 Options API
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
// 同时可以使用 Composition API
setup() {
const doubled = computed(() => this.count * 2)
return { doubled }
}
})
总结
Vue3 的新特性带来了:
-
性能飞跃:Proxy 响应式 + 编译时优化
-
开发体验:Composition API +
<script setup> -
类型安全:完整的 TypeScript 支持
-
灵活组合:组合式函数 + 碎片支持
-
构建优化:Tree-shaking + Vite
这些改进使 Vue3 在性能、开发体验和可维护性方面都达到了新的高度,为大型应用开发提供了更好的支持。
5610

被折叠的 条评论
为什么被折叠?



