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.mzph.cn/pingmian/6526.shtml

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

相关文章

Python数据分析案例44——基于模态分解和深度学习的电负荷量预测(VMD+BiGRU+注意力)

案例背景 承接之前的案例&#xff0c;说要做模态分解加神经网络的模型的&#xff0c;前面纯神经网络的缝合模型参考数据分析案例41和数据分析案例42。 虽然我自己基于各种循环神经网络做时间序列的预测已经做烂了.....但是还是会有很多刚读研究生或者是别的领域过来的小白来问…

Elasticsearch 数据聚合

Bucket聚合&#xff08;桶聚合&#xff09; 对文档做分组&#xff0c;aggs 按照文档字段值或日期进行分组&#xff0c;能参与分词的字段不能做聚合&#xff0c;如text类型的字段 例如&#xff1a;根据城市名称做聚合&#xff0c;也就是城市名称对数据进行分组统计。可以加qu…

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

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

平面模型上提取凸凹多边形------pcl

平面模型上提取凸凹多边形 pcl::PointCloud<pcl::PointXYZ>::Ptr PclTool::ExtractConvexConcavePolygons(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud) {pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);p…

「 网络安全常用术语解读 」通用漏洞报告框架CVRF详解

1. 背景 ICASI在推进多供应商协调漏洞披露方面处于领先地位&#xff0c;引入了通用漏洞报告框架&#xff08;Common Vulnerability Reporting Format&#xff0c;CVRF&#xff09;标准&#xff0c;制定了统一安全事件响应计划&#xff08;USIRP&#xff09;的原则&#xff0c;…

计算机网络——Dijkstra路由算法

实验目的 实现基于 Dijkstra 算法的路由软件 实验内容 网络拓扑如图所示 实验过程 先编写开辟应该图的空间&#xff0c;然后给点映射数字&#xff0c;构建图。程序获取用户输入的学号&#xff0c;构建图中边的权值。接下来程序从用户输入获取最短路径的搜索起点&#xff0…

JVM知识总汇(JVM面试题篇5.1)

个人理解&#xff0c;所学有限&#xff0c;若有不当&#xff0c;还请指出 1.JVM是由哪些部分组成&#xff0c;运行流程是什么&#xff1f; JVM为java虚拟机&#xff0c;是java程序的运行环境&#xff08;其实是java字节码文件的运行环境&#xff09;&#xff0c;能够实现一次编…

深入理解Docker容器镜像

深入理解Docker容器镜像 1 容器是什么&#xff1a;特殊的进程 容器其实是一种沙盒技术。顾名思义&#xff0c;沙盒就是能够像一个集装箱一样&#xff0c;把你的应用“装”起来的技术。这样&#xff0c;应用与应用之间&#xff0c;就因为有了边界而不至于相互干扰&#xff1b;而…

Docker高频使用命令

一、Docker常用命令总结 1.镜像命令管理 指令描述ls列出镜像build构建镜像来自Dockerfilehoistory查看历史镜像inspect显示一个或多个镜像的详细信息pull从镜像仓库拉取镜像push推送一个镜像仓库rm移除一个或多个镜像prune一处未使用的镜像&#xff0c;没有被标记或被任何容器…

linux之ssh

SSH远程连接协议 SSH远程管理 定义 SSH&#xff08;Secure Shell &#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程的登录、远程复制等功能。 SSH协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令。因此SSH协议具…

WPF之绑定验证(错误模板使用)

1&#xff0c;前言&#xff1a; 默认情况下&#xff0c;WPF XAML 中使用的绑定并未开启绑定验证&#xff0c;这样导致用户在UI上对绑定的属性进行赋值时即使因不符合规范内部已抛出异常&#xff08;此情况仅限WPF中的数据绑定操作&#xff09;&#xff0c;也被程序默认忽略&…

《苍穹外卖》前端课程知识点记录

一、VUE基础知识 基于脚手架创建前端工程 1. 环境要求 安装node.js&#xff1a;Node.js安装与配置&#xff08;详细步骤&#xff09;_nodejs安装及环境配置-CSDN博客查看node和npm的版本号 安装Vue CLI&#xff1a;Vue.js安装与创建默认项目&#xff08;详细步骤&#xff09;…

分享一篇关于AGI的短文:苦涩的教训

学习强化学习之父、加拿大计算机科学家理查德萨顿&#xff08; Richard S. Sutton &#xff09;2019年的经典文章《The Bitter Lesson&#xff08;苦涩的教训&#xff09;》。 文章指出&#xff0c;过去70年来AI研究走过的最大弯路&#xff0c;就是过于重视人类既有经验和知识&…

探究Android的多分辨率支持以及各种类型图标尺寸大小

术语和概念 屏幕尺寸 屏幕的物理尺寸&#xff0c;以屏幕的对角线长度作为依据&#xff08;比如 2.8寸&#xff0c; 3.5寸&#xff09;。 简而言之&#xff0c; Android把所有的屏幕尺寸简化为三大类&#xff1a;大&#xff0c;正常&#xff0c;和小。 程序可以针对这三种尺寸…

Docker部署nginx并且实现https访问

实验环境&#xff1a; 在已有的docker环境和nginx镜像的基础上进行操作 1、生成私钥 &#xff08;1&#xff09;openssl genrsa -out key.pem 2048 生成证书签名请求 (CSR) 并自签证书: &#xff08;2&#xff09;openssl req -new -x509 -key key.pem -out cert.pem -day…

DDD:根据maven的脚手架archetype生成ddd多模块项目目录结构

随着领域驱动的兴起&#xff0c;很多人都想学习如何进行ddd的项目开发&#xff0c;那ddd的项目结构是怎么样的&#xff1f;又是如何结合SpringBoot呢&#xff1f;那么针对这个问题&#xff0c;笔者使用maven的archetype封装一个相对通用的ddd的项目目录&#xff0c;方便一键生成…

karpathy Let‘s build GPT

1 introduction 按照karpathy的教程&#xff0c;一步步的完成transformer的构建&#xff0c;并在这个过程中&#xff0c;加深对transformer设计的理解。 karpathy推荐在进行网络设计的过程中&#xff0c;同时利用jupyter notebook进行快速测试和python进行主要的网络的构建。 …

STM32标准库SPI通信协议与W25Q64

目录 一、SPI通信 1.SPI通信简介 2.硬件电路 3.移位示意图 4.SPI基本时序图 &#xff08;1&#xff09;起始和终止 &#xff08;2&#xff09;交换一个字节 模式0&#xff1a; 模式1&#xff1a;​编辑 模式2&#xff1a;​编辑 模式3&#xff1a;​编辑 5.SPI时序 …

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

数据仓库实验三:分类规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、决策树分类规则挖掘&#xff08;1&#xff09;新建一个 Analysis Services 项目 jueceshu&#xff08;2&#xff09;建立数据源视图&#xff08;3&#xff09;建立挖掘结构 DST.dmm&#xff08;4&#xff…