Options API 和 Composition API 的对比
在vue2中,我们会在一个vue文件中methods,computed,watch,data中等等定义属性和方法,共同处理页面逻辑,我们称这种方式为Options API
缺点: 一个功能往往需要在不同的vue配置项中定义属性和方法,比较分散,项目小还好,清晰明了,但是项目大了后,一个methods中可能包含很多个方法,你往往分不清哪个方法对应着哪个功能
当然这种都是废话了,不过在vue3中还是可以使用vue2.x中的开发方式,就不演示了,主要演示一下Composition API
setup
不喜欢看文档或者不喜欢这个文档节奏的,直接看我的就行了,带你一步步使用各种api,开始小试牛刀
根据setup,Vue3.2以上推出了语法糖 <script setup></script>
,其实学习晚点也是有好处的,黑科技都在后面
<template>
<div>
<div>{{ number }}</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup(){
return{
number:ref(0)
}
}
}
</script>
再对比一下 <script setup></script>
<template>
<div>
<div>{{ number }}</div>
</div>
</template>
<script setup>
import { ref } from "vue";
let number = ref(0);
</script>
再对比一下引入组件的方式
<template>
<div>
home
<Count></Count>
</div>
</template>
<script setup>
import Count from '../components/Count.vue'
</script>
<script>
import Count from '../components/Count.vue'
export default {
components:{Count}
}
</script>
对比一下相对来说<script setup></script>就十分简洁,composition api初使用还是很舒服的哈
那么setup函数还是有俩参数的,那么这个留到父子传值的时候再写仔细点,现在也用不上哈
新的响应式数据
Vue 中用过三种响应式解决方案,分别是 defineProperty、Proxy 和 value setter,但 defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有一些缺陷。比如在配置代码中删除某属性,set 函数就不会执行,还是之前的数值。这也是为什么在 Vue 2 中,我们需要 $delete 一个专门的函数去删除数据(demo就不演示了,老玩家都知道哈,不知道赶紧先学vue2
)。
Vue 3 的响应式机制是基于 Proxy 实现的,有个deleteProperty函数去管理各种操作哈,从而实现了响应式的功能,之前的文章有介绍,可以自己找一下
如何使用ref reactive toRefs
在Vue3.X中,使用ref 与reactive 来声明响应式变量,给大家演示一下
<template>
<div>
<div>
<button @click="add"> {{ number }}</button>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
let number = ref(0);
const add = function(){
number.value = ++ number.value
}
</script>
setup函数版,学习就要多敲
<template>
<div>
<button @click="add">{{ number }}</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let number = ref(0)
const add = function () {
number.value = ++number.value
}
return {
number,
add,
}
}
}
</script>
问题不大,++效果非常完美,就是页面比较丑,那不重要
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ obj.name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script setup>
import { ref } from "vue";
let number = ref(0);
const add = function () {
number.value = ++number.value
}
let obj = ref({
name: 'lwj',
age: '24'
})
const changename = function () {
obj.value.name = 'test'
}
</script>
setup函数版
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{obj.name}}
<button @click="changename"> 修改名字 </button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let number = ref(0)
let obj = ref({
name:'lwj',
age:'24'
})
const add = function () {
number.value = ++number.value
}
const changename = function(){
obj.value.name = 'test'
}
return {
number,
add,
obj,
changename
}
}
}
</script>
可以成功修改哈,如果将对象分配为 ref 值,则它将被 reactive 函数处理为深层的响应式对象。相当于你还不如使用reactive,而且reactive不用使用.value去读取
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ obj.name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script setup>
import { ref,reactive } from "vue";
let number = ref(0);
const add = function () {
number.value = ++number.value
}
let obj = reactive({
name: 'lwj',
age: '24'
})
const changename = function () {
obj.name = 'test'
}
</script>
setup函数版
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{obj.name}}
<button @click="changename"> 修改名字 </button>
</div>
</template>
<script>
import { ref,reactive } from "vue";
export default {
setup() {
let number = ref(0)
let obj = reactive({
name:'lwj',
age:'24'
})
const add = function () {
number.value = ++number.value
}
const changename = function(){
obj.name = 'test'
}
return {
number,
add,
obj,
changename
}
}
}
</script>
有一点点简洁了吧,那写到这里,其实格式还不是很好,那么其实对象这种东西是可以储存任何数据的,我们可以把方法变量都使用reactive 不是很好
<template>
<div>
<button @click="add">{{ obj.number }}</button>
<div></div>
{{ obj.name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script>
import { ref, reactive,toRefs } from "vue";
export default {
setup() {
const obj = reactive({
name: 'lwj',
number: 0,
add: () => {
//setup中 this改向了undefined
obj.number = ++obj.number
},
changename: () => {
obj.name = 'test'
}
})
return {obj}
}
}
</script>
此处使用setup函数演示比较清楚哈,方法的话没有生效,因为数据没有响应,那我们引入toRef函数,可以使其响应
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script>
import { ref, reactive, toRefs, toRef } from "vue";
export default {
setup() {
const obj = reactive({
name: 'lwj',
number: 0,
add: () => {
//setup中 this改向了undefined
obj.number = ++obj.number
},
changename: () => {
obj.name = 'test'
}
})
const name = toRef(obj, 'name')
const number = toRef(obj, 'number')
return { name, number }
}
}
</script>
这样的话数据是响应了,但是方法没生效,因为我们没有将方法返回,
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script>
import { ref, reactive,toRefs,toRef} from "vue";
export default {
setup() {
const obj = reactive({
name: 'lwj',
number: 0,
add: () => {
//setup中 this改向了undefined
obj.number = ++obj.number
},
changename: () => {
obj.name = 'test'
}
})
const name = toRef(obj,'name')
const number = toRef(obj,'number')
const add = ()=>{
obj.number = ++obj.number
}
const changename = () => {
obj.name = 'test'
}
return {name,number,add,changename}
}
}
</script>
那么这样就可以了,这样比较鸡肋,toRefs将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 [ref
],在Vue2中每个响应式数据如果你打印会发现有set get属性,在vue3中就是有个ref属性了,待会演示一下,使用toRefs 与解构 可以快速解决上方的问题
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script>
import { ref, reactive,toRefs,toRef} from "vue";
export default {
setup() {
const obj = reactive({
name: 'lwj',
number: 0,
add: () => {
//setup中 this改向了undefined
obj.number = ++obj.number
},
changename: () => {
obj.name = 'test'
}
})
var newobj = toRefs(obj)
return {...newobj}
}
}
</script>
语法糖版
<template>
<div>
<button @click="add">{{ number }}</button>
<div></div>
{{ obj.name }}
<button @click="changename">修改名字</button>
</div>
</template>
<script setup>
import { ref, reactive, toRefs } from "vue";
const obj = reactive({
name: 'lwj',
number: 0,
add: () => {
//setup中 this改向了undefined
obj.number = ++obj.number
},
changename: () => {
obj.name = 'test'
}
})
const { name,number,add,changename } = toRefs(obj);
</script>
现在我们看下ref的数据
<template>
</template>
<script>
import { ref, reactive,toRefs,toRef} from "vue";
export default {
setup() {
const num = ref(0)
const obj = reactive({
name:'lwj',
age:18
})
const name = toRef(obj,'name')
console.log(num)
console.log(obj)
console.log(name)
}
}
</script>
那直白点,存在ifRef属性的就是可相应数据,顺带提一下,vue2中我们template标签下必须存在根标签,那vue3 无所谓了
export default {
setup() {
const num = ref(0)
const obj = reactive({
name: 'lwj',
age: 18
})
const name = toRef(obj, 'name')
console.log(isRef(num)) // true
console.log(isRef(obj)) // false
console.log(isRef(name)) // true
console.log(unref(num)) // 0
console.log(unref(obj)) // Object
console.log(unref(name)) // lwj
}
}
isRef 判断是否是ref数据,unref如果参数是一个 ref
,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val
的语法糖函数。
computed 创建只读计算属性
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象,或者接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象
<template>
<div>{{ num1 }}</div>
<div>{{ num2 }}</div>
</template>
<script setup>
import { ref, computed } from "vue";
const num1 = ref(0)
const num2 = computed(() => {
return num1.value + 1
})
//报错 不能修改数据 ++失败 值不变
num2++
//修改num1的值 发生改变
num1.value = ++ num1.value
</script>
自定义可修改数据,相当于生成一个ref响应数据
<template>
<div>{{ num1 }}</div>
<div>{{ num2 }}</div>
</template>
<script setup>
import { ref, computed } from "vue";
const num1 = ref(0)
const num2 = computed({
get: () => num1.value + 10,
set: val => {
num1.value = val - 10
}
})
</script>
此时页面的值为0 10
如果执行 num2.value = 20,页面就会展示10 20,因为num1也是响应数据,当num2改变的时候执行set函数,此时num1会变为10,执行完set执行get获取值的操作,此时num2返回20,打印num2的话返回ComputedRefImpl对象,存在isRef为true的属性,现在例子比较生疏,后面结合完整点的会更清楚
watch和watchEffect都是监听器,但在写法和使用上有所区别
监听器的作用主要就是监听响应数据的改变,这玩意最好用的地方就是异步请求的时候,在监听里面完全不用担心数据未发生改变,挺喜欢这玩意
当watch监听 ref时
<template></template>
<script setup>
import { ref, watch } from "vue";
const num = ref(0)
//监听变量 新值 老值
watch(num, (newValue, oldValue) => {
// 1s后打印 0 1
console.log(`原值为${oldValue}`)
console.log(`新值为${newValue}`)
})
setTimeout(() => {
num.value++
}, 1000)
</script>
当watch监听reactive时
<template></template>
<script setup>
import { ref, watch,reactive} from "vue";
const num = reactive({
count: 0
})
//监听变量 新值 老值
watch(() => num.count, (newValue,oldValue) => {
console.log(`原值为${oldValue}`)
console.log(`新值为${newValue}`)
})
setTimeout(() => {
num.count++
}, 1000)
</script>
watchEffect
首次加载就会监听
只能拿到最新的值
不需要指定监听的数据,组件初始化的时候就会执行一次用以收集依赖,而后收集到的依赖发生变化,这个回调才会再次执行
<template></template>
<script setup>
import { reactive, watchEffect,ref } from "vue";
const num = reactive({
count: 0
})
const name = ref('lwj')
//监听变量 新值 老值
watchEffect(()=>{
//首次加载打印 初始值
console.log(num.count)
console.log(name.value)
//1s后打印 修改值
})
setTimeout(() => {
num.count++
name.value='test'
}, 1000)
</script>
先学到这里,最后演示一下vue3的变量css
<template>
<div>
<h1 @click="add">{{ count }}</h1>
</div>
</template>
<script setup>
import { ref } from "vue";
let count = ref(1)
let color = ref('red')
function add() {
count.value++
color.value = Math.random() > 0.5 ? "blue" : "red"
}
</script>
<style scoped>
h1 {
color: v-bind(color);
}
</style>>
网友评论