3.3Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3框架-企业级应用-Vue组合式API

news2024/12/25 12:49:31

为什么要使用Composition API

一个Options API实例

在前面的课程中,我们都是采用 Options API(基于选项的 API ) 来写一个组件的。下面是一个实例:

<template> 
Count is: {{ count }}, doubleCount is: {{ doubleCount }} 
<button @click="add">加</button> 
</template> 
<script> 
export default { 
data() { 
return { 
count: 0, 
}; 
}, 
computed: { 
doubleCount() { 
return this.count * 2; 
}, 
}, 
methods: { 
add() { 
this.count++; 
} 
} 
} 
</script>

当要去理解一个组件时,我们更加关心的是:“这个组件是要干什么(即代码背后的意图)”,而不是:“这个组件用到了什么选项”。

Options API 撰写出来的代码自然采用了后者的表述方式,然而对前者的表述并不好。

Options API存在的问题

在 Options API 中实际上形成了一种强制的约定:

props 里面设置接收参数

data 里面设置变量

computed 里面设置计算属性

watch 里面设置监听属性

methods 里面设置事件方法

我们会发现: Options API 都约定了我们该在哪个位置做什么事,这在一定程度上也强制我们进行了代码分割。这就为展示背后的逻辑关注点设置了障碍。我们必须不断地在选项代码块之间“跳转”,以找到与该关注点相关的部分。

尤其是在大型组件中,数据与方法会很多,而数据与其相关联的方法就会被其他数据和方法分隔的很远,往往很难被看出它们之间的关联。

这是一个大型组件的示例,其中逻辑关注点是按颜色分组。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果我们能够将与同一个逻辑关注点相关的代码配置在一起,这样会更好。而这正是组合式 API 使我们能够做到的。

Composition API简介

Composition API:组合式 API;一组低侵入式的、函数式的 API,使得我们能够更灵活地【组合】组件的逻辑。这是有别于 Options API 的一种函数式 API。无需通过很多选项来完成业务逻辑,Composition API提供了一个setup函数,我们可以将data数据、计算属性、方法等等,都放在setup函数中,这样就可以对业务进行集中处理了。

采用Composition API来重写上面的组件:

<template> 
Count is: {{ state.count }}, doubleCount is: {{ state.doubleCount }} 
<button @click="add">加</button> 
</template> 
<script> 
import { reactive, computed } from "vue"; 
export default { 
setup() { 
const state = reactive({ 
count: 0, 
doubleCount: computed(() => state.count * 2), 
}); 
function add() { 
state.count++; 
} 
return { 
state, 
add 
} 
} 
} 
</script>

还有一个 setup 函数,setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点,如

果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文,我们就可以在模板里使用对应

的属性和方法。

Composition API

setup()入口

setup 函数是一个新的组件选项,它是在组件内使用 Composition API 的入口点。它会在Vue实例创建完成前被调

用。所以,setup函数中没有this指针

<template> 
<div></div> 
</template> 
<script> 
export default { 
setup() { 
//这里书写本地状态(data数据)、计算属性或方法等 
console.log('setup函数'); 
} 
} 
</script>

如果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文,我们就可以在模板里使用对应的属性和方法。所以,我们可以将本地状态(data数据)、方法、计算属性等写在 setup 函数中。

<template> 
Count is: {{ count }} 
</template> 
<script> 
export default { 
setup() { 
let count = 10; 
return { 
count 
} 
} 
} 
</script>

上面代码中,在 setup 函数中声明了一个 count 数据。然后使用 return 返回需要暴露的内容。运行之后可以看到:视图能够正确显示count数据。

setup函数总结:

setup函数是Composition API 的入口点,是它的核心。

由于执行 setup 时,组件实例尚未被创建,因此在 setup 中不能使用 this。

setup中定义的东西必须要return出去,才能被使用或绑定视图。

ref 响应式监听

上面实例中,虽然视图能够显示数据。但当改变数据时,视图却不会得到响应。

<template> 
Count is: {{ count }} 
<button @click="add">加</button> 
</template> 
<script> 
export default { 
setup() { 
let count = 10; 
function add(){ 
count++; //setup函数中没有this指针 
} 
return { 
count, 
add 
} 
} 
} 
</script> 

