Vue3 是对 Vue2 的全面重构而非简单更新,核心变化可概括为:
Object.defineProperty
→ Proxy
重构以下通过源码对比详细解析核心差异:
源码位置:src/core/observer/index.js
// 简化版实现
export function defineReactive(obj, key) {
const dep = new Dep()
let val = obj[key]
Object.defineProperty(obj, key, {
get() {
dep.depend() // 收集依赖
return val
},
set(newVal) {
val = newVal
dep.notify() // 触发更新
}
})
}
缺陷分析:
Vue.set
)源码位置:packages/reactivity/src/reactive.ts
// 基于Proxy的实现
function createReactiveObject(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key) // 追踪依赖
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key) // 触发更新
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
trigger(target, key) // 删除也能触发更新
return result
}
})
}
优势对比:
特性 | Vue2 | Vue3 | 提升效果 |
---|---|---|---|
检测能力 | 仅限已有属性 | 全属性 | 无需Vue.set |
数组支持 | 需重写方法 | 原生支持 | 直接索引赋值 |
集合类型 | 不支持Map/Set | 原生支持 | 扩展数据结构支持 |
初始化性能 | O(n)递归遍历 | O(1)代理 | 大型对象快40% |
源码位置:src/core/vdom/patch.js
// 全量比较策略
function patchVnode(oldVnode, vnode) {
// 1. 比较标签类型
if (oldVnode.tag !== vnode.tag) {
replaceVnode(oldVnode, vnode)
return
}
// 2. 比较属性
const oldProps = oldVnode.data.attrs || {}
const newProps = vnode.data.attrs || {}
updateAttrs(oldVnode.elm, oldProps, newProps)
// 3. 比较子节点(递归)
updateChildren(oldVnode.elm, oldVnode.children, vnode.children)
}
问题:即使静态内容也要遍历比较
源码位置:packages/runtime-core/src/vnode.ts
// PatchFlag 标记(二进制位)
export const enum PatchFlags {
TEXT = 1, // 动态文本
CLASS = 1 << 1, // 动态class
STYLE = 1 << 2, // 动态style
PROPS = 1 << 3, // 动态属性(非class/style)
FULL_PROPS = 1 << 4, // 动态key需全量比较
HYDRATE_EVENTS = 1 << 5,
STABLE_FRAGMENT = 1 << 6,
KEYED_FRAGMENT = 1 << 7,
UNKEYED_FRAGMENT = 1 << 8,
NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10,
HOISTED = -1, // 静态节点
BAIL = -2 // 差异太大需全量比较
}
// 编译后生成的代码
const _hoisted_1 = /*#__PURE__*/_createVNode("h1", null, "静态标题", -1 /* HOISTED */)
function render(_ctx) {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_createVNode("p", null, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */)
]))
}
优化效果:
Vue2 实现计数器:
// src/components/Counter.vue
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
},
computed: {
double() {
return this.count * 2
}
}
}
Vue3 Composition API实现:
// src/components/Counter.vue
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, double, increment }
}
}
核心源码:packages/runtime-core/src/component.ts
// 组件实例创建流程
function setupComponent(instance) {
// 解析Options API
const { setup } = instance.type
if (setup) {
// 创建setup上下文
const setupContext = createSetupContext(instance)
// 执行setup函数
const setupResult = callWithErrorHandling(
setup, instance, [instance.props, setupContext]
)
// 处理返回结果
if (isFunction(setupResult)) {
// 返回渲染函数
instance.render = setupResult
} else if (isObject(setupResult)) {
// 绑定到实例上下文
instance.setupState = proxyRefs(setupResult)
}
}
// 兼容Options API
if (!instance.render) {
instance.render = instance.type.render || NOOP
}
}
设计优势:
逻辑复用:自定义Hook代替Mixins
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const double = computed(() => count.value * 2)
const increment = () => count.value++
return { count, double, increment }
}
更好的TS支持:完整类型推导
代码组织:相关逻辑集中管理
Vue2 | Vue3 Composition | 源码位置(Vue3) |
---|---|---|
beforeCreate | 使用setup()替代 | 在createComponentInstance之前 |
created | 使用setup()替代 | 在setupComponent之后 |
beforeMount | onBeforeMount | packages/runtime-core/src/apiLifecycle.ts |
mounted | onMounted | 同上 |
beforeUpdate | onBeforeUpdate | 同上 |
updated | onUpdated | 同上 |
beforeDestroy | onBeforeUnmount | 同上 |
destroyed | onUnmounted | 同上 |
Vue2 全局API:
// src/core/global-api/index.js
export function initGlobalAPI(Vue) {
Vue.config = {...}
Vue.util = {...}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
Vue.observable = observable
Vue.options = {}
// 注册内置组件
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
Vue3 模块化API:
// packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI(render) {
return function createApp(rootComponent, rootProps = null) {
const context = createAppContext()
const app: App = {
_component: rootComponent,
_props: rootProps,
_container: null,
component(name, component) {
context.components[name] = component
return app
},
directive(name, directive) {
context.directives[name] = directive
return app
},
mount(rootContainer) {
// 挂载逻辑
}
}
return app
}
}
变化核心:
createApp()
)编译产物:
function render() {
with(this) {
return _c('div', [
_c('h1', [_v("静态标题")]),
_c('p', [_v(_s(dynamicText))])
])
}
}
问题:无静态动态区分,全量Diff
源码位置:packages/compiler-core/src/transform.ts
// PatchFlag 生成逻辑
export const transformElement = (node, context) => {
// 标记动态属性
const patchFlag = getPatchFlag(node)
return () => {
if (patchFlag !== PatchFlags.HOISTED) {
node.codegenNode = createVNodeCall(
context,
node.tag,
node.props,
node.children,
patchFlag
)
}
}
}
// 编译产物(带PatchFlag)
import { createVNode as _createVNode, openBlock as _openBlock } from "vue"
export function render(_ctx) {
return (_openBlock(),
_createVNode("div", null, [
_createVNode("h1", null, "静态标题"),
_createVNode("p", null, _ctx.dynamicText, 1 /* TEXT */)
]))
}
核心优化点:
问题表现:
import Vue from 'vue'
const Component = Vue.extend({
props: {
message: String // 类型声明有限
},
methods: {
handleClick() {
this.$emit('custom') // 无法推断事件类型
}
}
})
源码结构:
packages/
compiler-core/ # TS实现
runtime-core/ # TS实现
reactivity/ # TS实现
...
组件类型声明:
// packages/runtime-core/src/apiDefineComponent.ts
export function defineComponent(options) {
return isFunction(options)
? { setup: options }
: options
}
// 使用示例
import { defineComponent } from 'vue'
export default defineComponent({
props: {
message: {
type: String,
required: true
}
},
emits: ['change'], // 声明事件
setup(props, { emit }) {
props.message // 自动推断为string
function handleChange() {
emit('change') // 类型安全
}
}
})
优势:
Vue3 不是简单的版本迭代,而是前端框架设计的范式转变。其架构思想已影响 React 18、Svelte 等框架设计,代表了现代前端框架的发展方向。掌握 Vue3 不仅是为了使用新特性,更是理解前端框架设计思想的必修课。