mixin 和 extends
mixin 是代码的共用,vuex 是数据的共用,extends 是拓展,拿其他的组件来用
mixin 混入
(可以参考本人文章 Vue2 里面的介绍)[juejin.cn/post/703925…]
重点:组件使用 mixin 和内部自定义的属性或函数冲突时的优先级
- 基本类型变量以实例为主
- 引用类型:键值替换
- 生命钩子:队列处理,混入先出
- 函数方法:重载
extends 继承
引入其他组件,能够直接使用组件内部的方法和属性
<template>
<div>
HOme
<!-- 继承data内的属性,以及其他的方法 -->
<h2>extends: {{message}}</h2>
<button @click="foo">baseFoo</button>
</div>
</template>
<script>
import BasePage from "./BasePage.vue"
export default {
extends: BasePage
}
</script>
optionsAPI 和 compositionAPI 的区别
optionsAPI
- 不同的类型放在不同的 option 里面,例如:data、computed、methods 等
- 当一个 option 中代码过多,来回查看时,阅读性很差;data 属性归在一处,methods 归在一处,查询需要来回滚动
compositionAPI
- 不用书写过多属性,需要时将对应 api 导入进来即可(watch、computed 等)
- 代码能够在同一处(setup 函数中)书写,方便代码维护,增强可读性
setup 函数
函数自带两个属性,以及函数返回一个对象
setup(props, context) {
return {}
}
函数的参数
-
@param {*} props: 父组件传递过来的属性(proxy 对象)
-
@param {*} context: 包含三个属性(因此 context 可以解构成 { attrs, slots, emit })
- attrs(非 props 的属性)
- slots(父组件传递的插槽)
- emit(发出事件)
函数的返回值
return {}; 返回的对象能够替换data的属性,因此data这以optionsAPI就可以省略。
setup中的this
在setup函数中打印this,输出为undefined
console.log(this); // setup中没有绑定this: 输出undefined
vue官方解释: 在setup中避免使用this,在setup调用钱,data、computed等都还没有被解析;所以无法在setup中获取到this。
在生命周期中执行,setup在所有之前
compositionAPI使用
reactive
reactive 能够在setup中使用该api返回一个proxy响应式对象
const info = reactive({
name: "it is reactive"
})
console.log(info.name) // 使用值按正常对象操作
ref
ref 能够让基本数据类型成为ref响应式对象(reference)
ref对象的值通过 ref.value 进行获取
// 使用简单数据类型,可使用 ref API(reference:引用)
let counter_ref = ref(100); // ref对象引用
counter_ref.value++; // 获取引用的值,需要拿到对象内部的值value
console.log(counter_ref.value);
在template模板里面,会存在解包操作,因此页面上并不需要使用 .value获取, 可以直接使用
注意:搭配标签内部的ref能够获取到对应元素
<template>
<div>
<h2 ref="title">喝水水</h2>
</div>
</template>
<script>
import { ref } from "vue"
export default {
setup() {
// 给ref传入null值,能够获取到页面上ref指定的元素或组件
const title = ref(null)
return {
title
}
}
};
</script>
readonly
readonly 返回一个响应式对象的只读副本(readONLY修饰的对象仍然是 proxy对象;只是劫持了set方法)
在对于一些父组件传来只读的对象可以进行该操作,不会对父组件的属性产生影响
import { reactive, ref, readonly } from "vue";
const info2 = reactive({
name: "it is readonly"
})
// 得到一个info2(ref 或 reactive)的只读副本
// 在对于一些父组件传来只读的对象可以进行该操作
const readOnlyInfo2 = readonly(info2)
const increment = () => {
// info2.name = "changed" // 修改数据源本体,正常调用set方法
// 当对只读副本进行修改时,会爆出警告不能修改只读数据
readOnlyInfo2.name = "Is it readonly?"
};
computed 和 watch
computed 计算属性
使用vue3的compositionAPI的方式引入,有两种用法
- 传入一个回调函数:默认将回调函数放在计算属性的get函数上,使用的时候自动调用get函数
- 传入get和set属性:能分别给计算属性进行拦截,对于获取和赋值时进行操作
import { ref, computed } from "vue";
setup() {
const firstName = ref("kobe");
const lastName = ref("Bryant");
// computed 返回一个 ref对象
// 1.用法一:传入一个getter函数
// const fullName = computed(() => firstName.value + " " + lastName.value);
// 2.用法二:传入一个对象,对象包含getter、setter
const fullName = computed({
get: () => firstName.value + " " + lastName.value,
set(newValue) {
const names = newValue.split(" ");
firstName.value = names[0]
lastName.value = names[1]
}
})
const changeName = () => {
// firstName.value = "james"
// lastName.value = "jack"
fullName.value = "bluse li"
}
return {
fullName,
changeName
};
},
watch 侦听器
在vue3中使用时,需传入侦听对象以及对应的回调函数,可以侦听单个或多个对象
import { ref, watch, reactive } from "vue";
-
单个对象——两个参数:
-
(getter 函数:必须引用一个响应式对象) / (响应式对象 reactive、ref)
-
回调函数:监听到变化执行操作,两个参数(新,旧)
- 回调函数返回值根据第一个参数的不同而不同
- ref对象返回的是 value,reactive返回的是proxy对象
const info = reactive({ name: "kobe", age: 18, }); const name = ref("code"); watch( // name, // ref // () => info.name, // getter // info, // reactive () => { // 结构返回后,回调函数返回是普通对象,而不是proxy对象 // 结构出来的并不会深度侦听,需要添加对应的参数 deep return { ...info }; }, (newValue, oldValue) => { console.log(newValue, oldValue); }, { deep: true, immediate: true // 立即执行 } ); -
-
侦听多个数据——两个参数
- getter函数
- 回调函数
watch( // [info, name], () => [({...info}), name], // (newValue, oldValue) => { // // 多个数据源 // console.log(newValue, oldValue); // } // 解构 ([newInfo, newName], [oldInfo, oldName]) => { console.log(newInfo, oldInfo); console.log(newName, oldName); } )
watchEffect
watchEffect也能够进行侦听——自动收集响应式的依赖
当内部使用的响应式对象发生了改变,就会执行一次(其他的改变不会有影响)
首次声明时也会执行
示例:侦听上面ref绑定的title元素
// 当内部使用的响应式对象发生了改变,就会执行一次(其他的改变不会有影响)
// 默认立即执行
watchEffect(() => {
console.log(title.value);
}, {
// 等到dom挂载后再执行
// 不写该参数的话,上面的打印会执行会打印两次;
// 第一次是null值,页面还未挂载获取不到节点
flush: "post"
})
常用于清除副作用
// 返回一个函数,调用时可以停止侦听
const stop = watchEffect((onInvalidate) => {
// 根据name来发送网络请求是如果出错,数据发生异常 —— 副作用
onInvalidate(() => {
// 用这个函数清除掉副作用
console.log("onInvalidate");
})
console.log("name:", name.value);
console.log("Age:", age.value);
});
stop() // 调用回调函数,随时可以停止侦听
生命周期
compositionAPI直接使用on + 周期调用钩子函数
import { onMounted, onUpdated, onUnmounted, ref } from "vue"
export default {
setup() {
// 不推荐在beforeCreate和created里面进行操作
// setup在二者之前前执行
const counter = ref(0)
function increment () {
counter.value ++
}
onMounted(() => {
console.log("App onMounted");
})
// 生命周期函数可以多次使用,按顺序执行
// 队列,先入先出
onMounted(() => {
console.log("App onMounted2");
})
onUpdated(() => {
console.log("App onUpdated");
})
onUnmounted(() => {
console.log("App onUnMounted");
})
return {
increment,
counter
}
}
}
Provide 和 Inject
父组件给后代组件传递数据,能够动态的显示父组件中的数据(传输的数据建议使用readonly只读复制个副本)
父组件
<template>
<div>
App
<button @click="increment">increment</button>
<home></home>
</div>
</template>
<script>
import Home from "./Home.vue"
import { provide, ref, readonly } from "vue"
export default {
components: {
Home
},
setup() {
const name = "刘德华"
const counter = ref(0)
const increment = () => counter.value ++
// 子组件中不建议去修改父组件传来的值
// 用readonly包裹住
provide("name", readonly(name))
provide("counter", readonly(counter))
return {
increment
}
}
}
</script>
子组件
<template>
<div>
Home___{{name}}
<h2>APP里面的counter:{{counter}}</h2>
<button @click="increment">home ++</button>
</div>
</template>
<script>
import { inject } from "vue"
export default {
setup() {
const name = inject("name")
const counter = inject("counter")
const increment = () => counter.value ++
return {
name,
counter,
increment
}
}
}
</script>
nextTick
更新DOM元素后触发nextTick内部的回调函数:将nextTick加入到vue所处的微任务队列的最后面执行
若使用onUpdated生命周期函数的话,无论什么更新都会触发,会影响性能
示例:给标签添加内容,改变其高度
<template>
<div>
<h2 style="width: 100px" ref="title">{{ message }}</h2>
<button @click="changeMsg">changeMes</button>
</div>
</template>
<script>
import { ref, nextTick } from "vue";
export default {
setup() {
const message = ref("哈哈哈哈哈哈哈");
const title = ref(null);
const changeMsg = () => {
message.value += message.value;
// 输出的还是原来的高度
console.log(title.value.offsetHeight);
// 使用nextTick 在标签添加内容后输出变化之后的正确高度
nextTick(() => {
console.log(title.value.offsetHeight);
});
};
return {
message,
changeMsg,
title,
};
},
};
</script>