原因很简单,count只是声明的一个普通数据,不具备响应功能。

在 Vue 3.0 中,我们可以通过一个 ref 函数来实现响应式数据监听。ref 接受一个参数,并将其包裹在一个带有value 属性的对象中返回,然后可以使用该 value 属性访问或更改响应式变量的值:

<template> 
Count is: {{ count }} 
<button @click="add">加</button> 
</template> 
<script> 
//注意:要导入ref 
import {ref} from 'vue'; 
export default { 
setup() { 
let count = ref(10); //count成为响应式数据。 
function add(){ 
count.value++; //使用value属性获取响应数据 
}
return { 
count, 
add 
} 
} 
} 
</script>

为什么要将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。因为在 JavaScript 中,Number 或 String 等基本类型是通过值传递的,而不是通过引用传递的:

reactive与toRefs

上面实例中,操作数据时需要使用 value 属性,比较麻烦。

可以使用 reactive 与 toRefs 解决这个问题。首先使用 reactive 创建响应式对象,封装数据。

<template> 
<p>Count is: {{ state.count }}</p> 
<button @click="add">加</button> 
<p>{{ state.user.username }}</p> 
</template> 
<script> 
//注意:要导入ref 
import {reactive} from 'vue'; 
export default { 
setup() { 
//所有响应数据都声明在这里,包括对象、数组 
const state = reactive({ 
count: 10, 
user:{ 
userId: 1, 
username: '张三' 
} 
})
function add(){ 
state.count++; //这里可以不使用value了 
} 
return { 
state, 
add 
} 
} 
} 
</script> 

此时不用使用 value 属性了。

但是因为只有state是响应式数据,而state中的那些数据还不是响应式的。所以在视图访问数据时都需要使用 state作为前缀才可以。这就比较麻烦了。

此时我们可以使用 toRefs 进行优化。toRefs可以将state中的每一个数据进行展开,且都包装成响应数据。这样视图层就可以直接使用了。

<template> 
<p>Count is: {{ count }}</p> 
<button @click="add">加</button> 
<p>{{ user.username }}</p> 
</template> 
<script> 
//注意:要导入ref 
import {reactive, toRefs} from 'vue'; 
export default { 
setup() { 
//所有响应数据都声明在这里,包括对象、数组 
const state = reactive({ 
count: 10, 
user:{ 
userId: 1, 
username: '张三' 
} 
}) 
function add(){ 
state.count++; //这里可以不使用value了 
} 
return { 
...toRefs(state), //这里使用使用 ...toRefs(state) 
add 
} 
} 
} 
</script>

在返回时使用 ...toRefs(state) ,这样视图层就可以不使用 state 前缀了。

为什么要使用 ... 参数扩展运输符呢?因为toRefs(state) 将state对象展开,并包装成多个响应数据。

computed的用法

<template> 
<p>Count is: {{ count }}</p> 
<p>doubleCount is: {{ doubleCount }}</p> 
<button @click="add">加</button> 
<p>{{ user.username }}</p> 
</template> 
<script> 
//注意:要导入ref 
import { reactive, toRefs, computed } from "vue"; 
export default { 
setup() { 
//所有响应数据都声明在这里,包括对象、数组 
const state = reactive({ 
count: 10, 
user: { 
userId: 1, 
username: "张三", 
}, 
doubleCount: computed(() => { //使用computed函数 
return state.count * 2; 
}), 
}); 
function add() { 
state.count++; //这里可以不使用value了 
} 
return { 
...toRefs(state), 
add, 
}; 
}, 
}; 
</script>

首先要import导入computed。

在reactive({})中声明computed即可。

到现在为止,响应式数据就都可以处理了。

watch的用法

<template> 
<div> 
{{ num }} 
<button @click="add">加</button> 
</div> 
</template> 
<script> 
import { reactive, toRefs, watch } from "vue"; 
export default { 
setup() { 
const state = reactive({ 
num: 0, 
}); 
watch(state,(newValue, oldValue) => { 
console.log(newValue, oldValue); 
} 
); 
function add() { 
state.num++; 
} 
return { 
...toRefs(state), 
add, 
}; 
}, 
}; 
</script>

使用watch函数来进行数据监听。watch函数有两个参数。

第一个参数:要监听的数据。

第二个参数:触发监听时的处理函数(包括newValue, oldValue)

上面实例中直接监听state响应对象。但我们知道,在state中会有很多数据,如果只想监听其中的某个数据,就需要换一种写法:

watch(() => state.num,(newValue, oldValue) => { 
console.log(newValue, oldValue); 
});

第一个参数要写成函数返回值的形式,这样就能监听state响应对象中的某个数据了。

setup()参数

setup() 函数有两个参数:props 和 context。

为什么要有这两个参数呢?我们知道父子组件之间是可以传值。但是现在我们的业务逻辑都写在setup函数中,而setpu中没有this指针,那么就只能靠这两个参数来进行传递了。

props:父组件向子组件传值的参数。

context:子组件向父组件传值的参数。

props参数

setup() 函数的 props 是父组件向子组件传值的参数。

在components文件夹中创建子组件(Hello.vue):

<template> 
<div>我是子组件</div> 
</template> 
<script> 
export default { 
setup(props, context) { 
console.log(props.msg) 
}, 
props: { 
msg: String, 
}, 
}; 
</script> 
<style> 
</style>

父组件(HomeView.vue):

<template> 
<div> 
<Hello msg="hello"></Hello> 
</div> 
</template> 
<script> 
import Hello from '../components/Hello.vue' 
import { reactive, toRefs, computed } from "vue"; 
export default { 
setup() { 
const state = reactive({ 
}); 
return { 
...toRefs(state), 
}; 
}, 
components:{
Hello 
} 
}; 
</script>

注意,要先import导入子组件,然后使用components挂载子组件。

context参数

setup() 函数的 context 是子组件向父组件传值的参数。

子组件(Hello.vue):

<template> 
<div> 
<div>我是子组件</div> 
<button @click="send">给父组件发数据</button> 
</div> 
</template> 
<script> 
export default { 
setup(props, context) { 
function send() { 
context.emit("childmsg", "hello world!"); 
} 
return { 
send, 
}; 
}, 
props: { 
msg: String, 
}, 
}; 
</script> 
<style> 
</style>

父组件(HomeView.vue):

<template> 
<div> 
<Hello msg="hello" @childmsg="get"></Hello> 
<p>我是父组件,接受子组件传的值:{{welcome}}</p> 
</div> 
</template> 
<script> 
import Hello from '../components/Hello.vue' 
import { reactive, toRefs, computed } from "vue";
export default { 
setup() { 
//所有响应数据都声明在这里,包括对象、数组 
const state = reactive({ 
welcome: '' 
}); 
function get(param) { 
state.welcome = param; 
} 
return { 
...toRefs(state), 
get 
}; 
}, 
components:{ 
Hello 
} 
}; 
</script>

Composition API的使用

下面我们会将前面学过的知识点都改写为Composition API的形式。而且,Vue3兼容Options API和

Composition API两种写法。所以这两种写法都要会。

provide与inject的使用

我们学过provide与inject可用于多级组件直接传递数据,下面学习provide与inject在Composition API中的使用。

1.创建孙子组件(SubHello.vue)

<template> 
<div> 
<div>我是孙组件</div> 
</div> 
</template> 
<script> 
import { inject } from "vue"; 
export default { 
setup(props, context) { 
console.log(inject('msg'))
return {}; 
} 
}; 
</script>

在孙子组件中import导入inject,并使用inject接收上级组件的传值。

2.在子组件(Hello.vue)中使用孙子组件

<template> 
<div> 
<div>我是子组件</div> 
<SubHello></SubHello> 
</div> 
</template> 
<script> 
import SubHello from './SubHello.vue' 
export default { 
setup(props, context) { 
return {}; 
}, 
components:{ 
SubHello 
} 
}; 
</script> 
<style> 
</style> 

3.在父组件中使用provide给多级组件传值

<template> 
<div> 
<Hello></Hello> 
</div> 
</template> 
<script> 
import Hello from "../components/Hello.vue"; 
import { provide } from "vue"; 
export default { 
setup() { 
provide('msg','hello'); 
}, 
components:{ 
Hello
} 
}; 
</script> 

注意,由于父组件向孙子组件传递数据是单向的,所以孙子组件不能修改传递的值。如果子组件

vue生命周期的用法

在 setup () 内部调用生命周期钩子:

注意:在Composition API中没有beforeCreate()和created()这里两个声明周期函数了,统一使用setup()。

实例

<template> 
<div> 
{{ num }} 
<button @click="add">加</button> 
</div> 
</template> 
<script> 
import { 
reactive,toRefs,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } 
from "vue"; 
export default { 
setup() { 
const state = reactive({ 
num: 1, 
});
function add() { 
state.num++; 
} 
onBeforeMount(() => { 
console.log("DOM挂载前!"); 
}); 
onMounted(() => { 
console.log("DOM挂载后!"); 
}); 
onBeforeUpdate(() => { 
console.log("数据更新前!"); 
}) 
onUpdated(() => { 
console.log("数据更新后!"); 
}) 
onBeforeUnmount(() => { 
console.log("实例卸载前!"); 
}) 
onUnmounted(() => { 
console.log("实例卸载后!"); 
}) 
return { 
...toRefs(state), 
add, 
}; 
}, 
}; 
</script>

编程式路由的使用

下面学习如何在Composition API中使用路由。

打开App.vue组件,这里已经有了路由。当然,是使用router-link标签来实现的。现在我们将它改成编程式路由。

<template> 
<nav> 
<!-- 
<router-link to="/">Home</router-link> | 
<router-link to="/about">About</router-link> 
--> 
<button @click="toHome">Home</button> 
<button @click="toAbout">About</button>
</nav> 
<router-view /> 
</template> 
<script> 
import { useRouter } from "vue-router"; 
export default{ 
setup() { 
const router = useRouter(); 
function toHome(){ 
router.push('/'); 
} 
function toAbout(){ 
router.push({path:'/about',query:{name:'zhangsan'}}); 
} 
return { 
toHome, 
toAbout 
} 
}, 
} 
</script> 

先import导入useRouter模块。

通过useRouter模块获取router对象。以后的路由写法就与前面所学一样了。

下面是获取路由参数,打开AboutView.vue文件

<template> 
<div> 
</div> 
</template> 
<script> 
import { useRoute } from "vue-router"; 
export default { 
setup(){ 
const route = useRoute(); 
console.log(route.query.name); 
} 
} 
</script> 

通过同样的方式获取route对象后就可以获取路由参数了。

Vuex的使用

下面学习如何在Composition API中使用Vuex。

<template> 
<div> 
</div> 
</template> 
<script> 
import { useStore } from "vuex"; 
export default { 
setup() { 
const store = useStore(); 
console.log(store.state.num); 
console.log(store.getters.newnum); 
} 
}; 
</script> 

先import导入useStore模块。

通过useStore模块获取store对象。就可以通过store对象获取Vuex中的所有数据了。

获取DOM的使用

前面我们知道在Vue中,可以使用ref来获取DOM对象。下面学习如何在Composition API中使用ref。

<template> 
<div ref="myRef">获取单个DOM元素</div> 
</template> 
<script> 
import { ref, onMounted } from 'vue'; 
export default { 
setup() { 
const myRef = ref(null); //ref(null)是一个固定的写法 
onMounted(() => { 
console.dir(myRef.value); 
}); 
return { 
myRef 
}; 
} 
}; 
</script>

在HTML标签中使用 ref 属性标识一个DOM对象。

需要 import 导入 ref 对象。

使用 const myRef = ref(null); 的方式获取包装好的DOM对象,命名为HTML中的 ref 属性名。并且此数据需

要暴露出去。

使用 value 属性即可获取 DOM对象。

使用Composition API重写todoList

AddNew组件

