Just contact Vue 3, based on Vue 3 X + bootstrap is a small case of a task list. Here, simply sort out the process to deepen understanding

1. Initialize project
>>Run the command at the terminal to initialize the project
npm init vite-app todos
>>Use vscode to open the project and install dependencies
npm install
>>Install dependencies related to less syntax
npm i less -D
2. Sort out the project structure
Initialize index CSS file, create TodoList component and import it into app Vue. The initial project list is as follows

>>Reset index css
:root { font-size: 12px; } body { padding: 8px; }
>>Reset app Vue component
<template> <div> <h1>App Root component</h1> <hr> <todo-list></todo-list> </div> </template> <script> import TodoList from './components/todo-list/TodoList.vue' export default { name: 'MyApp', data() { return { // Task list data todolist: [ { id: 1, task: 'There is a meeting at 9 a.m. on Monday', done: false }, { id: 2, task: 'Dinner at 8 p.m. on Monday', done: false }, { id: 3, task: 'Prepare your speech on Wednesday morning', done: true } ] } }, components: { TodoList } } </script> <style lang="less" scoped> </style>
3. Package todo list component
>>bootstrap based rendering list component
Copy the bootstrap file to the src/assets static resource directory in main Import src/assets / CSS / bootstrap. JS into the portal file CSS style sheet.
import { createApp } from 'vue' import App from './App.vue' // bootstrap import './assets/css/bootstrap.css' import './index.css' createApp(App).mount('#app')
According to the provided by bootstrap List group and check box Render the basic effects of the list component.
<ul class="list-group"> <!-- List group --> <li class="list-group-item d-flex justify-content-between align-items-center"> <!-- check box --> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="customCheck1"> <label class="custom-control-label" for="customCheck1">Check this custom checkbox</label> </div> <!-- logo badge --> <span class="badge badge-success badge-pill">complete</span> <span class="badge badge-danger badge-pill">hang in the air</span> </li> </ul>
>>Declare props attribute for TodeList
In order to accept the list data passed from the outside, the following props attributes need to be declared in the TodoList component:
export default { name: 'TodoList', props: { // List data list: { type: Array, required: true, default: [] } } }
Then, in the App component, transfer the data to the TodoList component through the list attribute:
<todo-list :list="todolist"></todo-list>
>>DOM structure of rendering list
Through the v-for instruction, the DOM structure of the loop rendering list:
<template> <ul class="list-group"> <!-- List group --> <li class="list-group-item d-flex justify-content-between align-items-center" v-for="item in list" :key="item.id"> <!-- check box --> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" :id="item.id"> <label class="custom-control-label" :for="item.id">{{ item.task }}</label> </div> <!-- logo badge --> <span class="badge badge-success badge-pill">complete</span> <span class="badge badge-danger badge-pill">hang in the air</span> </li> </ul> </template>
Render the badge effect on demand through the v-if and v-else instructions (complete / incomplete):
<span class="badge badge-success badge-pill" v-if="item.done">complete</span> <span class="badge badge-danger badge-pill" v-else>hang in the air</span>
Through the v-model instruction, the completion status of two-way binding task:
<input type="checkbox" class="custom-control-input" :id="item.id" v-model="item.done">
Dynamically switch the class name of the element through v-bind attribute binding:
<label class="custom-control-label" :class="item.done ? 'delete':''" :for="item.id">{{ item.task }}</label>
Style declaration in TodoList component:
<style lang="less" scoped> .list-group { width: 400px; // Set fixed width for list } .delete { text-decoration: line-through; color: gray; font-style: italic; } </style>
4. Package todo input component
>>Create and register TodoInput components
Create a new todoinput in the Src / components / todo input / directory Vue component and import it into app Vue component.

>>Component structure of rendering based on bootstrap
According to the provided by bootstrap inline-forms Render the basic structure of the TodoInput component.
<form class="form-inline"> <div class="input-group mb-2 mr-sm-2"> <div class="input-group-prepend"> <div class="input-group-text">task</div> </div> <input type="text" class="form-control" placeholder="Please enter task information" style="with: 356px"> </div> <button type="submit" class="btn btn-primary mb-2">Add new task</button> </form>
>>Pass data outward through custom events
Requirement: in the App component, listen to the custom event of TodoInput component and get the name of the task to be added
Declare the following data in the data of TodoInput component:
data() { return { // New task name taskname: '' } }
Perform v-model bidirectional data binding for the input box in TodoInput component:
<input type="text" class="form-control" placeholder="Please enter task information" style="with: 356px" v-model.trim="taskname">
Listen to the submit event of the form, block the default submission behavior, and specify the event handling function:
<form class="form-inline" @submit.prevent="onFormSubmit">
Declare the onFormSubmit event handler in methods:
emits: ['add'], methods: { onFormSubmit() { // 1. Judgment task name cannot be empty if (!this.taskname) return alert('Task name cannot be empty!') // 2. Trigger the user-defined add event and transfer data to the outside world this.$emit('add', this.taskname) // 3. Clear the text box this.taskname = '' } }
>>Implement the function of adding tasks
On app Listen to the add event defined by TodoInput component in Vue component:
<todo-input @add="onAddNewTask"></todo-input>
On app Declare nextId in the data of Vue component to simulate the operation of id self increment + 1:
data() { return { // Task list data todolist: [ { id: 1, task: 'There is a meeting at 9 a.m. on Monday', done: false }, { id: 2, task: 'Dinner at 8 p.m. on Monday', done: false }, { id: 3, task: 'Prepare your speech on Wednesday morning', done: true } ], // Next available Id nextId: 4 } },
On app The onAddNewTask event handling function declared in the methods of the Vue component is as follows:
methods: { onAddNewTask(taskname) { // Add task information to the task list this.todolist.push({ id: this.nextId, task: taskname, done: false }) // nextId auto increment this.nextId++ } },
5. Package todo button components
>>Create and register TodoButton components
Create a new todobutton in Src / components / todo button / Vue component and import it into app In Vue:

>>Component structure of rendering based on bootstrap
According to the provided by bootstrap Button group Render the basic structure of TodoButton component:
<div class="button-container"> <div class="btn-group"> <button type="button" class="btn btn-primary">whole</button> <button type="button" class="btn btn-secondary">Completed</button> <button type="button" class="btn btn-secondary">hang in the air</button> </div> </div>
BTN primary and BTN secondary are predefined button styles built into Bootstrap. See Buttons
Beautify the style with the class name button container
<style lang="less" scoped> .button-container { width: 400px; text-align: center; } </style>
>>Specify the default active button through props
Declare the following props in the TodoButton component to specify the index of the default activation button:
props: { active: { type: Number, required: true, default: 0 // All buttons are activated by default } }
Control the activation status of the button by dynamically binding the class name:
<div class="button-container"> <div class="btn-group"> <button type="button" class="btn" :class="active === 0 ? 'btn-primary' : 'btn-secondary'">whole</button> <button type="button" class="btn" :class="active === 1 ? 'btn-primary' : 'btn-secondary'">Completed</button> <button type="button" class="btn" :class="active === 2 ? 'btn-primary' : 'btn-secondary'">hang in the air</button> </div> </div>
Declare the index of the default activation item in the App component and pass it to the TodoButton component through attribute binding:
<todo-button class="mt-3" :active="activeBtnIndex"></todo-button>
data() { ... return { activeBtnIndex: 0 } },
>>Update the index of the active item through v-model
Bind the click event handler function to the three buttons in the TodoButton component respectively:
<button type="button" class="btn" :class="active === 0 ? 'btn-primary' : 'btn-secondary'" @click="onBtnClick(0)">whole</button> <button type="button" class="btn" :class="active === 1 ? 'btn-primary' : 'btn-secondary'" @click="onBtnClick(1)">Completed</button> <button type="button" class="btn" :class="active === 2 ? 'btn-primary' : 'btn-secondary'" @click="onBtnClick(2)">hang in the air</button>
The following custom events are declared in the TodoButton component to update the props data passed by the parent component through the v-model instruction:
export default { name: 'TodoButton', emits: ['update:active'], // Declare custom events related to v-model props: { // Index of the active item active: { type: Number, required: true, default: 0 // All buttons are activated by default } }, }
Declare the onBtnClick event handler function in the methods node of the TodoButton component:
methods: { onBtnClick(index) { if (index === this.active) return // The index value remains unchanged. There is no need to transfer data this.$emit('update:active', index) // Trigger a custom event to pass the new index } }
>>Dynamically switch list data by calculating attributes
Requirements: click different buttons to switch and display different list data. The list data to be displayed can be dynamically calculated and returned according to the index of the currently activated button.
Declare the following calculation properties in the App root component:
computed: { // Dynamically calculate the list data to be displayed according to the index of the active button tasklist() { switch (this.activeBtnIndex) { case 0: return this.todolist case 1: return this.todolist.filter(x => x.done === true) case 2: return this.todolist.filter(x => x.done === false) } } },
In the DOM structure of the App root component, modify the: list="todolist" of the TodoList component to the calculation attribute:
<!-- use TodoList assembly --> <todo-list :list="tasklist" class="mt-2"></todo-list>