
Autor |
https://juejin.cn/post/7011372376969445413
Espero que este artículo pueda ayudarlo a profundizar su comprensión de Vue y pueda jurar que domina Vue2/3. Además, también espero que los amigos que pasan puedan ayudarme a revisar y llenar los vacíos.
El contenido es mixto 用法
++ , se recomienda recopilarlo y verlo despacio. 原理
使用小心得
la diferencia
cambios en el ciclo de vida
En general, no hay muchos cambios, pero la mayoría de los nombres necesitan + y las funciones son similares. La API combinada de Vue3 debe introducirse primero; la API de opción de Vue2 se puede llamar directamente, como se muestra a continuación. on
// vue3
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
...
})
// 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不被覆盖
onMounted(() => {
...
})
</script>
// vue2
<script>
export default {
mounted() {
...
},
}
</script>
A continuación se muestra una tabla de ciclo de vida común.
Vue2.x | vue3 |
---|---|
antes de crear | Innecesario* |
creado | Innecesario* |
antes del monte | onBeforeMount |
montado | en Montado |
antes de Actualizar | onBeforeUpdate |
actualizado | onActualizado |
antes de destruir | onBeforeUnmount |
destruido | en Desmontado |
Sugerencias: funciona y se enlaza con el ciclo de vida, por lo que no es necesario definirlo explícitamente. setup
beforeCreate
created
Múltiples nodos raíz
Vue3 admite componentes de nodos multiraíz, es decir, fragment
.
En Vue2, al escribir una página, debemos envolver el componente en <div>
, de lo contrario, se informará una advertencia de error.
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
Con Vue3, podemos incluir múltiples nodos raíz en nuestros componentes y podemos escribir una capa menos, ¡muy bien!
<template>
<header>...</header>
<main>...</main>
<footer>...</footer>
</template>
Componentes asíncronos
Vue3 proporciona componentes que permiten que los programas representen el contenido final, como la carga, mientras esperan componentes asincrónicos, lo que hace que la experiencia del usuario sea más fluida. Para usarlo, declárelo en la plantilla e incluya dos ranuras con nombre: y . Asegúrese de mostrar la ranura predeterminada cuando se cargue el contenido asíncrono y utilice la ranura como estado de carga. Suspense
default
fallback
Suspense
fallback
<tempalte>
<suspense>
<template #default>
<todo-list />
</template>
<template #fallback>
<div>
Loading...
</div>
</template>
</suspense>
</template>
He pisado el pozo en proyectos reales. Si desea llamar a una solicitud asíncrona en la configuración, debe agregar una async
palabra clave antes de la configuración. En este momento, se le advertirá async setup() is used without a suspense boundary
.
Solución: envuelva una capa de componentes alrededor de la página principal que llama al Suspense
componente actual.
teletransportarse
Vue3 proporciona Teleport
componentes para mover partes del DOM fuera de la aplicación Vue. Tales como los Dialog
componentes comunes en el proyecto.
<button @click=dialogVisible = true>点击</button>
<teleport to=body>
<div class=dialog v-if=dialogVisible>
</div>
</teleport>
API compuesta
Vue2 es que una lógica se dispersará en diferentes posiciones del archivo (datos, accesorios, computado, reloj, funciones de ciclo de vida, etc.), lo que resultará en una mala legibilidad del código y tendrá que saltar arriba y abajo en la ubicación del archivo. . Vue3 resuelve bien este problema y puede escribir juntos el contenido de la misma lógica. 选项式API(Option API)
组合式API(Composition API)
Además de mejorar la legibilidad y la cohesión del código, la API combinada también proporciona una solución de reutilización lógica relativamente perfecta. Por ejemplo, el ejemplo de las coordenadas públicas del mouse se muestra a continuación.
// main.vue
<template>
<span>mouse position {{x}} {{y}}</span>
</template>
<script setup>
import { ref } from 'vue'
import useMousePosition from './useMousePosition'
const {x, y} = useMousePosition()
}
</script>
// useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'
function useMousePosition() {
let x = ref(0)
let y = ref(0)
function update(e) {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return {
x,
y
}
}
</script>
Resuelve el peligro oculto de los conflictos de nombres en Vue2 , las dependencias no son claras y la configuración entre diferentes componentes no es lo suficientemente flexible. Mixin
Principios de respuesta
Vue2 se basa en el principio de capacidad de Object.defineProperty
respuesta; Vue3 se basa en el principio de capacidad de respuesta Proxy
.
Objeto.defineProperty
Uso básico: defina nuevas propiedades o modifique las propiedades existentes directamente en un objeto y devuelva el objeto.
Consejos: Armonía y Armonía coexisten . writable
value
getter
setter
let obj = {}
let name = '瑾行'
Object.defineProperty(obj, 'name', {
enumerable: true, // 可枚举(是否可通过for...in 或 Object.keys()进行访问)
configurable: true, // 可配置(是否可使用delete删除,是否可再次设置属性)
// value: '', // 任意类型的值,默认undefined
// writable: true, // 可重写
get: function() {
return name
},
set: function(value) {
name = value
}
})
Mueva el código fuente principal de Vue2 y elimínelo ligeramente.
function defineReactive(obj, key, val) {
// 一 key 一个 dep
const dep = new Dep()
// 获取 key 的属性描述符,发现它是不可配置对象的话直接 return
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) { return }
// 获取 getter 和 setter,并获取 val 值
const getter = property && property.get
const setter = property && property.set
if((!getter || setter) && arguments.length === 2) { val = obj[key] }
// 递归处理,保证对象中所有 key 被观察
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// get 劫持 obj[key] 的 进行依赖收集
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if(Dep.target) {
// 依赖收集
dep.depend()
if(childOb) {
// 针对嵌套对象,依赖收集
childOb.dep.depend()
// 触发数组响应式
if(Array.isArray(value)) {
dependArray(value)
}
}
}
}
return value
})
// set 派发更新 obj[key]
set: function reactiveSetter(newVal) {
...
if(setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 新值设置响应式
childOb = observe(val)
// 依赖通知更新
dep.notify()
}
}
Entonces, ¿por qué Vue3 lo abandonó? Eso debe tener algunos defectos.
Motivo principal: no se pueden monitorear elementos nuevos o eliminados de objetos o matrices.
Solución Vue2: piratee el procesamiento para métodos comunes de prototipos de matrices push
, pop
, shift
, unshift
, splice
, , sort
y reverse
proporcione Vue.set
nuevas propiedades para escuchar objetos/matrices. En respuesta a la adición/eliminación new
de un objeto, también se puede crear un nuevo objeto. Al agregar, el nuevo atributo y el objeto anterior se fusionan; al eliminar, el objeto con el atributo eliminado se copia profundamente en el nuevo objeto.
Sugerencias: es posible monitorear los elementos existentes de la matriz, pero la razón por la que Vue2 no lo proporciona es un problema. Para obtener más detalles, consulte el segundo artículo ~. Object.defineOProperty
性能
apoderado
Proxy
handler
Es una nueva característica de ES6, que intercepta el comportamiento del objeto de destino a través del segundo parámetro . Elimina las limitaciones en comparación con Object.defineProperty
la capacidad de respuesta en toda la gama de idiomas. Pero renunció a la compatibilidad (por debajo de IE11)
limitación
-
Agregar y eliminar objetos/matrices. -
Supervise las modificaciones de .length. -
Compatibilidad con mapas, conjuntos, WeakMap y WeakSet.
Uso básico: cree un proxy para que un objeto implemente la intercepción de operaciones básicas y operaciones personalizadas.
const handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : ''
},
set: function() {},
...
}
Mueva el archivo fuente reactive.ts de Vue3
function createReactiveObject(target, isReadOnly, baseHandlers, collectionHandlers, proxyMap) {
...
// collectionHandlers: 处理Map、Set、WeakMap、WeakSet
// baseHandlers: 处理数组、对象
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
Tomando baseHandlers.ts como ejemplo, la razón para usar Reflect.get en su lugar es target[key]
que el parámetro del receptor puede apuntar esto al objeto cuando se llama al getter, no cuando se construye el Proxy.
// 依赖收集
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
...
// 数组类型
const targetIsArray = isArray(target)
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
// 非数组类型
const res = Reflect.get(target, key, receiver);
// 对象递归调用
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
// 派发更新
function createSetter() {
return function set(target: Target, key: string | symbol, value: unknown, receiver: Object) {
value = toRaw(value)
oldValue = target[key]
// 因 ref 数据在 set value 时就已 trigger 依赖了,所以直接赋值 return 即可
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
// 对象是否有 key 有 key set,无 key add
const hadKey = hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
DOM virtual
Vue3 agrega patchFlag
campos al DOM virtual en comparación con Vue2. Echemos un Vue3 Template Explorer
vistazo.
<div id=app>
<h1>技术摸鱼</h1>
<p>今天天气真不错</p>
<div>{{name}}</div>
</div>
La función de renderizado es la siguiente.
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
const _withScopeId = n => (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, 技术摸鱼, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 今天天气真不错, -1 /* HOISTED */))
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(div, _hoisted_1, [
_hoisted_2,
_hoisted_3,
_createElementVNode(div, null, _toDisplayString(_ctx.name), 1 /* TEXT */)
]))
}
Tenga en cuenta que _createElementVNode
el cuarto parámetro del tercero es el patchFlag
tipo de campo, y el tipo de campo es el siguiente. 1 significa que el nodo es un nodo de texto dinámico, luego, en el proceso de diferencias, solo necesita comparar el texto, no necesita prestar atención a la clase, el estilo, etc. Además, se encuentra que todos los nodos estáticos se guardan como una variable 静态提升
, a la que se puede hacer referencia directamente cuando se vuelve a renderizar sin volver a crear.
export const enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 1 << 1, // 动态类名
STYLE = 1 << 2, // 动态样式
PROPS = 1 << 3, // 动态属性,不包含类名和样式
FULL_PROPS = 1 << 4, // 具有动态 key 属性,当 key 改变,需要进行完整的 diff 比较
HYDRATE_EVENTS = 1 << 5, // 带有监听事件的节点
STABLE_FRAGMENT = 1 << 6, // 不会改变子节点顺序的 fragment
KEYED_FRAGMENT = 1 << 7, // 带有 key 属性的 fragment 或部分子节点
UNKEYED_FRAGMENT = 1 << 8, // 子节点没有 key 的fragment
NEED_PATCH = 1 << 9, // 只会进行非 props 的比较
DYNAMIC_SLOTS = 1 << 10, // 动态的插槽
HOISTED = -1, // 静态节点,diff阶段忽略其子节点
BAIL = -2 // 代表 diff 应该结束
}
caché de eventos
Vue3 puede almacenar en caché nuestros eventos después del primer renderizado. Comparado con Vue2, no hay necesidad de pasar una nueva función cada renderizado. Agrega un evento. cacheHandler
click
<div id=app>
<h1>技术摸鱼</h1>
<p>今天天气真不错</p>
<div>{{name}}</div>
<span onCLick=() => {}><span>
</div>
La función de renderizado es la siguiente
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
const _withScopeId = n => (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, 技术摸鱼, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 今天天气真不错, -1 /* HOISTED */))
const _hoisted_4 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(span, { onCLick: () => {} }, [
/*#__PURE__*/_createElementVNode(span)
], -1 /* HOISTED */))
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(div, _hoisted_1, [
_hoisted_2,
_hoisted_3,
_createElementVNode(div, null, _toDisplayString(_ctx.name), 1 /* TEXT */),
_hoisted_4
]))
}
optimización de diferencias
Lleva el código fuente de Vue3 patchChildren. Al combinar lo anterior con el código fuente, patchFlag ayuda a distinguir entre los nodos estáticos y los diferentes tipos de nodos dinámicos cuando se diferencian. Hasta cierto punto, se reduce la alineación del propio nodo y sus atributos.
function patchChildren(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {
// 获取新老孩子节点
const c1 = n1 && n1.children
const c2 = n2.children
const prevShapeFlag = n1 ? n1.shapeFlag : 0
const { patchFlag, shapeFlag } = n2
// 处理 patchFlag 大于 0
if(patchFlag > 0) {
if(patchFlag && PatchFlags.KEYED_FRAGMENT) {
// 存在 key
patchKeyedChildren()
return
} els if(patchFlag && PatchFlags.UNKEYED_FRAGMENT) {
// 不存在 key
patchUnkeyedChildren()
return
}
}
// 匹配是文本节点(静态):移除老节点,设置文本节点
if(shapeFlag && ShapeFlags.TEXT_CHILDREN) {
if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(c1 as VNode[], parentComponent, parentSuspense)
}
if (c2 !== c1) {
hostSetElementText(container, c2 as string)
}
} else {
// 匹配新老 Vnode 是数组,则全量比较;否则移除当前所有的节点
if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense,...)
} else {
unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)
}
} else {
if(prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(container, '')
}
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(c2 as VNodeArrayChildren, container,anchor,parentComponent,...)
}
}
}
}
El código fuente de patchUnkeyedChildren es el siguiente.
function patchUnkeyedChildren(c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {
c1 = c1 || EMPTY_ARR
c2 = c2 || EMPTY_ARR
const oldLength = c1.length
const newLength = c2.length
const commonLength = Math.min(oldLength, newLength)
let i
for(i = 0; i < commonLength; i++) {
// 如果新 Vnode 已经挂载,则直接 clone 一份,否则新建一个节点
const nextChild = (c2[i] = optimized ? cloneIfMounted(c2[i] as Vnode)) : normalizeVnode(c2[i])
patch()
}
if(oldLength > newLength) {
// 移除多余的节点
unmountedChildren()
} else {
// 创建新的节点
mountChildren()
}
}
El código fuente de patchKeyedChildren es el siguiente, que tiene la idea del algoritmo de usar la secuencia creciente más larga.
function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {
let i = 0;
const e1 = c1.length - 1
const e2 = c2.length - 1
const l2 = c2.length
// 从头开始遍历,若新老节点是同一节点,执行 patch 更新差异;否则,跳出循环
while(i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i]
if(isSameVnodeType) {
patch(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)
} else {
break
}
i++
}
// 从尾开始遍历,若新老节点是同一节点,执行 patch 更新差异;否则,跳出循环
while(i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
if(isSameVnodeType) {
patch(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)
} else {
break
}
e1--
e2--
}
// 仅存在需要新增的节点
if(i > e1) {
if(i <= e2) {
const nextPos = e2 + 1
const anchor = nextPos < l2 ? c2[nextPos] : parentAnchor
while(i <= e2) {
patch(null, c2[i], container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)
}
}
}
// 仅存在需要删除的节点
else if(i > e2) {
while(i <= e1) {
unmount(c1[i], parentComponent, parentSuspense, true)
}
}
// 新旧节点均未遍历完
// [i ... e1 + 1]: a b [c d e] f g
// [i ... e2 + 1]: a b [e d c h] f g
// i = 2, e1 = 4, e2 = 5
else {
const s1 = i
const s2 = i
// 缓存新 Vnode 剩余节点 上例即{e: 2, d: 3, c: 4, h: 5}
const keyToNewIndexMap = new Map()
for (i = s2; i <= e2; i++) {
const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i] as VNode)
: normalizeVNode(c2[i]))
if (nextChild.key != null) {
if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
warn(
`Duplicate keys found during update:`,
JSON.stringify(nextChild.key),
`Make sure keys are unique.`
)
}
keyToNewIndexMap.set(nextChild.key, i)
}
}
}
let j = 0
// 记录即将 patch 的 新 Vnode 数量
let patched = 0
// 新 Vnode 剩余节点长度
const toBePatched = e2 - s2 + 1
// 是否移动标识
let moved = false
let maxNewindexSoFar = 0
// 初始化 新老节点的对应关系(用于后续最大递增序列算法)
const newIndexToOldIndexMap = new Array(toBePatched)
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
// 遍历老 Vnode 剩余节点
for (i = s1; i <= e1; i++) {
const prevChild = c1[i]
// 代表当前新 Vnode 都已patch,剩余旧 Vnode 移除即可
if (patched >= toBePatched) {
unmount(prevChild, parentComponent, parentSuspense, true)
continue
}
let newIndex
// 旧 Vnode 存在 key,则从 keyToNewIndexMap 获取
if (prevChild.key != null) {
newIndex = keyToNewIndexMap.get(prevChild.key)
// 旧 Vnode 不存在 key,则遍历新 Vnode 获取
} else {
for (j = s2; j <= e2; j++) {
if (newIndexToOldIndexMap[j - s2] === 0 && isSameVNodeType(prevChild, c2[j] as VNode)){
newIndex = j
break
}
}
}
// 删除、更新节点
// 新 Vnode 没有 当前节点,移除
if (newIndex === undefined) {
unmount(prevChild, parentComponent, parentSuspense, true)
} else {
// 旧 Vnode 的下标位置 + 1,存储到对应 新 Vnode 的 Map 中
// + 1 处理是为了防止数组首位下标是 0 的情况,因为这里的 0 代表需创建新节点
newIndexToOldIndexMap[newIndex - s2] = i + 1
// 若不是连续递增,则代表需要移动
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex
} else {
moved = true
}
patch(prevChild,c2[newIndex],...)
patched++
}
}
// 遍历结束,newIndexToOldIndexMap = {0:5, 1:4, 2:3, 3:0}
// 新建、移动节点
const increasingNewIndexSequence = moved
// 获取最长递增序列
? getSequence(newIndexToOldIndexMap)
: EMPTY_ARR
j = increasingNewIndexSequence.length - 1
for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i
const nextChild = c2[nextIndex] as VNode
const anchor = extIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
// 0 新建 Vnode
if (newIndexToOldIndexMap[i] === 0) {
patch(null,nextChild,...)
} else if (moved) {
// 移动节点
if (j < 0 || i !== increasingNewIndexSequence[j]) {
move(nextChild, container, anchor, MoveType.REORDER)
} else {
j--
}
}
}
}
Optimización de envases
tree-shaking: Conceptos en el empaquetado de módulos
webpack
,rollup
etc. Elimina el código sin referencia del contexto de JavaScript. La dependencia principalimport
y laexport
declaración se utilizan para detectar si un archivo JavaScript exporta, importa y utiliza un módulo de código.
nextTick
Por ejemplo, en Vue2 tree-shaking
puede eliminar incluso si no se usa.
import Vue from 'vue'
Vue.nextTick(() => {
// 一些和DOM有关的东西
})
Las API globales e internas se han refactorizado en Vue3 teniendo tree-shaking
en cuenta el soporte. Por lo tanto, ahora solo se puede acceder a la API global como exportaciones nombradas de compilaciones de módulos ES.
import { nextTick } from 'vue'
nextTick(() => {
// 一些和DOM有关的东西
})
tree-shaking
Con este cambio, las API no utilizadas en la aplicación Vue se eliminarán del paquete final para obtener un tamaño de archivo óptimo , siempre que el enlace del módulo lo admita . Las API globales afectadas por este cambio son las siguientes.
-
Vue.nextTick -
Vue.observable (用 Vue.reactive 替换) -
Vue.version -
Vue.compile (仅全构建) -
Vue.set (仅兼容构建) -
Vue.delete (仅兼容构建)
内部 API 也有诸如 transition、v-model等标签或者指令被命名导出。只有在程序真正使用才会被捆绑打包。
根据 尤大 直播可以知道如今 Vue3 将所有运行功能打包也只有22.5kb
,比 Vue2 轻量很多。
自定义渲染API
Vue3 提供的createApp
默认是将 template 映射成 html。但若想生成canvas
时,就需要使用custom renderer api
自定义render生成函数。
// 自定义runtime-render函数
import { createApp } from './runtime-render'
import App from './src/App'
createApp(App).mount('#app')
TypeScript 支持
Vue3 由TS重写,相对于 Vue2 有更好地TypeScript
支持。
-
Vue2 Option API
中 option 是个简单对象,而TS是一种类型系统,面向对象的语法,不是特别匹配。 -
Vue2 需要 vue-class-component
强化vue原生组件,也需要vue-property-decorator
增加更多结合Vue特性的装饰器,写法比较繁琐。
周边
列举一些 Vue3 配套产物,具体Composition API新语法可见官方迁移文档,参考中有链接~ 。
-
vue-cli 4.5.0
-
Vue Router 4.0
-
Vuex 4.0
-
Element plus
-
Vite
参考
Vue3.0性能优化之重写虚拟Dom[1]
记一次思否问答的问题思考:Vue为什么不能检测数组变动[2]
Vue3 源码解析系列 \- 响应式原理(reactive 篇)[3]
Vue 源码解读(3)—— 响应式原理[4]
Vue 3 Virtual Dom Diff源码阅读[5]
Vue 2 迁移[6]
参考资料
https://blog.csdn.net/summer_zhh/article/details/108080930: https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2Fsummer_zhh%2Farticle%2Fdetails%2F108080930
[2]
https://segmentfault.com/a/1190000015783546: https://link.juejin.cn?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000015783546
[3]
https://zhuanlan.zhihu.com/p/87899787: https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F87899787
[4]
https://juejin.cn/post/6950826293923414047#heading-12: https://juejin.cn/post/6950826293923414047#heading-12
[5]
https://segmentfault.com/a/1190000038654183: https://link.juejin.cn?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000038654183
[6]
https://v3.vuejs.org/guide/migration/migration-build.html#overview: https://link.juejin.cn?target=https%3A%2F%2Fv3.vuejs.org%2Fguide%2Fmigration%2Fmigration-build.html%23overview

每日分享前端插件干货,欢迎关注!
点赞和在看就是最大的支持❤️