Vue3 System Learning#
1. Vue3 Basics#
1.1 Loop Rendering#
<div v-for="item in items" :key="item.id">
<!-- Content -->
</div>
Let's talk about key, why add key?
Key is used to manage state, which means you are binding a list, an array with v-for.
In default mode, when you do not add key, if the order of your data changes, Vue will not move the order of the DOM flow, but will update each element again.
However, if the output of your list rendering depends on the state of child components or temporary DOM states (such as form input values),
to give Vue a hint so that it can track the identity of each node, allowing it to reuse and reorder existing elements, you need to provide a unique key attribute for each corresponding block of elements.
1.2 Event Handling#
The value of an event handler can be:
- Inline event handler: Inline JavaScript statements executed when the event is triggered (similar to onclick).
- Method event handler: A property name or path pointing to a method defined on the component.
What are inline event handlers and method event handlers?#
Inline event handlers and method event handlers are the two main ways to handle HTML element events in JavaScript.
Inline Event Handlers
Inline event handlers are specified directly within the HTML element's tag. When a user interacts with that element (such as clicking), the corresponding event is triggered. A typical example of an inline event handler is the onclick
attribute, which can be set directly in the element's HTML tag. For example:
<button onclick="alert('You clicked the button!')">Click me</button>
In this code, the onclick
attribute is an inline event handler that defines the JavaScript code to be executed when the button is clicked.
Method Event Handlers
Method event handlers bind event handling functions to elements through JavaScript code. This approach is usually more flexible and easier to manage, especially for complex applications. You can use the addEventListener
method in JavaScript to add event handlers to elements. For example:
const name = ref('Vue.js')
function greet(event) {
alert(`Hello ${name.value}!`)
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
<!-- `greet` is the method name defined above -->
<button @click="greet">Greet</button>
This code first retrieves the element with the ID myButton
using getElementById
, then uses addEventListener
to add a click event handler to that element. When the button is clicked, an alert box will pop up.
In summary, inline event handlers are easy to implement quickly but hard to maintain, especially in larger projects; while method event handlers require writing more JavaScript code but provide greater flexibility and maintainability.
1.3 Form Input Binding#
v-model binds values, strings, booleans, arrays, objects, custom components, etc.
- Text
- String format
- Multi-line text
- String format
- Checkbox
- Binds a single boolean value
- Multiple checkboxes
- Multiple checkboxes bound to the same array or collection of values
<div>Checked names: {{ checkedNames }}</div> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label>
- Radio buttons
- Binds to a single value
<div>Picked: {{ picked }}</div> <input type="radio" id="one" value="One" v-model="picked" /> <label for="one">One</label> <input type="radio" id="two" value="Two" v-model="picked" /> <label for="two">Two</label>
- Selectors
- If the initial value of the v-model expression does not match any of the options, the
<select>
element will render as "not selected." On iOS, this will prevent users from selecting the first item, as iOS does not trigger a change event in this case. Therefore, we recommend providing a disabled option with an empty value, as shown in the example above.<select v-model="selected"> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select> <span>Selected: {{ selected }}</span>
- If the initial value of the v-model expression does not match any of the options, the
2. Component System Learning#
2.1 Component Data Transmission#
There are two ways to pass data between components:
- Passing data from parent component to child component
Achieved through defineProps:// In the parent component <template> <ChildComponent :title="title" /> </template> <script> import ChildComponent from './ChildComponent.vue' </script>
// In the child component <script setup> import { defineProps } from 'vue' const props = defineProps({ title: String }) </script>
- Passing data from child component to parent component
- Passing data to the parent component through custom events
In Vue 3, child components can pass data to parent components through custom events. This process typically involves the following steps:
- Passing data to the parent component through custom events
-
In the child component: Use the
emit
method to trigger a custom event and pass data as a parameter.```vue // In the child component <!-- BlogPost.vue, <script> omitted --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">Enlarge text</button> </div> </template> ```
In Vue, custom components can transmit data through events. In your example, the component triggers an enlarge-text
event using the $emit
method. If you need to pass data to the parent component while triggering the event, you can pass the data as the second parameter of the $emit
method.
Here’s how to modify your component template to carry data when triggering the enlarge-text
event:
<template>
<div class="blog-post">
<h4>{{ title }}</h4>
<!-- Add data as the second parameter of $emit -->
<button @click="$emit('enlarge-text', someData)">Enlarge text</button>
</div>
</template>
-
In the parent component: Listen for this custom event and receive data through the event handler function.
```vue <BlogPost ... @enlarge-text="postFontSize += 0.1" /> ```
<!-- Parent component template --> <template> <div> <your-component @enlarge-text="handleEnlargeText"></your-component> </div> </template> <script> export default { methods: { // Event handler method receives the passed data as a parameter handleEnlargeText(data) { // Handle the received data here console.log(data); } } } </script>
With the listener @enlarge-text="postFontSize += 0.1", the parent component will receive this event and update the value of postFontSize.
In short, in Vue, the key is to associate **custom events** and **actions** through emit, and then in the parent component, **listen to this custom** event with @ and handle the data **in the listened event**.
So if you want to trigger a component in Vue, use $emit to define a component trigger.
- Passing data to the parent component through two-way binding
```vue
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>parent bound v-model is: {{ model }}</div>
</template>
<!-- Parent.vue -->
<Child v-model="count" />
The value returned by defineModel() is a ref. It can be accessed and modified like other refs, but it serves to create a two-way binding between the parent component and the current variable:
Its .value synchronizes with the value of the parent component's v-model;
When it is changed by the child component, it triggers the update of the bound value in the parent component.
v-model accepts a parameter
The v-model on the component can also accept a parameter:
<UserName
v-model:first-name="first"
v-model:last-name="last"/>
<MyComponent v-model:title="bookTitle" />
In the child component, we can support the corresponding parameters by passing a string as the first parameter to defineModel():
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
2.2 Component Event Learning#
- Binding events through the $emit method, the following binds the button's touch to the someEvent event.
<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>
The parent component listens for the event through @/v-on
<MyComponent @some-event="callback" />
<MyComponent @some-event.once="callback" />
Through this parent component's listener, the child component can trigger the parent's callback function.
2. If I want this component to trigger the parent component's callback with data, how can I do that?
<button @click="$emit('increaseBy', 1)">
Increase by 1
</button>
<MyButton @increase-by="(n) => count += n" />
or
<MyButton @increase-by="increaseCount" />
function increaseCount(n) {
count.value += n
}
This means that the second parameter of emit is the value that will be carried by its callback, which we can accept using a function parameter; the first method is an arrow function, and the second is a component method.
All additional parameters passed to $emit() will be directly passed to the listener. For example, when $emit('foo', 1, 2, 3) is triggered, the listener function will receive these three parameter values.
Then we can use the three parameters to accept the callback it passes in.
- Now I want this event to be triggered only by click, but I want to implement a condition check through JS. If the condition is met, how can I trigger this event?
In the child component, define defineEmits() to declare the event I want to trigger, and then after declaring it, I can trigger it through emit.
<script setup>
const emit = defineEmits(['inFocus', 'submit'])
function buttonClick(){
emit('submit')
}
</script>
- OK, that basically clarifies everything. In Vue's template, to trigger it is
@click='$emit('name', parameter)'
, while after defineEmit, using emit('name') is to trigger it in JS.
2.3 Props Component Data Transmission#
Props are the solution for passing data from parent components to child components. Its role is to explicitly declare the props it accepts.
- How to pass in props?
First, you should declare in the child component, which means stating what data you need to accept. Declaration can be done through an array or an object.
<script setup>
const props = defineProps(['foo'])
const props = defineProps({
title: String,
likes: Number
})
console.log(props.foo)
</script>
If the parent component wants to transmit data, it can do so:
<BlogPost title="My journey with Vue" likes="2"/>
<BlogPost title="Blogging with Vue" likes="2" />
<BlogPost title="Why Vue is so fun" likes="2" />
- Can the value of the data passed in be dynamic, meaning the value given by the parent component is changing?
<!-- Dynamically pass in based on a variable's value -->
<BlogPost :title="post.title" />
<!-- Dynamically pass in based on a more complex expression's value -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
Through this method, a dynamic value can be passed in, and when this value changes, the child component will also change.
3. Passing different types of values, the value can be number, boolean, array, etc., but based on its syntax, it can sometimes be counterintuitive.
```vue
Passing in a Number type value
<!-- Dynamically pass in based on a variable's value -->
<BlogPost :likes="post.likes" />
Passing in a boolean value
<!-- Just writing the prop without passing a value will implicitly convert to `true` -->
<BlogPost is-published />
<!-- Although `false` is a static value, we still need to use v-bind -->
<!-- Because this is a JavaScript expression and not a string -->
<BlogPost :is-published="false" />
<!-- Dynamically pass in based on a variable's value -->
<BlogPost :is-published="post.isPublished" />
Passing in an Array value
<!-- Although this array is a constant, we still need to use v-bind -->
<!-- Because this is a JavaScript expression and not a string -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Dynamically pass in based on a variable's value -->
<BlogPost :comment-ids="post.commentIds" />
Passing in an Object value
<!-- Although this object literal is a constant, we still need to use v-bind -->
<!-- Because this is a JavaScript expression and not a string -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Dynamically pass in based on a variable's value -->
<BlogPost :author="post.author" />
```
In short, if you want to express a string, use the props attribute; if you want to express an expression, use the :props attribute.
4. Props are a one-way data flow, meaning data can only be passed from parent components to child components, and child components cannot pass data back to parent components through props.
However, many times we actually want to change the data of this props, for example, I want to obtain a local data situation of props / I want to transform the data of props.
2.4 v-model Component Data Transmission#
v-model is a syntactic sugar that allows us to implement two-way binding more conveniently.
- How to use v-model
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>parent bound v-model is: {{ model }}</div>
</template>
<!-- Parent.vue -->
<Child v-model="count" />
The value returned by defineModel() is a ref. It can be accessed and modified like other refs, but it serves to create a two-way binding between the parent component and the current variable:
Its .value synchronizes with the value of the parent component's v-model;
When it is changed by the child component, it triggers the update of the bound value in the parent component.
2. v-model accepts a parameter
The v-model on the parent component can also accept a parameter:
<UserName
v-model:first-name="first"
v-model:last-name="last"/>
<MyComponent v-model:title="bookTitle" />
In the child component, we can support the corresponding parameters by passing a string as the first parameter to defineModel():
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
- v-model modifiers
- .lazy
By default, v-model updates data after each input event (except for IME spelling stage states). You can add the lazy modifier to update data after each change event:
<!-- Synchronize updates after the "change" event instead of "input" -->
<input v-model.lazy="msg" />
- .number
If you want user input to be automatically converted to a number, you can add the .number modifier after v-model to manage the input:
<input v-model.number="age" />
If the value cannot be processed by parseFloat(), the original value will be returned. The number modifier will be automatically enabled when the input box has type="number".
3. .trim
If you want to automatically filter leading and trailing whitespace characters from user input, you can add the trim modifier to v-model:
vue <input v-model.trim="msg" />
2.5 Attribute Passing#
-
Attribute Inheritance
Attribute passing refers to passing the attributes of the parent component to the child component. If this attribute is not declared as props and emits, then this attribute will be passed to the child component.<Child id="foo" class="bar" />
The most common examples are class, style, and id. When a component is rendered with a single element as the root, the passed attributes will automatically be added to the root element. For example, if we have a component whose template looks like this:
<!-- Template of <MyButton> --> <button>click me</button> A parent component uses this component and passes in a class: <MyButton class="large" /> The final rendered DOM result is: <button class="large">click me</button>
In simple terms, if you add attributes outside the component that are neither props nor emits, this attribute will automatically load to the outermost data inside the component.
It can generally be used for merging class and style, meaning if you write a class outside, it will also merge inside. -
The basic v-on listener means you can add event listeners to the component and then trigger this event within the component.
<Child v-on:custom-event="handleCustomEvent" />
The click listener will be added to the
<Child>
root element, which is the native<button>
element. When the native<button>
is clicked, it will trigger the parent's onClick method.
Similarly, if the nativebutton
element itself also binds an event listener through v-on, both this listener and the inherited listener from the parent component will be triggered.
Therefore, this listener can bridge the connection between child components and parent components. -
Passing through has continuity
It means that one can pass through another, A passes to B, B passes to C can be passed continuously.