目录
1.ref函数
import { ref } from 'vue'
export default {
name: 'App',
setup() {
// 直接写数据不是响应式的
let name = ref('张三')
let age = ref(18)
function changeInfo() {
name = '李四'
age = 19
console.log(name, age)
}
// 还要return出去 返回一个对象
return {
name,
age,
changeInfo
}
// 返回一个函数(渲染函数 跟render相似 ,引入h)
// return () => h('h1', '尚硅谷')
}
}
</script>
-
作用: 定义一个响应式的数据
-
语法:
const xxx = ref(initValue)
-
创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
-
JS中操作数据:
xxx.value
-
模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
-
-
备注:
-
接收的数据可以是:基本类型、也可以是对象类型。如果是对象,对象还用调用 reactive,成为reactive型的响应式数据
-
基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 -
对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
-
2.reactive函数
作用:定义一个对象类型的响应式数据(基本类型不要用reactive,要用ref)
- 语法:const 代理对象= reactive(源对象)```接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
- reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
let sum = ref(0)
let person1 = ref({
name: 'zhangsan',
age: 19
})
let person2 = reactive({
name: 'lisi',
age: 18
})
输出
3.Vue3的响应式实现
vue2的响应式只有get和set,直接增加属性和删除属性不能得到响应式,要用到this.$set(对象,属性名,值)或者Vue.set()来获得响应式以及this.$delete和Vue.delete,很麻烦。主要是Vue实现响应式用到Object.defineProperty()只有查和改。
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
Vue3实现响应式用Proxy,把增删改查全实现了
target就是对象:
propsName是属性名:
在里面要用target[propsName]不能用点的形式:因为propsName是一个变量,[]有解析变量的作用,而点后面应该是字符串。
<script>
let person = {
name: 'zhangsan',
age: 18
}
const p = new Proxy(person, {
// 增和查都设置在这里了
get(target, propsName) {
console.log(target, propsName)
console.log(`有人读取了上${propsName}属性`)
return target[propsName]
},
// 修改
set(target, propsName, value) {
console.log(`p上的${propsName}在被修改`)
target[propsName] = value
},
// 删除
deleteProperty(target, propsName) {
console.log(`属性${propsName}正在被删除`)
return delete target[propsName]
}
})
</script>
用Reflect
来进行增删改查,这也是框架的做法,其实本质上没什么太大的区别,只是Reflect
可以避免一些报错吧,不然还要用try-catch
去捕获?这里不用太纠结,其实就是为了让框架更加的友好(少一些报错)
<script>
let person = {
name: 'zhangsan',
age: 18
}
const p = new Proxy(person, {
// 增和查都设置在这里了
get(target, propsName) {
console.log(target, propsName)
console.log(`有人读取了上${propsName}属性`)
return Reflect.get(target, propsName)
},
// 修改
set(target, propsName, value) {
console.log(`p上的${propsName}在被修改`)
Reflect.set(target, propsName, value)
},
// 删除
deleteProperty(target, propsName) {
console.log(`属性${propsName}正在被删除`)
return Reflect.defineProperty(target, propsName)
}
})
</script>
4.reactive和ref的区别
从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
从原理角度对比:
ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
从使用角度对比:
ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。reactive定义的数据:操作数据与读取数据:均不需要
.value
。
5.setup的两个注意点
-
setup执行的时机
-
在beforeCreate之前执行一次,this是undefined。
-
-
setup的参数
-
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
-
context:上下文对象
-
attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 -
slots: 收到的插槽内容, 相当于
this.$slots
。 -
emit: 分发自定义事件的函数, 相当于
this.$emit
-
-
打印输出第一个参数props,父组件传来的数据用props接收,把封装成了proxy对象
打印输出第二个参数:
父组件给子组件绑定的自定义事件,子组件也可以接收
context的emit就是用来触发自定义事件
6.computed计算属性
就是变成了函数形式并且需要import,其它没啥区别,如果没有set也是简写形式,直接在computed里写回调函数
<template>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.fistName">
<br>
名:<input type="text" v-model="person.lastName">
<br>
全名:<input type="text" v-model="fullName">
<button @click="test">测试</button>
<slot></slot>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
name: 'Demo',
setup() {
let person = reactive({
fistName: '张',
lastName: '三',
})
let fullName = computed({
get() {
return person.fistName + '-' + person.lastName
},
set(value) {
const arr = value.split('-')
person.fistName = arr[0]
person.lastName = arr[1]
}
})
return {
person,
fullName
}
}
}
</script>
<style></style>
7.watch计算属性
/第一种情况:监视ref所定义的一个响应式数据
watch(sum, (newValue, oldValue) => {
console.log(newValue + '------' + oldValue)
}, { immediate: true })
情况二:监视多个ref定义的响应式数据
watch([sum, msg], (newValue, oldValue) => {
console.log('sum或msg变了', newValue, oldValue)
})
情况三:监视reactive定义的响应式数据
能监视person里的全部属性,此处无法正确获得oldValue
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true})
情况四:监视reactive定义的响应式数据中的某个属性
监视对象里的一个属性,必须写成一个箭头函数进行返回,要不然就是死的值,无法进行监视,如果你监视的还是一个对象,必须手动开启深度监视
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
情况五:监视reactive定义的响应式数据中的某些属性
如果要监视对象里的多个属性,一样写成数组的形式
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
watchEffect函数
watch的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-watchEffect有点像computed:
但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
//watchEffect的回调一上来会先执行一次
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})