// Vue3 中的组件
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
return {
count,
increment: () => { count.value++ },
};
},
};
</script>
// Vue2 中的组件
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
},
};
</script>
Vue3中取消了beforeCreated和created,添加了setup函数,其负责设置组件的初始状态、响应式数据和执行其他逻辑。它在组件实例被创建之前被调用,并且接收两个参数:props
和 context
:
props
参数表示组件接收到的属性(props
),并且是只读的。context
参数包含了一些常用的属性和方法,如 attrs
、slots
、emit
等。由于setup就是初始化时执行的函数,所以可以替代beforeCreated和created的效用
常用生命周期对比:
vue2 | vue3 |
---|---|
beforeCreate | |
created | |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
并且再Vue3中提供了onServerPrefetch钩子,若在其中返回了一个Promise,服务端就可以等待Promise完成后再对该组件进行渲染
Vue.component
变成了 app.component
。// Vue3 中的全局组件注册
const app = createApp(App);
app.component('my-component', MyComponent);
app.mount('#app');
// Vue2 中的全局组件注册
Vue.component('my-component', MyComponent);
new Vue({
el: '#app',
render: h => h(App),
});
传统的 Vue 组件会被渲染到其父组件的 DOM 结构内,但是有时我们希望将组件渲染到 DOM 结构的不同位置,而不需要改变其父组件的布局。这就是 Teleport
的作用所在。
<!-- Vue3 中的 Teleport -->
<teleport to="body">
<Modal />
</teleport>
Teleport
组件将 Modal
组件渲染到了页面的 body
元素下。无论在组件内部的哪个位置使用 Teleport
,最终 Modal
组件都会被渲染到 body
元素下,而不是组件的父容器内部。
通过使用 Teleport
,你可以将组件渲染到任何合法的 DOM 元素位置,而不局限于其父组件的 DOM 结构。这对于实现全局的弹窗、模态框或通知等功能非常有用。
需要注意的是,Teleport
需要配合 to
属性来指定组件渲染的目标位置,该位置可以是一个有效的 CSS 选择器,或者是特殊的预定义值,如 "body"
、"#app"
等。
另外,需要确保目标位置在组件被挂载的时候是存在的,否则 Teleport
会抛出警告并在渲染期间失效。
总结来说,Teleport
组件使你能够非常灵活地将组件渲染到任意位置,而不受其父组件的限制。这为处理全局弹窗、模态框等提供了方便的解决方案。
fragment
组件把这个多根组件的结构给包起来,相当于这个组件还是只有一个根节点<!-- Vue3 中的多个根节点 -->
<template>
<div></div>
<div></div>
</template>
<!-- Vue2 中只能有一个根节点 -->
<template>
<div>
<p></p>
</div>
</template>
Suspense
组件,用于处理异步组件的加载状态,使得在异步操作完成之前可以展示备用内容。在 Vue 3 中,异步组件加载可以使用 Suspense
组件和 async setup
功能来实现。Suspense
组件可以用于处理异步组件的加载和显示占位符内容,而 async setup
可以在异步组件加载完成后执行相关逻辑。
下面是使用 Suspense
和 async setup
的示例:
首先,在父组件中使用 Suspense
包裹需要异步加载的子组件,并设置一个占位符(fallback)内容:
<template>
<Suspense>
<template #default>
<!-- 异步加载的子组件 -->
<AsyncComponent />
</template>
<template #fallback>
<!-- 加载时的占位符内容 -->
<div>Loading...</div>
</template>
</Suspense>
</template>
然后,在异步加载的子组件中使用 async setup
来定义组件的逻辑:
export default {
async setup() {
// 异步加载资源或执行其他异步操作
await someAsyncOperation();
// 返回组件的响应式数据和方法
return {
data: reactive({}),
method() {
// ...
}
};
}
};
</script>
当父组件渲染时,Suspense
组件会检测到子组件的异步加载,并显示占位符内容(“Loading…”)。一旦异步加载完成,Suspense
组件会自动切换到子组件的内容,并显示子组件的内容。
通过使用 Suspense
和 async setup
,你可以更方便地处理异步组件加载过程中的状态,并在加载完成后执行相关逻辑。
reactive
和 ref
函数,使得在应用程序中管理全局状态变得更加简单。新的响应式系统使得 Vuex(Vue 的状态管理库)的需求降低。// Vue3 中的全局状态管理
import { reactive } from 'vue';
const state = reactive({
count: 0,
});
beforeMount
替代了 Vue2 中的 bind
和 inserted
钩子。bind
和 inserted
两个钩子。// Vue3 中的自定义指令
app.directive('自定义指令', {
beforeMount(el, binding) {
// ...
},
});
// Vue2 中的自定义指令
Vue.directive('自定义指令', {
bind(el, binding) {
// ...
},
inserted(el, binding) {
// ...
},
});
// Vue3 中的组件使用 Typescript
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
data() {
return {
count: ref(0),
};
},
});
</script>
// Vue3 中的事件处理
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
methods: {
handleClick(event,other) {
// event 是事件对象
},
},
};
</script>
// Vue2 中的事件处理
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
methods: {
handleClick(customArg, event) {
// event 是事件对象
},
},
};
</script>
// Vue3 中的 TSX 支持
<script lang="tsx">
import { defineComponent } from 'vue';
export default defineComponent({
render() {
return <div>Hello, TSX!</div>;
},
});
</script>
emits
选项,用于声明组件可以触发的事件,提高了事件的类型安全性。// Vue3 中的 emits 选项
<script>
export default {
emits: ['custom-event'],
methods: {
handleClick() {
this.$emit('custom-event', /* data */);
},
},
};
</script>
v-model
进行了改进,允许你自定义属性和事件,使得在使用 v-model
时更加灵活。v-model
的定制性相对较低。<!-- Vue3 中的 v-model 定制 -->
<script>
export default {
model: {
prop: 'checkedValue',
event: 'change',
},
props: {
checkedValue: Boolean,
},
};
</script>
Tree-shaking:模块打包 webpack、rollup 等中的概念。移除 JavaScript 上下文中未引用的代码。主要依赖于 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。
以 nextTick 为例子,在 Vue2 中,全局API暴露在Vue实例上,即使未使用,也无法通过 tree-shaking 进行消除。
import Vue from 'vue';
Vue.nextTick(() => {
// 一些和DOM有关的东西
});
vue3:
import { nextTick } from 'vue'; // 显式导入
nextTick(() => {
// 一些和DOM有关的东西
});
通过这一更改,只要模块绑定器支持 tree-shaking,则Vue应用程序中未使用的 api 将从最终的捆绑包中消除,获得最佳文件大小。
受此更改影响的全局API如下所示。
内部API也有诸如 transition、v-model 等标签或者指令被命名导出。只有在程序真正使用才会被捆绑打包。Vue3 将所有运行功能打包也只有约22.5kb,比 Vue2 轻量很多。
// Vue3 中的动态注入
// Parent.vue
<script>
import { provide } from 'vue';
export default {
setup() {
provide('message', 'Hello from parent!');
},
};
</script>
// Child.vue
<script>
import { inject } from 'vue';
export default {
setup() {
const message = inject('message');
// ...
},
};
</script>
$on
, $off
, $once
等方法被弃用,使用 on
, off
, once
替代。// Vue3 中的事件处理
const emitter = createEmitter();
const off = emitter.on('event', () => {
// ...
});
// 取消监听事件
off();
createRenderer
函数,使得可以更方便地在不同平台上进行渲染。// Vue3 中的自定义渲染器 API
import { createRenderer } from 'vue';
const renderer = createRenderer({
// ...
});
patchFlag
字段是一个位掩码,通过对不同位进行组合来表示不同的变化类型。它可以帮助 Vue 3 在进行 diff 运算时更高效地定位和处理需要更新的部分,避免不必要的比较和操作,从而提升性能。
具体作用如下:
patchFlag
可以告诉 Vue 3 虚拟 DOM 的变化类型,包括节点的类型、文本内容、元素的动态属性等。通过检查 patchFlag
的不同位,可以快速判断节点类型是否发生了变化,避免进行不必要的深度比较。patchFlag
值,可以将节点的比较和更新粒度调整得更加准确。例如,如果只有属性发生了变化,可以仅针对属性进行比较,而无需再对子节点进行深度比较。patchFlag
标记节点的变化,Vue 3 可以针对性地处理不同类型的更新。例如,当一个组件的 props
发生变化时,只需针对 props
进行更新,而无需重新渲染整个组件。类型列举:
// patchFlags 字段类型列举
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 应该结束
}
errorCaptured
生命周期钩子,用于捕获子组件的错误,并提供了更好的错误处理机制。// Vue3 中的错误处理
<script>
export default {
errorCaptured(err, vm, info) {
// 处理错误
},
};
</script>
Vue3 的cacheHandler
可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。
<div id="app">
<h1>vue3事件缓存</h1>
<p>这是一个p标签</p>
<div>{{name}}</div>
<span onCLick=() => {}><span>
</div>
渲染函数如下所示。
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, vue3事件缓存, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 这是一个p标签, -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
]))
}
观察以上渲染函数,你会发现 click 事件节点为静态节点(HOISTED 为 -1),即不需要每次重新渲染。
Proxy与Object.defineProperty:
Vue 2: Vue 2使用Object.defineProperty
来实现响应式。该方法通过劫持对象的get
和set
操作,在数据发生变化时触发视图更新。但这种方式存在一些限制,例如不能监听数组的变化。
Vue 3: Vue 3采用了ES6中引入的Proxy
对象。Proxy
提供了更强大和灵活的拦截器,可以覆盖目标对象的各种操作,包括get
、set
、delete
等。这使得Vue 3的响应式系统更加强大且性能更优。
Proxy 对象:Vue 3 使用了 ES6 中的 Proxy 对象来实现响应式。Proxy 对象可以拦截 JavaScript 对象上的各种操作,比如读取属性、设置属性、删除属性等。通过使用 Proxy 对象,Vue 3 可以在属性发生变化时捕获到对应的操作,并触发更新。
依赖追踪:Vue 3 使用了依赖追踪的机制来追踪属性之间的依赖关系。每个响应式对象都有一个关联的依赖追踪器,当访问响应式对象的某个属性时,会将当前的依赖关系添加到依赖追踪器中。这样,在属性发生变化时,可以找到所有依赖于该属性的地方,并触发相应的更新。
更高效的触发更新机制:
Watcher
实例,用于追踪数据的变化。当数据变化时,会触发Watcher
,进而导致视图更新。scheduler
的新的更新机制。通过scheduler
,Vue 3可以更灵活地控制何时执行更新,以提高性能。这种机制使得Vue 3在处理大规模数据变化时更加高效。组合式API的影响:
Tree-shakable: