New Features of Vue3
About 2042 wordsAbout 7 min
Vue3Review
2025-01-18
Composition API
Basic Usage of setup()
Initialize data and logic when the component is created. It is called before the component instance is created, so it cannot directly access this.
Parameters:
- props: Properties passed from the parent component.
- context: Contains the context object of the component, including methods like emit to trigger custom events.
Lifecycle hooks can be defined in setup by importing the hook functions from Vue and then calling them within setup. The usage of Vue2 version is still supported.
// Vue3 version
import { onMounted } from 'vue';
export default {
setup() {
onMounted(() => {
// Your code
});
}
// ...
}
// Vue2 version
export default {
setup() {}
mounted() {
// Your code
}
// ...
}
Reactive API
The reactivity system in Vue 3 is rebuilt based on Proxy (Vue 2 uses Object.defineProperty).
The reactivity system in Vue 3 relies on the following three main functions:
reactive: Create reactive objects.
- Advantage: Deep proxy, properties in nested objects also become reactive.
- Note: reactive returns a Proxy, not the original object.
ref: Create reactive basic data types (primitive values like numbers, strings, booleans, etc.).
- value property: When using ref, you must access or modify its value through .value.
- Binding with DOM: In Vue templates, ref is automatically unpacked, no need to manually use .value.
computed: Create dynamically computed values based on other reactive data.
- Caching: Computed properties are only recalculated when the dependent data changes.
- Read-only: By default, computed properties are read-only, but writable computed properties can be defined using set.
[!NOTE]{#writable_computed} Writable computed properties can be defined by passing an object with set and get methods to the computed function. When the computed property value is modified, the set method is triggered. If the variables depended on in get are modified, the places depending on the computed property will be updated
Usage and Practical Scenarios of computed and watch
computed and watch are two core features for handling reactive data, each with its own applicable scenarios and usage.
- Usage of computed
computed properties are used to define derived values that depend on other data and have caching characteristics. They are only recalculated when their dependent reactive data changes. Suitable for pure function calculations.
- Usage of watch
watch is used to listen for changes in reactive data and execute specific logic. It is suitable for handling asynchronous operations, side effects, or complex logic.
Feature | computed | watch |
---|---|---|
Caching | Yes, returns cached value if dependent data hasn't changed | No, callback is executed every time it is triggered |
Suitable for handling side effects | No, suitable for pure function calculations | Yes, suitable for handling asynchronous or side effect logic |
Applicable scenarios | Derived values that depend on data and need caching | Listening for data changes and executing side effects or complex logic |
Provider/Inject for Cross-Level Component Communication
provide and inject are a pair of features for cross-level component communication. They allow data to be provided in ancestor components (via provide) and accessed in descendant components (via inject), even if these components are separated by multiple levels.
// Ancestor component
export default {
provide() {
return {
sharedValue: 'Hello from ancestor!',
};
},
};
// Descendant component
export default {
inject: ['sharedValue'],
mounted() {
console.log(this.sharedValue); // Output: Hello from ancestor!
},
};
provide supports reactive data. If the provided value is a reactive object, descendant components will automatically sense changes. provide and inject are a lightweight state-sharing mechanism, suitable for handling specific context data rather than replacing global state management tools.
lifecycle hooks
Details reference
script setup Syntax Sugar and Its Advantages
<script setup>
is a syntax sugar that further simplifies the writing of components while improving performance. It combines the functionality of the Composition API and the setup function, making the code more concise and intuitive.
More concise code
Better performance
<script setup>
is parsed at compile time, not runtime. This means Vue can more efficiently optimize component initialization and scope management.Clearer scope
Variables and methods in<script setup>
are by default visible to the template, no need to expose them through return.Better type inference support (TS integration)
Directly use TypeScript in<script setup>
without additional configuration. The types of variables and props are automatically inferred or defined, reducing the complexity of type definitions.Supports multiple new features
- Top-level defineProps and defineEmits: Used to define the props and emits of the component, making the syntax simpler.
<script setup> defineProps({ name: String }); defineEmits(['submit']); const handleClick = () => emit('submit'); </script>
- useSlots and useAttrs: Directly access slots and attributes without going through the setup parameters.
<script setup> import { useSlots, useAttrs } from 'vue'; const slots = useSlots(); // slots is an object where each key corresponds to a slot name, and the value is a function to render the slot content. const attrs = useAttrs(); // attrs is a reactive object containing all bound attributes that are not explicitly declared as props. </script> <template> <div> <h2>Child Component</h2> <!-- Render default slot --> <div v-if="slots.default"> <slot /> </div> </div> </template>
- Supports multiple
<script>
tags
<script> // General logic or export content export const globalVar = "Hello!"; </script> <script setup> import { ref } from 'vue'; const count = ref(0); </script>
Faster Performance
Virtual DOM
Virtual DOM Update Process
- Data change triggers re-render: When reactive data changes, Vue triggers the component's render function.
- Generate new virtual DOM: The render function is called, generating a new virtual DOM.
- Diff algorithm comparison: Vue compares the new and old virtual DOM trees to find the differences (diff).
- Update real DOM: Vue applies the diff results to the real DOM, updating only the necessary parts.
Advantages of Virtual DOM
- Better performance
- Cross-platform support
- Predictable updates: Data-driven virtual DOM update logic is more predictable than manual DOM manipulation.
Limitations of Virtual DOM
Building and comparing virtual DOM brings additional performance overhead.
Mark Static Nodes at Compile Time to Reduce Update Overhead
Static nodes are only created during the initial render and are not regenerated in subsequent updates.
More Flexible Components
Custom Renderer
Vue 3 provides a custom renderer feature, allowing developers to apply Vue's reactivity and component system to non-browser environments, such as:
- Server-side rendering (SSR)
- Native application development (e.g., Weex, UniApp, Electron)
- Rendering to Canvas, WebGL, terminal, or other custom environments.
Custom renderer is an extension mechanism in Vue 3 that allows developers to define custom DOM operation implementations, rendering Vue's templates and components to any environment.
Vue's core renderer encapsulates DOM operations for the browser, while the custom renderer provides a set of general interfaces that can replace these default operation logics.
createRenderer
createRenderer is the core function for creating custom renderers. It receives a renderer options object as the rendering options, defining how to operate nodes in the target platform.
import { createRenderer } from '@vue/runtime-core';
const rendererOptions = {
// Implement renderer options
};
// Create custom renderer
const { createApp } = createRenderer(rendererOptions);
// Create application
const App = {
render() {
return {
type: 'rect',
props: { x: 10, y: 20, width: 100, height: 50, fill: 'red' },
};
},
};
const canvasRoot = {
type: 'canvas', // Target terminal type
children: []
};
createApp(App).mount(canvasRoot);
Renderer Options
rendererOptions is the key part of the custom renderer, requiring the user to define the corresponding operation methods based on the target environment. For example:
- createElement: How to create elements
- insert: How to insert elements
- patchProp: How to update set properties
- remove: How to remove elements
- setElementText: How to set text content
Rendering Logic
- Vue's reactivity system drives component updates.
- During template compilation, Vue generates the corresponding render function, returning the virtual DOM.
- The custom renderer uses rendererOptions to map the virtual DOM to the target platform.
Fragment Allows Components to Return Multiple Root Nodes.
Teleport Renders Child Components to Other Locations in the DOM.
Teleport moves its child component content to the target location by specifying the target DOM node (to attribute).
<template>
<div>
<button @click="showModal = true">Open Modal</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<p>This is a modal rendered in the body element.</p>
<button @click="showModal = false">Close</button>
</div>
</Teleport>
</div>
</template>
The value of to can be dynamically changed to move the content to different targets.
Suspense Handles Asynchronous Component Loading
Suspense is a new component provided by Vue 3 for gracefully handling asynchronous component loading or asynchronous operations. It allows displaying a placeholder content before the asynchronous task is completed and switching to the real content after the task is completed.
<template>
<Suspense>
<!-- Asynchronous content -->
<template #default>
<AsyncComponent />
</template>
<!-- Placeholder content -->
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
</template>
Core Concepts
Placeholder Content
Use the #fallback slot to provide placeholder content during asynchronous loading. Commonly used for loading indicators (e.g., "Loading..." or progress bars).
Default Content
Use the #default slot to define the actual asynchronous content to be rendered. Asynchronous content can be asynchronous components or regular components containing asynchronous logic.
Support for Asynchronous Logic
The core capability of Suspense comes from its support for asynchronous components or asynchronous setup functions.
Lifecycle of Suspense
- onPending: Triggered when asynchronous content starts loading.
- onResolve: Triggered when all asynchronous tasks are completed.
Note
- In Vue components with setup mode, you can directly write async/await asynchronous logic. If await operation is used, the component setup actually returns a Promise.
- The Suspense component collects all child components that return Promises and maintains the corresponding queue. By monitoring the status of these Promises, it can determine whether the child components have finished loading.
- The execution of the component setup occurs before the onMounted event.
Other Improvements
Tree-shaking
Tree-shaking is an optimization technique used by modern JavaScript bundlers (such as Webpack, Rollup, Vite, etc.) to remove unused code, thereby reducing the size of the bundled files and improving application performance.
Basic Principle
The core of Tree-shaking is based on the following two points:
- Static structure of ES Module
import and export are key features of ES Module, with static analysis capabilities. The bundler can determine which modules are used and which are not during the build process.
Enablement
- Write code using the ES Module specification.
- Configure the bundler.
- Webpack: Ensure mode is set to production, and Tree-shaking will be automatically enabled.
- Rollup: Naturally supports Tree-shaking and will remove unused code by default.
- Vite: Based on Rollup, supports Tree-shaking by default without additional configuration.
Notes and Limitations
Side Effects Declaration
If a module has side effects (e.g., modifying global variables), use the sideEffects configuration in package.json to inform the bundler which files cannot be removed.
Dynamic Imports
Tree-shaking has limited support for dynamic imports.
Third-Party Libraries
Some older third-party libraries do not use ES Module or do not correctly declare sideEffects, which may hinder Tree-shaking. For how to support non-ESM modules in Vite, please reference
Scope
Tree-shaking does not globally optimize all code in the project but performs analysis based on the module scope.
emits Option for Defining Events
The defineEmits method in setup mode is used to define events. In fact, passing callback parameters can also define events. The difference between the two lies in:
- Semantic Clarity: defineEmits is more semantically clear when defining events, indicating that the component will trigger certain events. This makes the component's behavior clearer and highly integrated with Vue's event system.
- Flexibility: Passing callback parameters is more flexible because communication between parent and child components does not rely on Vue's event mechanism. The parent component directly controls the passing and execution order of callback functions. However, this approach may lose some traceability and structure of events, especially in more complex component trees.
- Usage: defineEmits is suitable for standard event communication between components (e.g., click, submit, update data). Passing callbacks is more suitable for the parent component to directly control the behavior of the child component (e.g., the parent component controls the execution timing of certain methods in the child component).
New v-model Syntax, Supports Multiple Bindings.
Details reference