<template> 
<div> 
<input type="text" v-model="newItem" /> 
<button @click="handleAdd">添加</button> 
</div> 
</template> 
<script> 
import {reactive, toRefs} from 'vue'; 
export default { 
setup(props, context){ 
const state = reactive({ 
newItem: "" 
}) 
function handleAdd() { 
if (state.newItem == "") { 
alert("不能为空"); 
return; 
} 
//注意:这里使用setup参数context来出发父组件事件 
context.emit("submitNewItem", state.newItem); 
state.newItem = ""; 
} 
return { 
...toRefs(state), 
handleAdd 
} 
} 
} 
</script> 

TheList组件

<template> 
<ol> 
<li v-for="(item, index) in list" :key="index" @click="judgeItem(index)">
{{ item }} 
</li> 
</ol> 
</template> 
<script> 
export default { 
setup(props, context) { 
//这里分别使用了setup的两个参数 
function judgeItem(index) { 
if (props.listType) { 
context.emit("handleDelete", index); 
} else { 
context.emit("handleJudge", index); 
} 
} 
return { 
judgeItem 
}; 
}, 
props: { 
list: { 
type: Array, 
required: true, 
}, 
listType: { 
type: Boolean, 
default: false, 
}, 
}, 
}; 
</script> 

TodoList组件

<template> 
<div> 
<h1>todoList</h1> 
<AddNew @submitNewItem="addNewItem"></AddNew> 
<TheList :list="todoList" @handleJudge="toDone"></TheList> 
<hr /> 
<TheList :list="doneList" :listType="true" @handleDelete="toDelete"></TheList> 
</div> 
</template> 
<script> 
import AddNew from "../components/AddNew.vue"; 
import TheList from "../components/TheList.vue"; 
import {reactive, toRefs} from 'vue'; 
export default {
setup(){ 
const state = reactive({ 
todoList: [], //待办事项 
doneList: [] //完成事项 
}) 
function addNewItem(newItem){ 
state.todoList.push(newItem); 
} 
function toDone(index){ 
state.doneList.push(state.todoList.splice(index,1)[0]); 
} 
function toDelete(index){ 
state.doneList.splice(index,1); 
} 
return { 
...toRefs(state), 
addNewItem, 
toDone, 
toDelete 
} 
}, 
components: { 
AddNew, 
TheList, 
}, 
}; 
</script>

setup语法糖

在Composition API中,在setup函数中声明的数据、函数等内容,都需要通过 return 对外暴露,才能被组件的视图模板(template)使用,这就造成了书写上的不方便。于是,Vue官方又给我们推出了一个新的setup语法糖。

使用setup语法糖,就可以不用写setup函数了。并且,数据和函数也不用返回,组件也不需要注册了。

setup语法糖的基本结构

<template> 
</template> 
<script setup> 
//此处直接写setup函数中的内容 
</script> 
<style> 
</style>

在script标签中使用setup属性即可。

运行时,script标签中的内容会被重新编译成 setup() 函数的形式。

而且,声明的数据、函数不需要通过 return 暴露,即可被 template所使用

响应数据的使用

<template> 
<div> 
<p>{{ num }}</p> 
<button @click="add">加</button> 
</div> 
</template> 
<script setup> 
let num = 10; 
//const num = 10; 
//由于num不是响应数据,所以改变num是无效的。 
const add = ()=>{ 
alert("触发了此方法"); 
num++; 
} 
</script> 

直接声明的数据不是响应式的,数据改变时不会响应到视图模板中。

<template> 
<div> 
<p>{{ num }}</p> 
<p>{{ dept.deptno }},{{ dept.dname }},{{ dept.loc }}</p> 
<ul> 
<li v-for="user in userArr" :key="user.userId"> 
{{user.userId}},{{user.userName}},{{user.userAge}} 
</li> 
</ul> 
<button @click="add">加</button> 
</div> 
</template>
<script setup> 
import { reactive, ref } from "vue"; 
const num = ref(10); 
const dept = reactive({ 
deptno: 20, 
dname: "技术部", 
loc: '沈阳市', 
}); 
const userArr = ref([ 
{ 
userId: 100, 
userName: "张三", 
userAge: 25, 
}, 
{ 
userId: 101, 
userName: "李四", 
userAge: 26, 
}, 
{ 
userId: 102, 
userName: "王五", 
userAge: 27, 
}, 
]); 
const add = () => { 
num.value++; //注意:要使用value属性获取 
dept.deptno++; 
//userArr.value[0].userAge++; 
userArr.value = []; 
} 
</script>

