一、Hook函数
Vue3 的hook函数相当于 vue2 的 mixin,不同点在于hooks是函数。Vue3的hook函数可以帮助我们提高代码的复用性,能在不同的组件中都使用hook函数。
在src目录下建立一个 hooks 文件夹,文件夹中新建一个我们要用的方法的名字.js文件(一般情况下use开头)。
useCar.js
import { ref, computed } from 'vue'
export default function () {
let carName = ref("奔驰")
let carPrice = ref(50000)
// 修改汽车名称
let updateCarName = (name) => {
carName.value = name
}
// 修改汽车价格
let updateCarPrice = (price) => {
carPrice.value = price
}
let usaCarPrice = computed(() => {
return '$' + (carPrice.value / (Math.random() + 6)).toFixed(2)
})
return {
carName,
carPrice,
updateCarName,
updateCarPrice,
usaCarPrice
}
}
usePlane.js
// 导入ref,computed
import { ref, computed } from "vue";
export default function () {
// 飞机名称
let planeName = ref("波音747");
// 飞机价格
let planePrice = ref(20000);
// 修改飞机名称的方法
let updatePlaneName = (name) => {
planeName.value = name;
};
// 修改飞机价格的方法
let updatePlanePrice = (price) => {
planePrice.value = price;
};
// 飞机在美国的价格
let usaPlanePrice = computed(() => {
return "$" + (planePrice.value / (Math.random() + 6)).toFixed(2);
});
return {
planeName,
planePrice,
updatePlaneName,
updatePlanePrice,
usaPlanePrice,
};
}
组件
将文件引入到要使用的组件中。
<div class="child">
<div class="car">
<p>汽车名称:{{carName}}<button @click="updateCarName('宝马')">修改汽车名称</button></p>
<p>汽车价格:{{'¥'+carPrice}}<button @click="updateCarPrice(10000)">修改汽车价格</button></p>
<p>美国价格:{{usaCarPrice}}</p>
</div>
<hr>
<div class="plane">
<p>汽车名称:{{planeName}}<button @click="updatePlaneName('B2轰炸机')">修改飞机名称</button></p>
<p>汽车价格:{{'¥'+planePrice}}<button @click="updatePlanePrice(50000)">修改飞机价格</button></p>
<p>美国价格:{{usaPlanePrice}}</p>
</div>
</div>
import {computed, ref} from 'vue'
// 导入hook函数useCar
import useCar from '../hooks/useCar'
// 导入hook函数usePlane
import usePlane from '../hooks/usePlane'
export default {
name: "Child",
setup() {
return {
...useCar(),
...usePlane()
}
}
};
二、生命周期
Vue3 中,可以在 setup() 函数中使用生命周期,定义组合式API生命周期函数。组合式API生命周期函数,会先与传统的生命周期函数执行。
与vue2相比,在vue3中对beforeDestroy和destroyed这两个生命周期函数,进行了重命名,beforeUnmount 替换了 beforeDestroy;unmounted 替换了 destroyed。
setup()函数,可以替代beforeCreate 和 created 这两个生命周期函数,因为vue2中的beforeCreate和created生命周期的执行几乎与VUE3中的setup在同一时间执行。
<div class="child3">
{{ count }}
<button @click="updateCount">count++</button>
</div>
// 导入生命周期组合式api,除了beforeCreate和created这两个生命周期函数没有组合式api
// 其他的生命周期函数,都有对应的组合式api,命名方式只是在原有方法名的前面加上on
// setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount,onUnmounted } from "vue";
export default {
name: "Child3",
data() {
return {
show:true
}
},
setup() {
//注意:setup是在beforeCreate和created之前运行的。
//所以,在setup里面,无法调用data和methods里面的数据
console.log('setup');
let count = ref(1);
let updateCount = () => {
count.value++;
};
//挂载前
onBeforeMount(() => {
console.log('onBeforeMount');
}),
//挂载完成
onMounted(()=>{
console.log('onMounted');
})
//修改后,页面重新挂载前
onBeforeUpdate(()=>{
console.log('onBeforeUpdate');
})
//修改后,页面重新挂载完成
onUpdated(()=>{
console.log('onUpdated');
})
//卸载前
onBeforeUnmount(()=>{
console.log('onBeforeUnmount');
})
//卸载完成
onUnmounted(() => {
console.log('onUnmounted');
})
return {
count,
updateCount,
};
},
/* data() {
return {
count: 1,
};
},
methods: {
updateCount() {
this.count++;
},
}, */
// 数据初始化前
/*beforeCreate() {
console.log("--beforeCreate--");
},
// 数据初始化完成
created() {
console.log("--created--");
},
// 页面挂载前
beforeMount() {
console.log("--beforeMount--");
},
// 页面挂载完成
mounted() {
console.log("--mounted--");
},
// 数据修改后,页面重新挂载前
beforeUpdate() {
console.log(this.count);
console.log("--beforeUpdate--");
},
// 数据修改后,页面重新挂载完成
updated() {
console.log(this.count);
console.log("--updated--");
},
// 卸载组件之前前
beforeUnmount() {
console.log("--beforeUnmount--");
},
// 卸载组件完成
unmounted() {
console.log("--unmounted--");
}, */
};
三、toRef和toRefs
toRef()函数:可以用来为一个 reactive 对象里面的指定属性创建一个 ref,这个 ref 可以被传递并且能够保持响应性。这样做的好处是简化了模板中的表达式。toRef()函数,需要传两个参数:reactive 对象、具体的属性名。
toRefs()函数:把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个 ref。
<div class="child1">
<ul>
<li>姓名:{{ name }}</li>
<li>年龄:{{ age }}</li>
<li>性别:{{ sex }}</li>
<li>地址:{{ address }}</li>
<li>汽车名称:{{ car.name }}</li>
<li>汽车价格:{{ car.price }}</li>
<li><button @click="updateStudent">修改学生信息</button></li>
</ul>
</div>
import { reactive, toRefs } from "vue";
export default {
name: "Child1",
setup() {
// 学生对象
let student = reactive({
name: "张三",
age: 20,
sex: "男",
address: "南京",
car: {
name: "宝马",
price: 1000,
},
});
// 修改学生的方法
let updateStudent = () => {
student.name = "李四";
student.age = 22;
student.sex = "女";
student.address = "成都";
student.car.name = "奔驰";
student.car.price = 2000;
};
return {
// toRef()方法,用于将一个reactive对象里面的指定属性以ref形式的对象返回
// name:toRef(student,'name'),
// age:toRef(student,'age'),
// sex:toRef(student,'sex'),
// address:toRef(student,'address'),
// 假如 reactive 对象中,有100个属性,上面的操作要写100次,所以,一般都直接用toRefs函数
// 注意:如果student里面的属性是一个对象,这个属性对象里面的内容是不能直接转为ref的
// toRefs只能保证一级属性
...toRefs(student),
updateStudent,
};
},
};
四、其他的组合式API
1、readonly
readonly()方法:用于返回一份只读数据。传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的。
注意:该方法,不能将一个普通值类型数据转为只读数据。
<div>{{ student }} <button @click="updateStudent">修改学生信息</button></div>
import { reactive, readonly } from "vue";
export default {
name: "Child2",
setup() {
let data = reactive({
name: "张三",
age: 20,
});
let student = readonly(data);
// student是一个只读对象,无法修改数据
let updateStudent = () => {
student.name = "李四";
student.age = 22;
};
return {
student,
updateStudent,
};
},
};
2、isReadonly
检查一个对象是否是由 readonly 创建的只读代理。
import { reactive, readonly, isReadonly } from "vue";
let data = reactive({
name: "张三",
age: 20,
});
let student = readonly(data);
console.log('student is readonly',isReadonly(student)); //true
3、isRef
isRef():检查一个值是否为一个 ref 对象。
import { ref, isRef } from "vue";
let name = ref('你好')
console.log('name is ref',isRef(name)); //true
4、isProxy
isProxy():检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。
import { reactive, readonly, isProxy } from "vue";
setup() {
let data = reactive({
name: "张三",
age: 20,
});
let student = readonly(data);
console.log('data is proxy',isProxy(data)); //true
console.log('student is proxy',isProxy(student)); //true
5、isReactive
isReactive():检查一个对象是否是由 reactive 创建的响应式代理。如果这个代理是由 readonly 创建的,但是又被 reactive 创建的另一个代理包裹了一层,那么同样也会返回 true。
import { reactive, isReactive } from "vue";
setup() {
let data = reactive({
name: "张三",
age: 20,
});
console.log('data is reactive',isReactive(data)); //true
}
6、unref
unref()函数:如果参数是一个 ref 则返回它的 value,否则返回参数本身。等同于 val = isRef(val) ? val.value : val 。
import { ref, unref } from "vue";
setup() {
let name = ref('你好')
let age = 20
console.log(unref(name)); // '你好'
console.log(unref(age)); // 20
}
7、markRaw
markRaw():使用markRaw()方法返回出来的对象,再也不能成为响应式对象,函数返回这个对象本身。如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的。
import { reactive, markRaw } from "vue";
setup() {
// 使用markRaw()方法,返回出来的对象,再也不能成为响应式对象。
let car = markRaw({
name:'奔驰',
price:20
})
let car2 = reactive(car)
// car2只是一个普通对象,不是响应式对象
console.log(car2);
}
8、shallowRef
shallowRef():返回的对象 value 属性值是 object对象(普通对象),不再具备任何响应式了。
<div>
汽车信息:{{ car3 }}
<button @click="updateCar">修改汽车</button>
</div>
import { shallowRef } from "vue";
setup() {
let car3 = shallowRef({
name: "大众",
type: {
typeName: "SUV",
},
});
let updateCar = () => {
// 由于value返回的是object对象,所以,这里不再具有响应式
car3.value.name = "奔驰";
car3.value.type.typeName = "跑车";
};
return {
car3,
updateCar
};
}
9、shallowReactive
shallowReactive():用于定义浅响应式对象,只对第一层属性设置响应式。
import { shallowReactive,isReactive } from "vue";
setup() {
let luhan = shallowReactive({
name:'鹿晗',
age:30,
friend:{
name:'关晓彤',
age:20,
}
})
let updateLuhan = ()=>{
// luhan.name = '张艺兴',
// luhan.age = 25
// 如果只是修改对象的深层属性,不会触发页面更新
luhan.friend.name = '小美'
luhan.friend.age = 22
}
}
10、shallowReadonly
shallowReadonly:浅只读,只有第一层属性是只读的。
import { shallowReadonly } from "vue";
setup() {
let luhan = shallowReactive({
name:'鹿晗',
age:30,
friend:{
name:'关晓彤',
age:20,
}
})
let luhan3 = shallowReadonly(luhan);
// luhan3.name = '小明' // 属性只读
luhan3.friend.name = "小美"; // 属性可修改
}
11、toRaw
toRaw()方法:用于将一个响应式对象转为普通对象。
import { toRaw } from "vue";
setup() {
let luhan = shallowReactive({
name:'鹿晗',
age:30,
friend:{
name:'关晓彤',
age:20,
}
})
let luhan2 = toRaw(luhan);
console.log("luhan2", luhan2); // 普通对象
}