Vue3学后总结
1. setup
1.1 为什么使用setup
在vue2中我们需要在script中写方法(method)、数据(data)等等,这样会给我们的项目维护带来很大的不便,这时vue3中的setup就给我们带来了很大的便利。
1.2 使用
根据官方文档:
setup()钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
其余情况我们都要用这种语法:
<script setup></script>
使用setup的语法糖时不需要加export default{},来主动暴露,也不需要再调用setup()函数再在里面写代码,更不需要return{}作为返回值暴露给模板。我们可以用代码来直观感受一下。
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count
}
}
}
</script>
<script setup>
import {ref} from 'vue'
const count = ref(0)
</script>
可以很直观的看出使用setup会让我们的代码看上去更加简洁明了,更能体现出vue3更加轻量级的特点。
2. ref函数
2.1定义
接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .value。
通俗来说就是ref函数返回一个包含get和set方法的对象,而且是一个可响应的对象。
2.2 如何使用ref
2.2.1 基本用法
首先我们要定义一个量,并且用ref响应式来接收
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
2.2.2 如何操作ref中的值
使用ref函数返回的对象其实是一个RefImpl对象并不是一个value,我们还是通过代码来分析一下。
let name = ref("zs")
console.log(name)
当我们直接打印name时在浏览器控制台中返回的是一个
并非是一个'zs'也就是我们上面所说的RefImpl对象,里面有个属性value,value里的值才是’zs‘,所以如果我们想要直接获取这个值,我们必须这样写:
let name = ref("zs")
console.log(name.value)
这时控制台打印的只有zs这两个字母啦。
3. reactive函数
3.1 定义
接收一个普通对象然后返回该普通对象的响应式代理。
注意:虽然reactive和ref都是响应式,但是reactive只能用来返回一个对象类型数据,而ref可以用来返回基本类型数据,当然ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
3.2 如何使用reactive
reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
3.2.1 基本用法
let person = reactive({
name:'zs',
age:20,
job:{
type:'设计经理',
salary:'30k',
a:{
b:{
c:666
}
}
},
hobby:['吃饭','睡觉','打豆豆']
})
console.log(person)
console.log(person.name)//zs
console.log(person.job.type)//设计经理
console.log(person.job.a.b.c)//666
通过浏览器控制台我们可以观察到reactive返回的是一个Proxy对象
并且reactive获取值的时候并不需要像ref一样,在属性后加value。因此reative在操作person对象里的属性时也就比ref更加的容易。
4. computed
4.1 传入getter函数
传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。
let person = reactive({
firstName:'张',
lastName: '三'
})
person.fullName = computed(()=>{
return person.firstName+"-"+person.lastName
})
person.fullName = "1"+person.fullName//error
如果你修改就会在浏览器控制台中报以下错误。
4.2 传入一个get和set函数
或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
let person = reactive({
firstName:'张',
lastName: '三'
})
person.fullName = computed({
get(){
return person.firstName+"-"+person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
person.fullName = "1"+person.fullName// 1张-三
5. watch监听
5.1 监听ref
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
</template>
<script setup>
let sum = ref(0)
watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true})
</script>
{immediate:true}即刚打开网页就开始监听,无论你是否点击+1。如图第一条即是{immediate:true}所展示出来的效果,而后面两条数据则是监听才有的效果。
5.2 监听ref所定义的多个响应式数据
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>//1
<br>
<button @click="msg+='!'">你好啊:</button>
<h2>{{msg}}</h2>//你好啊!
</template>
<script setup>
let sum = ref(0)
let msg = ref('你好啊')
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})
</script>
5.3 监视reactive所定义的一个响应式数据的全部属性
<template>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="person.name+='qwq'">修改姓名</button>//zsqwq
<button @click="person.age+=1">增长年龄</button>//20
<h2>薪水:{{person.job.j1.salary}}k</h2>//20k
<button @click="person.job.j1.salary++">增长薪水</button>21k
</template>
<script setup>
let person = reactive({
name:"zs",
age:19,
job:{
j1:{
salary:20
}
}
})
watch(person,(newValue,oldValue)=>{
console.log('person变了',newValue,oldValue)
},{immediate:true})
</script>
5.4 监听reactive所定义的一个响应式数据中的某个属性
watch(()=>person.age,(newValue,oldValue)=>{
console.log('person变了',newValue,oldValue)
})
此时监听person.age的属性,只要age变了就会监听到
5.5 监听reactive所定义的一个响应式数据中的某些属性
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
console.log('age或name变了',newValue,oldValue)
})
因为监听内容只能填一个的关系,这里巧妙的用了数组的方法来同时监听person.age和person.name这样只要age或者name变了我们都可以监听到。
5.6 特殊情况
let person = reactive({
name:"pp",
age:19,
job:{
j1:{
salary:20
}
}
})
watch(()=>person.job,(newValue,oldValue)=>{
console.log("薪水变了",newValue,oldValue)
},{deep:true})
这里如果不用{deep:true}是监听不到salary的变化的,但是情况一的话不用deep就可以监视的到salary的变化,也是感觉很神奇。以我的理解可能是因为监听person的话自动开启深度监听,而job为person中的一个属性,默认不开启深度监听吧。如果有懂的大佬希望能来指正。
5.7 注意事项
1、使用watch监听reactive所定义的一个响应式数据时时,无法获取oldValue,只能监听到新数据。
6. watchEffect
6.1 定义
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
即watchEffect是立即执行,在加载页面时主动执行。
6.2 基本用法
watchEffect(()=>{
sum.value
console.log("watchEffect所指定的回调执行了")
})
我们可以看出watchEffect只需要传递一个回调函数,在加载中收集依赖,所以我们只要在内部写出我们需要指定要监听的数据就行。
7. watch和watchEffect的异同
异:
- 执行时机:watchEffect是立即执行的,在页面加载时会主动执行一次,来收集依赖;而watch是惰性地执行副作用,它不会立即执行,但可以配置 {immediate:true},使其主动触发。
- 参数不同:watchEffect只需要传递一个回调函数,不需要传递监听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数,第三个参数是配置项可以根据需求来配置,第一个参数是监听的数据,第二个参数是回调函数。
- 结果不同:watchEffect和watch监听reactive的时候一样都获取不到更改前的值,而watch可以同时获取更改前和更改后的值。
同:
watch 与 watchEffect 在手动停止侦听、清除副作用 (将 onInvalidate 作为第三个参数传递给回调)、刷新时机和调试方面有相同的行为。
学习中遇到的BUG
1、未使用驼峰命名时template会报错如图:
而且报错的时候是因为我用了setup的语法糖,没使用语法糖的时候没有报错。
后来看报错原因是因为我没有用驼峰命名法来命名我的文件,按照网上的方式在vue.config.js中加上 lintOnSave:false 即:
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false
})
加完之后发现仍然报错不知道是什么原因,后来无奈只能修改了文件名为驼峰命名的方式。