ref 和 reactive 都可以做响应式数据,它们的区别如下:

reactive:用于定义引用类型。只能修改数据,不能改变其引用。

ref:用于定义基本类型和引用类型。可以修改数据,也可以改变其引用。

在方法中修改数据时需要使用 value属性。因为,Ref的本质是通过Reactive创建的,Ref(10) 就相当于:Reactive({value:10});

在视图模板调用可以省略value属性的书写。

其它语法的使用

下面例子演示了computed计算属性、watch监听、生命周期函数的使用。

<template>
<div> 
{{ num }} 
{{ newNum }} 
<button @click="add">add</button> 
</div> 
</template> 
<script setup> 
import { ref, computed, watch, onMounted } from 'vue'; 
const num = ref(10); 
const newNum = computed(() => { 
return num.value*2; 
}) 
const add = ()=>{ 
num.value++; 
} 
watch(num,(newValue,oldValue)=>{ 
console.log(newValue,oldValue); 
}) 
//生命周期函数 
onMounted(() => { 
console.log("DOM挂载后!"); 
}); 
</script> 

引入组件的使用

引入的组件不必注册,可以直接使用。

<template> 
<div class="home"> 
<HelloWorld msg="Welcome to Your Vue.js App"/> 
</div> 
</template> 
<script setup> 
// @ is an alias to /src 
import HelloWorld from '@/components/HelloWorld.vue' 
</script>

父子组件传值的使用

defineProps的使用

defineProps用于父组件向子组件传值。

父组件

<template> 
<div class="home"> 
<HelloWorld msg="Welcome to Your Vue.js App" :num="num"/> 
</div> 
</template> 
<script setup> 
// @ is an alias to /src 
import HelloWorld from '@/components/HelloWorld.vue' 
const num = 20 
</script> 

子组件

<template> 
<div class="hello"> 
<h1>{{ msg }},{{ num }}</h1> 
</div> 
</template> 
<script setup> 
//const myProps = defineProps(['msg','num']); 
const myProps = defineProps({ 
msg:{ 
type: String 
}, 
num:{ 
type: Number, 
required: true 
} 
}); 
</script> 
<style scoped> 
</style>

defineProps也可以有数组形式和对象形式两种写法。

defineEmits的使用

defineEmits用于子组件向父组件传值。

父组件

<template> 
<div class="home"> 
<HelloWorld 
msg="Welcome to Your Vue.js App" 
:num="num" 
@childmsg="get"/> 
</div> 
</template> 
<script setup> 
// @ is an alias to /src 
import HelloWorld from '@/components/HelloWorld.vue' 
const num = 20; 
const get = (value) => { 
console.log(value) 
} 
</script>

子组件

<template> 
<div class="hello"> 
<h1>{{ msg }},{{ num }}</h1> 
<button @click="send">给父组件传值</button> 
</div> 
</template> 
<script setup> 
const myProps = defineProps(['msg','num']); 
const emit = defineEmits(['childmsg']); 
const send = () => { 
emit('childmsg','子组件向父组件传的值'); 
} 
</script> 
<style scoped> 
</style>

使用setup语法糖重写todoList

AddNew组件

<template> 
<div> 
<input type="text" v-model="newItem" /> 
<button @click="handleAdd">添加</button> 
</div> 
</template> 
<script setup> 
import { ref } from "vue"; 
const newItem = ref(""); 
const emit = defineEmits(["submitNewItem"]); 
const handleAdd = () => { 
if (newItem.value == "") { 
alert("不能为空"); 
return; 
} 
emit("submitNewItem", newItem.value); 
newItem.value = ""; 
}; 
</script> 

TheList组件

<template> 
<ol> 
<li v-for="(item, index) in list" :key="index" @click="judgeItem(index)"> 
{{ item }} 
</li> 
</ol> 
</template> 
<script setup> 
const emit = defineEmits(['handleDelete','handleJudge']); 
const judgeItem = (index) => { 
if (myProps.listType) { 
emit("handleDelete", index); 
} else { 
emit("handleJudge", index); 
} 
}; 
const myProps = defineProps({ 
list: { 
type: Array, 
required: true, 
}, 
listType: { 
type: Boolean,
default: false, 
}, 
}); 
</script> 

