选项式API和组合式API的区别/Vue2和Vue3的区别/为什么需要Vue3?
Vue2和Vue3的最大的区别其实就是前者是选项式API,后者是组合式API。
那么我们为什么需要组合式API呢?对此,官方作者其实已经做出了回答。
我们可以打开Vue3的官网,点击侧栏最下方的“组合式API常见问答”见到如下图:
可以看到左边的选项式API,因为data、methods等选项是分离的,那么对于一个业务功能可能需要数据放在上面data里,方法放在下面的methods里。那么当一个组件里面有多个业务功能时,就会如左图一样,一个业务功能是一种颜色,我们可以看到,颜色分散在了各处。
而组合式API实际上更偏向于原生js的写法,让相同业务功能的代码更加聚集。
下载vscode相关插件vue-official
setup省去声明子组件(components选项)和命名当前组件(name选项)
一段vue2的代码如下:
App.vue
<template>
<div id="app">
<Movie></Movie>
</div>
</template>
<script>
import Movie from './components/Movie.vue';
export default {
name: 'App',
components: {
Movie
},
}
</script>
Movie.vue
<template>
<div>
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: "Movie",
data: function () {
return {
title: "金刚狼"
}
}
}
</script>
</script>
但是在vue3中就变为了:
App.vue
<template>
<div id="app">
<Movie></Movie>
</div>
</template>
<script setup>
import Movie from './components/Movie.vue';
</script>
<style>
可以看到只要在script标签中加上setup,整段export都在报错。于是我们去掉export的内容。刷新后运行发现页面仍然显示数据。
意味着我们用了setup后就不再需要申明当前组件的子组件,即不再需要components选项。但凡是import了的都会自动变为当前组件的子组件。
而且不再需要命名当前组件,即不再需要name选项。
响应式数据的声明
那么对于Movie.vue又该怎么修改呢?如果我们用了setup,就没有export,那么也就没有了data选项。没有data选项又该怎么申明响应式数据呢?
实际上vue3中的响应式数据非常像原生js的声明,分为以下几种。
reactive和shallowreactive(对象)
reative适用于声明类型是对象的响应式数据。
注意数组也是个对象,所以我们这里以数组为例
<template>
<div>
<h1>{{ myData }}</h1>
<button @click="clickHandler">修改</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const myData = reactive([1, 2, 3])
function clickHandler() {
myData.push(4)
}
</script>
可以看到点击按钮后页面显示的数组就会在末尾增加4
注意,reactive申明的对象里面所有嵌套的属性或者说对象都是响应式的,只要修改就会被监听到。
譬如如下:
<template>
<div>
<h1>{{ myData }}</h1>
<button @click="clickHandler">修改</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const myData = reactive({
name: "shanshan",
age: 20,
scope: [10, 20]
})
function clickHandler() {
myData.scope.push(30)
}
</script>
可以发现我们修改的是第二维结构scope,也能被监听并且反向渲染到页面上。
而有时候我们的对象有比较复杂的数据结构,我们并不需要或者并不希望每一层都被监听,因为这样性能消耗会比较多,那么我们只希望第一层是响应式的,被监听,那么我们就可以使用shallowReactive
我们将myData的申明改为
const myData = shallowReactive({
此时再次运行并测试,发现scope的修改并不会渲染到页面上了。
但是如果我们继续将函数内改为
myData.age++
因为属于第一层的数据,是响应式的,那么就能看到修改效果。
ref(基本数据类型)
ref适用于类型是基本数据类型的响应式数据。譬如这里
<template>
<div>
<h1>{{ myData }}</h1>
<button @click="clickHandler">修改</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const myData = ref(0)
function clickHandler() {
myData.value++
}
</script>
可以看到我们在函数内修改myData的值用的是myData.value而不是myData本身。
这是因为上面的reactive是基于ES6的proxy实现的,而proxy支持的参数只有对象。而我们现在想对于一个基本类型做处理,vue3便通过ref在内部创建了一个空的对象,并且将我们初始化的基本类型数据挂载到了这个空对象的value属性上,从而达到支持声明响应式的基本数据类型的效果。
readonly
被readonly申明的变量都相当于一个常量,本身及内部属性或者对象都是只读的,不可以被修改的,且不会响应。
<template>
<div>
<h1>{{ myData }}</h1>
<button @click="clickHandler1">修改1</button>
<h2>{{ yourData }}</h2>
<button @click="clickHandler2">修改2</button>
</div>
</template>
<script setup>
import { readonly} from 'vue';
const myData = readonly(0)
function clickHandler1() {
myData++
}
const yourData = readonly({
name: "shanshan",
age: 20,
scope: [10, 20]
})
function clickHandler2() {
yourData.scope.push(30)
}
</script>
注意这里如果点击修改1的按钮,那么可以发现整个页面会报错。但是如果点击修改2的按钮,就不会报错。
这是因为readonly声明的变量相当于一个常量,我们是不支持修改常量的本身,所以函数1的操作会直接引发报错。
但是我们可以试图修改常量的内部属性,所以函数2的操作不会引发报错,只是并不会修改成功而已。
同样的,如果我们希望一个对象只有根属性是只读的,但是内部属性是非只读的,可修改的,那么我们可以使用shallowReadonly
将变量的申明改为如下:
const yourData = shallowReadonly({
然后打开控制台测试,可以发现控制台内的scope是会增加数据的,只不过依旧无法响应。
computed
<script setup>
import { computed, ref} from 'vue';
const myData = ref('this is a test')
const getLen = computed(() =>{
console.log("计算属性执行了");
return myData.value.length
})
function clickHandler1() {
myData.value+='!'
}
</script>
watch
watch支持基本数据类型,所以传入直接写ref本身即可
<script setup>
import { ref, watch} from 'vue';
const myData = ref('this is a test')
watch(myData, (newValue, oldValue)=>{
console.log(newValue, oldValue);
})
function clickHandler1() {
myData.value+='!'
}
</script>
如果想要watch监听一个对象的某个属性值,那么需要用函数包裹起来,如下
<script setup>
import { reactive, watch} from 'vue';
const myData = reactive({
name: "shanshan",
age: 20,
scope: [10, 20]
})
function clickHandler1() {
myData.scope.push(30)
}
watch(() => myData.friends, (newValue, oldValue)=>{
console.log(newValue, oldValue);
})
</script>
watchEffect
自动监听传入的数据中所有响应式数据的变化(可以传入非响应式数据)
<template>
<div>
<h1>{{ myData }}</h1>
<button @click="clickHandler1">修改1</button>
<h2>{{ yourData }}</h2>
<button @click="clickHandler2">修改2</button>
</div>
</template>
<script setup>
import { readonly, ref, watch, watchEffect} from 'vue';
const myData = readonly({
name: "shanshan",
age: 20,
scope: [10, 20]
})
function clickHandler1() {
myData.age++
}
const yourData = ref(0)
function clickHandler2() {
yourData.value++
}
watchEffect(()=>{
console.log("myData.age:"+myData.age+" yourData"+yourData.value);
})
</script>