VUE3学习手札
vue3成长之路学习笔记
文章目录
- VUE3学习手札
- 前言
- 一、markRaw
- 1.1 代码示例
- 1.2 应用场景
- 1.3 拓展(toRaw)
- 1.4 实际应用
- 二、ref 和 reactive
- 三、getCurrentInstance函数
- 四、mixins混入
- 五、props
- 六、emits 事件
- 七、computed计算属性
- 八、watch监听
- 九、生命周期
前言
主要用于自己的一个备忘,对知识点的查缺补漏
一、markRaw
将一个对象标记为不可被转为代理。返回该对象本身。
从而使其不会被 reactive 包裹,也就不会成为 Vue3 中的响应式对象
1.1 代码示例
<template><div><p> 姓名: {{person.name}}</p><p> 性别: {{person.sex}}</p><p> 爱好: {{person.likes}}</p><el-button @click="change">按钮</el-button></div>
</template><script setup>
import { reactive, markRaw } from 'vue'
let person = reactive({name: '张三',sex: '男',
})
let likes = ['吃饭', '睡觉']
// 往响应式对象中新增一个likes属性,该属性是响应式
// 但是我们使用markRaw包裹后这个likes属性值是不具有响应式的
person.likes = markRaw(likes)
// 因此试图是不会更新的
let change = () => {person.likes[0] = '我要吃饭'person.likes[1] = '我要睡觉'console.log(person.likes)
}
</script>
// 视图不会发生改变!!!
我们通过 markRaw 方法将 state.obj 标记为非响应式对象。这样做可以避免对 obj 的修改引起意外的响应式更新。
需要注意的是,一旦一个对象被标记为“非响应式”,它就无法再被 reactive 进行包裹成为响应式对象。所以在使用 markRaw 方法时,我们需要确保这个对象在后续的代码中不需要作为响应式对象来使用或者监听其变化
1.2 应用场景
- 有些值不应被设置成响应式时,例如复杂的第三方类库等
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
- 在动态渲染组件的时候我们就可以使用 markRaw 包裹。
1.3 拓展(toRaw)
markRaw 是声明(标记)一个不响应式的数据,
toRaw是将一个响应式的数据转化(还原)成不响应式的数据
代码示例
<template><div><el-button @click="change">自增</el-button>数值变化:<p>{{ state.count }}</p></div>
</template><script setup>
const state = reactive({count: 0,
})// 获取原始对象
const rawState = toRaw(state)// 验证原始对象与包装后的对象是否相等
console.log(rawState === state) // false
function change() {// 改变原始对象的值rawState.count += 1// 验证包装后的对象是否也受到了改变console.log(state.count) // 1
}
</script>
// 视图不会发生改变!!!
1.4 实际应用
代码示例:
<template><el-tabs v-model="tabName"@tab-click="tabClick"><el-tab-pane label="组件A"name="a"></el-tab-pane><el-tab-pane label="组件B"name="b"></el-tab-pane><el-tab-pane label="组件C"name="c"></el-tab-pane><el-tab-pane label="组件D"name="d"></el-tab-pane></el-tabs><component :is="currentComponent[tabName]"></component>
</template><script setup>
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
import ComponentC from './ComponentC'
import ComponentD from './ComponentD'const tabName = ref('ComponentA')
const currentComponent = {a: markRaw(ComponentA),b: markRaw(ComponentB),c: markRaw(ComponentC),d: markRaw(ComponentD),
}
/* tab切换 */
function tabClick(val) {console.log('val.paneName', val.paneName, currentComponent)
}
</script>
二、ref 和 reactive
从vue2到vue3,组合式api语法结构上,很重要一块data内的声明变量变成了,使用ref()或使用reactive()来进行包裹,以实现响应式变化,但是有时候在使用上会有一些心智负担,什么时候使用ref(),什么时候使用reactive(),官方文档的释义说明:响应式:核心
整个 data 这一部分的内容,你只需要记住下面这一点。
以前在 data 中创建的属性,现在全都用 ref() 声明。
在 template 中直接用,在 script 中记得加 .value
Vue3 里,还提供了一个叫做 reactive 的 api。
但是我的建议是,你不需要关心它。绝大多数场景下,ref 都够用了
对比
Ref
- 基本用法:Ref用于创建一个响应式的基本数据类型,如数字、字符串等。通过ref函数创建,访问和修改数据值需要使用.value。
- 在setup()中使用:在setup()函数中,我们可以使用ref来创建响应式数据,并在模板中使用。
- < script setup >语法:< script setup >语法是Vue3推荐的一种写法,可以在单文件组件中更简洁地使用ref。
- 为何使用ref:Ref适用于管理简单的基本数据类型,如数字、字符串等。
示例:
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button></div>
</template><script setup>
import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++;
};
</script>
Reactive
- 基本概念:Reactive用于创建一个响应式对象,可以包含多个属性。通过reactive函数创建,对象的任何属性变化都会被检测到。
- 在模板中使用Reactive:在模板中可以直接使用响应式对象,对对象的属性进行访问和修改。
- 深层响应式:Reactive会递归地将对象的所有嵌套属性都变成响应式。
- 与ref区别:Ref适用于简单数据类型,而Reactive适用于对象,可以处理对象的多个属性。
- 为何使用Reactive:Reactive适用于管理复杂对象,使整个对象都变成响应式。
示例:
<template><div><p>User Name: {{ user.name }}</p><p>Age: {{ user.age }}</p><p>Order ID: {{ order.orderId }}</p></div>
</template><script setup>
import { reactive } from 'vue';const user = reactive({name: 'Alice',age: 30
});const order = reactive({orderId: '123456'
});
</script>
比较和选择
性能和适用场景:Ref比Reactive轻量,适合简单数据;Reactive适合处理复杂对象。根据具体场景选择。
对象的处理:Ref处理单一数据,Reactive处理对象及其嵌套属性。
局限性和注意事项:Ref不能直接处理对象,Reactive在处理大型数据对象时可能影响性能。
总结:使用Ref处理简单数据类型,Reactive处理复杂对象
三、getCurrentInstance函数
【说明】:
getCurrentInstance这个函数来返回当前组件的实例对象,也就是当前vue这个实例对象,
【注意】:
开发中只适用于调试! 不要用于线上环境,否则会有问题!
使用上:
const { proxy } = getCurrentInstance()
使用解构赋值 { proxy } 从这个对象中提取了 proxy 属性。proxy 是一个被包装过的对象,它提供了对当前组件内属性和方法的访问,使用 proxy 来访问当前组件的属性和方法,而无需显式引用当前组件的实例对象。这使代码更加简洁和容易理解。
或者:
const instance = getCurrentInstance()
【使用场景】
- 获取dom节点
proxy.$refs['deptTreeRef']
- 调取公共方法
proxy.resetForm('queryRef')
在main.js文件中进行挂载全局方法,在实例中直接点调取
- 调用vuex或者nextTick等全局api
四、mixins混入
【说明】:在 Vue 2 中,mixins 是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了 mixins 支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式,之所以保留主要是为了向下兼容vue2的选项式语法。本次主要针对,编写vue2转vue3组合式语法,对照两种不同的写法,方便转化。
示例:增删改查页面表单项过多时添加展开/收起快捷按钮,
【选项式语法】
generalMixins.js
export default {data () {return {unpackFlag: false, // 是否展开unpackText: '展开', //展开、收起};},methods: {/* 展开收起事件 */unpackChange () {this.unpackFlag = !this.unpackFlag;this.unpackText = this.unpackFlag ? '收起' : '展开';},},
};
调用:
import generalMixins from '@/utils/generalMixins'mixins: [generalMixins],
【组合式语法】
combineMixins.js
import { ref } from 'vue';export function combineMixins () {const unpackFlag = ref(false);// 是否展开const unpackText = ref('展开');//展开、收起function unpackChange () {unpackFlag.value = !unpackFlag.value;unpackText.value = unpackFlag.value ? '收起' : '展开';}return {unpackFlag,unpackText,unpackChange};
}
调用:
import { combineMixins } from '@/utils/combineMixins'// 展开收起
const { unpackText, unpackFlag, unpackChange } = combineMixins()
页面调用:
五、props
声明 props 我们可以用 defineProps()
【写法对比】
vue2写法
<template><div>{{ foo }}</div></template><script>export default {props: {foo: String,},created() {console.log(this.foo);},}</script>
vue3写法:
<template><div>{{ foo }}</div></template><script setup>// 注意这里const props = defineProps({foo: String})// 在 script 标签里使用console.log(props.foo)</script
六、emits 事件
与 props 相同,声明 emits 我们可以用 defineEmits()
【vue2写法】
<template><div @click="onClick">这是一个div</div></template><script>export default {emits: ['click'], // 注意这里methods: {onClick() {this.$emit('click'); // 注意这里},},}</script>
【vue3写法】
<template><div @click="onClick">这是一个div</div></template><script setup>// 注意这里const emit = defineEmits(['click']);const onClick = () => {emit('click') // 注意这里}</script>
七、computed计算属性
【vue2写法】
<template><div><span>{{ value }}</span><span>{{ reversedValue }}</span></div></template><script>export default {data() {return {value: 'this is a value',};},computed: {reversedValue() {return value.split('').reverse().join('');},},}</script>
【vue3写法】
<template><div><span>{{ value }}</span><span>{{ reversedValue }}</span></div></template><script setup>import {ref, computed} from 'vue'const value = ref('this is a value')// 注意这里const reversedValue = computed(() => {// 使用 ref 需要 .valuereturn value.value.split('').reverse().join('');})</script>
八、watch监听
Vue3 中,watch 有两种写法。一种是直接使用 watch,还有一种是使用 watchEffect。
两种写法的区别是:
watch 需要你明确指定依赖的变量,才能做到监听效果。
而 watchEffect 会根据你使用的变量,自动的实现监听效果。
【vue2写法】
<template><div>{{ count }}</div><div>{{ anotherCount }}</div><button @click="onClick">增加 1</button></template><script>export default {data() {return { count: 1,anotherCount: 0,};},methods: {onClick() {this.count += 1;},},watch: {count(newValue) {this.anotherCount = newValue - 1;},},}</script>
【vue3写法–watch】
<template><div>{{ count }}</div><div>{{ anotherCount }}</div><button @click="onClick">增加 1</button></template><script setup>import { ref, watch } from 'vue';const count = ref(1);const onClick = () => {count.value += 1;};const anotherCount = ref(0);// 注意这里// 需要在这里,// 明确指定依赖的是 count 这个变量watch(count, (newValue) => {anotherCount.value = newValue - 1;})</script>
【vue3写法–watchEffect】
<template><div>{{ count }}</div><div>{{ anotherCount }}</div><button @click="onClick">增加 1</button></template><script setup>import { ref, watchEffect } from 'vue';const count = ref(1);const onClick = () => {count.value += 1;};const anotherCount = ref(0);// 注意这里watchEffect(() => {// 会自动根据 count.value 的变化,// 触发下面的操作anotherCount.value = count.value - 1;})</script>
九、生命周期
vue2不列举了
【vue3–选项式】
<template><div></div></template><script>export default {beforeCreate() {},created() {},beforeMount() {},mounted() {},beforeUpdate() {},updated() {},// Vue2 里叫 beforeDestroybeforeUnmount() {},// Vue2 里叫 destroyedunmounted() {},// 其他钩子不常用,所以不列了。}</script>
【vue3–组合式】
<template><div></div></template><script setup>import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,} from 'vue'onBeforeMount(() => {})onMounted(() => {})onBeforeUpdate(() => {})onUpdated(() => {})onBeforeUnmount(() => {})onUnmounted(() => {})</script>