TodoList组件

<template> 
<div> 
<h1>todoList</h1> 
<AddNew @submitNewItem="addNewItem"></AddNew> 
<TheList :list="todoList" @handleJudge="toDone"></TheList> 
<hr /> 
<TheList 
:list="doneList" 
:listType="true" 
@handleDelete="toDelete" 
></TheList> 
</div> 
</template> 
<script setup> 
import AddNew from "../components/AddNew.vue"; 
import TheList from "../components/TheList.vue"; 
import { reactive } from "vue"; 
const todoList = reactive([]); //待办事项 
const doneList = reactive([]); //完成事项 
const addNewItem = (newItem) => { 
todoList.push(newItem); 
} 
const toDone = (index) => { 
doneList.push(todoList.splice(index, 1)[0]); 
} 
const toDelete = (index) => { 
doneList.splice(index, 1); 
} 
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1642663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

brpc中http2 grpc协议解析

搭建gRPC服务 | bRPC https://blog.csdn.net/INGNIGHT/article/details/132657099 global.cpp http2_rpc_protocol.cpp ParseH2Message解析frame header信息 ParseResult H2Context::ConsumeFrameHead( 这个是固定长度的9字节帧头部&#xff0c;length是&#xff0c;3*8bit…

七. Django项目之电商购物商城 -- 退出登录

Django项目之电商购物商城 – 退出登录状态 需要开发文档和前端资料的可私聊 退出登录主要是基于Django自带的logout模块 , 该功能只有在登录是保存了用户状态才可以实现调用 一. 创建退出视图 class LogoutView(View):def get(self , request):# 删除用户数据logout(reque…

etcd源码流程---调试环境的搭建

etcd启动命令&#xff1a; name必须设置&#xff0c;否则会用default&#xff0c;集群内不同etcd实例的名字应该是唯一的&#xff0c;因为他会有一个map(name->ip)。如果initial-cluster-state设置为new&#xff0c;那么他会创建一个新的clusterid。需要在initial-cluster中…

PHP 反序列化

