1.Hook函数
hooks是一种开发思想,我们可以利用hooks把相关代码剥离出去。hook有个编码习惯是用use开头。
// 导入ref,computed
import {ref,computed} from 'vue'
let carName = ref("奔驰");
let carPrice = ref(5000);
// 修改汽车名称的方法
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
}
同理,再搞一份飞机的hook函数,hooks文件下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
}
}
components下Child.vue组件:
在组件里导入所需的hook函数useCar,
import useCar from'../hooks/useCar'
,然后在setup(){ } 里 return{ } 里 ...useCar()
(三个点是展开运算符)
<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>
<script>
import { computed, ref } from "vue";
// 导入hook函数useCar
import useCar from'../hooks/useCar'
// 导入hook函数userPlane
import usePlane from'../hooks/usePlane'
export default {
name: "Child",
setup() {
return {
...useCar(),
...usePlane()
};
},
};
</script>
页面效果:hooks下每个xx.js文件就是一个业务,这里面的业务可以在多个xx.vue组件中使用。分别在组件中导入所需的hook函数useCar,然后在setup(){ } 里 return{ } 里 ...useCar()
,就可以惹。
2.生命周期
vue3是上来就挂载。vue2是先走一半再挂载。
App.vue组件:
<template>
<div class="app">
<div>
{{count}}
<button @click="updateCount">count++</button>
</div>
<Child></Child>
<Child2></Child2>
</div>
</template>
<script>
import Child from './components/Child.vue'
import Child2 from './components/Child2.vue'
export default {
name: 'App',
components:{
Child,
Child2
},
data() {
return {
count:1
}
},
methods: {
updateCount(){
this.count++
}
},
beforeCreate() {
// console.log(this.count);
console.log('--beforeCreate--');
},
created() {
// console.log(this.count);
console.log('--created--');
},
beforeMount() {
console.log('--beforeMount--');
},
mounted() {
console.log('--mounted--');
},
beforeUpdate() {
console.log(this.count);
// debugger;
console.log('--beforeUpdate--');
},
updated() {
console.log(this.count);
// debugger
console.log('--updated--');
},
}
</script>
<style>
*{
margin: 0;
padding: 0;
list-style: none;
}
.app{
border: 1px solid #ccc;
margin: 10px;
padding: 10px;
}
</style>
后台打印效果:
点击count++之后:
beforeUpdate和updated会在什么场景下使用?
顾sensei答,这会是一个补救措施,或是修改后的跟踪措施;或有点像监视器,能够监测到页面是否在更新,watch是针对某一个变量去监视,但它是监测到整个页面。
在vue3中,beforeDestroy和destroyed这两个生命周期函数,进行了重命名,替换成了 beforeUnmount 和 unmounted 。
在vue3中,除了beforeCreate和created这两个生命周期函数没有组合式api,其他的生命周期函数都有对应的组合式api,命名方式只是在原有方法名的前面加上on。
setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。
注意:setup是在beforeCreate和created之前运行的。所以,在setup里面,无法调用data和methods里面的数据。
Child3.vue组件:
<template>
<div class="child3">
{{ count }}
<button @click="updateCount">count++</button>
</div>
</template>
<script>
// 导入生命周期组合式api,除了beforeCreate和created这两个生命周期函数没有组合式api,
// 其他的生命周期函数都有对应的组合式api,命名方式只是在原有方法名的前面加上on。
// setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。
import { ref, onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from "vue";
export default {
name: "Child3",
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,
};
}
};
</script>
<style scoped>
.child3 {
border: 1px solid #ccc;
padding: 10px;
margin-top: 10px;
}
</style>
App.vue组件:
<template>
<div class="app">
<button @click="unMount">卸载Child3组件</button>
<Child3 v-if="isShow"></Child3>
</div>
</template>
<script>
import Child3 from './components/Child3.vue'
export default {
name: 'App',
components:{
Child3
},
data() {
return {
isShow:true
}
},
methods: {
unMount(){
this.isShow=false
}
},
}
</script>
<style>
*{
margin: 0;
padding: 0;
list-style: none;
}
.app{
border: 1px solid #ccc;
margin: 10px;
padding: 10px;
}
</style>
3.toRef 和 toRefs
toRef() 方法,用于将一个响应式对象里面的指定属性以ref形式的对象返回,这样写的好处是,可以简化模板里面的语法。
toRefs() 方法,用于将一个reactive对象返回一个新对象,该对象里面的所有属性都是一个ref对象。
<template>
<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>
</template>
<script>
import {ref,reactive, toRef,toRefs} from 'vue'
export default {
name: "Child1",
setup() {
let student = reactive({
name:'张三',
age:20,
sex:'男',
address:'南京',
car:{
name:'迈巴赫',
price:200
}
})
// 修改学生的方法
let updateStudent =()=>{
student.name = '李四'
student.age = 22
student.sex = '女'
student.address = '杭州',
student.car.name = '布加迪威龙',
student.car.price = 100
}
return{
// toRef()方法,用于将一个响应式对象里面的指定属性以ref形式的对象返回,
// 这样写的好处是,可以简化模板里面的语法。
// name:toRef(student,'name'),
// age:toRef(student,'age'),
// sex:toRef(student,'sex'),
// address:toRef(student,'address'),
// toRefs()方法,用于将一个reactive对象返回一个新对象,
// 该对象里面的所有属性都是一个ref对象。
...toRefs(student),
updateStudent
}
}
};
</script>
4.其他的组合式API
readonly() 方法,用于返回一份只读数据。注意:该方法,不能将一个普通值类型数据转为只读数据。
markRaw() 方法,返回出来的对象,再也不能成为响应式对象。应该用的也不多奥。
unref() 方法,用于判断对象如果是ref,就返回ref的value值;如果不是ref,直接返回值。
shallowReactive() / shallowRef() 方法,用于定义浅响应式对象。只对第一层属性设置响应式。
shallowReadonly() 是浅只读,只有第一层属性是只读的。
toRaw() 方法,用于将一个响应式对象转为普通对象。
<template>
<div class="child2">
<div>{{ student }}</div>
<button @click="updateStudent">修改学生</button>
<hr>
<div>{{luhan}}</div>
<button @click="updateLuhan">修改鹿晗</button>
</div>
</template>
<script>
import { unref,isProxy,isRef,isReactive, isReadonly, markRaw,
onMounted,ref,reactive, readonly, shallowReactive, toRaw } from "vue";
export default {
name: "Child2",
setup() {
let name =ref('你好')
let age=20
let data = reactive({
name: "张三",
age: 20,
});
// readonly()方法,用于返回一份只读数据
// 注意:该方法,不能将一个普通值类型数据转为只读数据
let student = readonly(data);
let updateStudent = () => {
// student.name += "!";
data.name +="!"
// 此处,student是返回出去的数据,改不了,data是原数据所以改得了。
// student本质上是个代理对象proxy,但不能改奥,一改就报错,使用场景并不多。
};
// 使用markRaw()方法,返回出来的对象,再也不能成为响应式对象。应该用的也不多奥。
let car = markRaw({
name: "奔驰",
price: 20,
});
// console.log(car);
let car2 = reactive(car);
console.log(car2);
// shallowReactive() shallowRef() 用于定义浅响应式对象。
// 只对第一层属性设置响应式。
// shallowReadonly()是浅只读,只有第一层属性是只读的。
let luhan = shallowReactive({
name: "鹿晗",
age: 30,
friend: {
name: '关晓彤',
age: 20
},
});
console.log('luhan',luhan); //luhan是个响应式对象Proxy
// toRaw()方法,用于将一个响应式对象转为普通对象。
let luhan2 = toRaw(luhan)
console.log('luhan2',luhan2);
let updateLuhan = () => {
// luhan.name = "张艺兴",
// luhan.age = 25
// 如果只是修改对象的深层属性,不会触发页面更新。
luhan.friend.name='小美'
luhan.friend.age=22
};
onMounted(() => {
console.log('name is ref',isRef(name));
console.log('data is reactive',isReactive(data));
console.log('data is proxy',isProxy(data));
console.log('student is readonly',isReadonly(student));
console.log('student is proxy',isProxy(student));
// unref()方法,用于判断对象如果是ref,就返回ref的value值;如果不是ref,直接返回值。
// 等同于: val = isRef(val) ? val.value : val
console.log(unref(name));
console.log(unref(age));
})
return {
student,
updateStudent,
luhan,
updateLuhan
};
},
};
</script>