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

为什么要使用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.mfbz.cn/a/592722.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解网络原理3----TCP核心特性介绍(上)【面试高频考点】

文章目录 前言TCP协议段格式一、确认应答【保证可靠性传输的机制】二、超时重传【保证可靠性传输的机制】三、连接管理机制【保证可靠性传输的机制】3.1建立连接&#xff08;TCP三次握手&#xff09;---经典面试题3.2断开连接&#xff08;四次挥手&#xff09;3.3TCP状态转换 四…

【skill】onedrive的烦人问题

Onedrive的迷惑行为 安装Onedrive&#xff0c;如果勾选了同步&#xff0c;会默认把当前用户的数个文件夹&#xff08;桌面、文档、图片、下载 等等&#xff09;移动到安装时提示的那个文件夹 查看其中的一个文件的路径&#xff1a; 这样一整&#xff0c;原来的文件收到严重影…

政安晨:【Keras机器学习示例演绎】(三十五)—— 使用 LayerScale 的类注意图像变换器

目录 简介 导入 层刻度层 随机深度层 类注意力 会说话的头注意力 前馈网络 其他模块 拼凑碎片&#xff1a;CaiT 模型 定义模型配置 模型实例化 加载预训练模型 推理工具 加载图像 获取预测 关注层可视化 结论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#…

Topaz Video AI 5.0.3激活版 AI视频无损缩放增强

Topaz Video AI专注于很好地完成一些视频增强任务&#xff1a;去隔行&#xff0c;放大和运动插值。我们花了五年时间制作足够强大的人工智能模型&#xff0c;以便在真实世界的镜头上获得自然的结果。 Topaz Video AI 还将充分利用您的现代工作站&#xff0c;因为我们直接与硬件…

【数学建模】矩阵微分方程

一、说明 我相信你们中的许多人都熟悉微分方程&#xff0c;或者至少知道它们。微分方程是数学中最重要的概念之一&#xff0c;也许最著名的微分方程是布莱克-斯科尔斯方程&#xff0c;它控制着任何股票价格。 ​​ 股票价格的布莱克-斯科尔斯模型 微分方程可以由数学中的许多…

MidJourney提示词大全

大家好&#xff0c;我是无界生长。 这篇文章分享一下MidJourney提示词&#xff0c;篇幅内容有限&#xff0c;关注公众号&#xff1a;无界生长&#xff0c;后台回复&#xff1a;“MJ”&#xff0c;获取全部内容。 我是无界生长&#xff0c;如果你觉得我分享的内容对你有帮助&…

ArcGIS软件:地图投影的认识、投影定制

这一篇博客介绍的主要是如何在ArcGIS软件中查看投影数据&#xff0c;如何定制投影。 1.查看地图坐标系、投影数据 首先我们打开COUNTIES.shp数据&#xff08;美国行政区划图&#xff09;&#xff0c;并点击鼠标右键&#xff0c;再点击数据框属性就可以得到以下的界面。 我们从…

【Mac】graphpad prism for Mac(专业医学绘图工具) v10.2.3安装教程

软件介绍 GraphPad Prism for Mac是一款专业的科学数据分析和绘图软件&#xff0c;广泛用于生物医学和科学研究领域。它具有强大的统计分析功能&#xff0c;可以进行各种数据分析&#xff0c;包括描述性统计、生存分析、回归分析、方差分析等。同时&#xff0c;它还提供了丰富…

C++奇迹之旅:string类接口详解(上)

文章目录 &#x1f4dd;为什么学习string类&#xff1f;&#x1f309; C语言中的字符串&#x1f309;string考察 &#x1f320;标准库中的string类&#x1f309;string类的常用接口说明&#x1f320;string类对象的常见构造 &#x1f6a9;总结 &#x1f4dd;为什么学习string类…

FFmpeg学习记录(二)—— ffmpeg多媒体文件处理