一、PHP 序列化 1、对象的序列化 <?php class people{public $nameGaming;private $NationLiyue;protected $Birthday12/22;public function say(){echo "老板你好呀&#xff0c;我是和记厅的镖师&#xff0c;叫我嘉明就行&#xff0c;要运货吗你&#xff1f;"…

【LeetCode:1235. 规划兼职工作 + 动态规划 + 二分查找】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Unity---版本控制软件

13.3 版本控制——Git-1_哔哩哔哩_bilibili Git用的比较多 Git 常用Linux命令 pwd&#xff1a;显示当前所在路径 ls&#xff1a;显示当前路径下的所有文件 tab键自动补全 cd&#xff1a;切换路径 mkdir&#xff1a;在当前路径下创建一个文件夹 clear&#xff1a;清屏 vim…

能将图片转为WebP格式的WebP Server Go

本文完成于 2023 年 11 月 之前老苏介绍过 webp2jpg-online&#xff0c;可以将 webp 格式的图片&#xff0c;转为 jpg 等&#xff0c;今天介绍的 WebP Server Go 是将 jpg 等转为 webp 格式 文章传送门&#xff1a;多功能图片转换器webp2jpg-online 什么是 WebP ? WebP 它是由…

SpringBoot与SpringMVC的区别

SpringBoot与SpringMVC的区别是什么&#xff1f; SpringBoot和SpringMVC是Java开发中常用的两个框架&#xff0c;它们都是由Spring框架所提供的&#xff0c;但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。 一、SpringBoot的特点&#…

区域文本提示的实时文本到图像生成;通过一致性自注意力机制的视频生成工具保持视频的一致性;专门为雪佛兰汽车设计的客服聊天机器人

✨ 1: StreamMultiDiffusion StreamMultiDiffusion是首个基于区域文本提示的实时文本到图像生成框架&#xff0c;实现了高速且互动的图像生成。 StreamMultiDiffusion 旨在结合加速推理技术和基于区域的文本提示控制&#xff0c;以克服之前解决方案中存在的速度慢和用户交互性…

Pytorch: nn.Embedding

文章目录 1. 本质2. 用Embedding产生一个10 x 5 的随机词典3. 用这个词典编码两个简单单词4. Embedding的词典是可以学习的5. 例子完整代码 1. 本质 P y t o r c h \mathrm{Pytorch} Pytorch 的 E m b e d d i n g \mathrm{Embedding} Embedding 模块是一个简单的查找表&#…

活动图与状态图:UML中流程图的精细化表达——专业解析系统动态性与状态变迁

流程图是一种通用的图形表示法&#xff0c;用以展示步骤、决策和循环等流程控制结构。它通常用于描述算法、程序执行流程或业务过程&#xff0c;关注于任务的顺序执行。流程图强调顺序、分支和循环&#xff0c;适用于详细说明具体的处理步骤&#xff0c;图形符号相对基础和通用…

c++多线程基础

简介 c多线程基础需要掌握这三个标准库&#xff1a;std::thread, std::mutex, and std::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::thread…

论文阅读笔记(AAAI 20)Order Matters

个人博客地址 注&#xff1a;部分内容参考自GPT生成的内容 论文笔记&#xff1a;Order Matters&#xff08;AAAI 20&#xff09; 用于二进制代码相似性检测的语义感知神经网络 论文:《Order Matters: Semantic-Aware Neural Networks for Binary Code Similarity Detection》…

android_systemServer进程启动流程

一&#xff0c;systemServer进程是被Zygote进程fork出来的&#xff0c;具体代码&#xff0c; 在startBootstrapServices、startCoreServices、startOtherServices、startApexServices中&#xff0c;对各类服务进行了启动&#xff0c;比如我们常见的ActivityManagerService、Pa…

【idea-sprongboot项目】在linux服务器上纯远程开发方式

继上一篇博客【idea-sprongboot项目】SSH连接云服务器进行远程开发-CSDN博客 目录 五、远程开发方式 2&#xff09;纯远程开发方式 步骤 五、远程开发方式 2&#xff09;纯远程开发方式 实现原理&#xff0c; 步骤 &#xff08;1&#xff09;首先&#xff0c;关闭当前正在…

【LeetCode刷题记录】98. 验证二叉搜索树

98 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。 示例…

Rust 生命周期浅谈

1. 简述 Rust 中的每一个引用都有其 生命周期&#xff08;lifetime&#xff09;&#xff0c;也就是引用保持有效的作用域。大部分时候生命周期是隐含并可以推断的&#xff0c;正如大部分时候类型也是可以推断的一样。类似于当因为有多种可能类型的时候必须注明类型&#xff0c;…

MaxKB宝塔Docker安装并配置域名访问

准备 Linux系统 bt面板 默认环境LNMP随便装 服务器环境配置最好是4G&#xff0c; 占用硬盘存储大概1G 对于一些海外AI产品的对接需要使用香港或者海外的服务器 安装 在宝塔面板中打开SSH或者你本地使用SSH工具去链接服务器 运行docker命令 前提是放开服务器的8080端口 doc…

【深度学习基础(3)】初识神经网络之深度学习hello world

文章目录 一. 训练Keras中的MNIST数据集二. 工作流程1. 构建神经网络2. 准备图像数据3. 训练模型4. 利用模型进行预测5. (新数据上)评估模型精度 本节将首先给出一个神经网络示例&#xff0c;引出如下概念。了解完本节后&#xff0c;可以对神经网络在代码上的实现有一个整体的了…

智慧文旅展现文化新风貌,科技助力旅行品质升级:借助智慧技术,文旅产业焕发新生机,为旅行者带来更高品质的文化体验之旅

一、引言 在数字化、智能化的浪潮下&#xff0c;文旅产业正迎来前所未有的发展机遇。智慧文旅作为文旅产业与信息技术深度融合的产物&#xff0c;不仅为旅行者带来了全新的文化体验&#xff0c;也为文旅产业注入了新的活力。本文旨在探讨智慧文旅如何借助智慧技术展现文化新风…