1.日志系统 常用的日志级别&#xff1a; AV_LOG_ERRORAV_LOG_WARNINGAV_LOG_INFOAV_LOG_DEBUG #include <stdio.h> #include <libavutil/log.h>int main(int argc, char *argv[]) {av_log_set_level(AV_LOG_DEBUG);av_log(NULL, AV_LOG_DEBUG, "hello worl…

Cisco Nexus Dashboard 3.1(1k) - 云和数据中心网络管理软件

Cisco Nexus Dashboard 3.1(1k) - 云和数据中心网络管理软件 跨数据中心和云实现集中配置、运行和分析。 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-nexus-dashboard/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sys…

根据docker部署nginx并且实现https

目录 一、Docker中启用HTTPS有几个重要的原因 二、https介绍 三、https过程 四、安装docker-20.10.18 五、如何获取证书 通过阿里云获取证书 六、docker部署nginx并且实现https 6.1准备证书 6.2准备nginx.conf 和 index.html文件 6.3生成容器 6.4浏览器验证证书 一、…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑碳捕集和电转气的综合能源系统优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

STM32标准库控制一盏LED闪烁

实物连接&#xff1a; ## 软件编程&#xff1a;默认已经有一个工程模板&#xff0c;代码实现逻辑&#xff1a; 1、使用RCC开启GPIO的时钟&#xff1b; 2、使用GPIO初始化函数实现初始化GPIO 3、使用输入或输出的函数控制GPIO口 #include "stm32f10x.h" …

JavaEE 多线程详细讲解(1)

1.线程是什么 &#xff08;shift F6&#xff09;改类名 1.1.并发编程是什么 &#xff08;1&#xff09;当前的CPU&#xff0c;都是多核心CPU &#xff08;2&#xff09;需要一些特定的编程技巧&#xff0c;把要完成的仍无&#xff0c;拆解成多个部分&#xff0c;并且分别让…

【Java】面向对象核心知识点(三),文章层次分明,内容精益求精,代码简单易懂

目录 一、对象类型转换 1.1 情况 1.2 语法 1.3 代码 1.4 结论 二、static关键字 2.1 作用 2.2 代码 三、final关键字 3.1 作用 3.2 代码 四、instanceof关键字 4.1 作用 4.2 代码 &#xff08;原创文章&#xff0c;转载请注明出处&#xff09; 博主是计算机专业…

StableDiffusion 文生视频教程,从Mov2mov到AnimateDiff

文章目录 0. 前言1. 简介2. 文生视频2.1 Mov2mov2.1.1 插件安装2.1.2 视频生成 2.2 ffmpeg Ebsynth2.2.1 ffmpeg 安装2.2.2 Ebsynth安装2.2.3 Ebsynth 插件安装2.2.4 视频生成2.2.4.1 Step 1 蒙版裁剪2.2.4.2 Step2 识别关键帧2.2.4.3 Step3~4 关键帧重绘2.2.4.3 Step5~6 生成…

n-Track Studio Suite for Mac激活版:打造您的专属音频工作室

n-Track Studio Suite for Mac是一款功能强大的数字音频工作站软件&#xff0c;让您在家中就能享受到专业录音棚的待遇。无论是录制人声、乐器还是MIDI序列&#xff0c;都能轻松应对。 n-Track Studio Suite for Mac激活版下载 这款软件拥有实时音高校准、时间拉伸和自动补足功…

18 内核开发-内核重点数据结构学习

课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中。 课程特点&#xff1a; 1. 入门级别&…

9.4.k8s的控制器资源(job控制器,cronjob控制器)

目录 一、job控制器 二、cronjob控制器 一、job控制器 job控制器就是一次性任务的pod控制器&#xff0c;pod完成作业后不会重启&#xff0c;其重启策略是&#xff1a;Never&#xff1b; 简单案例 启动一个pod&#xff0c;执行完成一个事件&#xff0c;然后pod关闭&#xff1b;…
最新文章