五、toRef
与 toRefs
函数
toRef
与toRefs
的使用区别
toRef
: 将一个数据变成响应式数据。toRefs
: 将多个数据变成响应式数据。
reactive
定义的对象,如果直接用...
解构,就会失去响应式的特点,然而使用toRef() 或 toRefs()
解构,则可以保留响应式特点。总结:解构之后还具有响应式
。
let { 属性1, 属性2 } = toRefs(响应式数据)
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ user.name }}</div> </template> <script> import { defineComponent, toRef, toRefs, reactive } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm' }) // 通过 toRef 取值,保留响应式特点(通过 key 单个取值) // let name = toRef(user, 'name') // 通过 toRefs 取值,保留响应式特点 let { name } = toRefs(user) // 点击按钮 function touchButton () { // 修改值 name.value = 'xyq' } // 返回 return { user, name, touchButton } } }) </script>
六、computed
函数
computed
支持get
、set
写法,普通写法只用到了get
。
- 普通写法:返回值就是计算属性的值
import { computed } from 'vue' const 计算属性名 = computed(() => { return 响应式数据相关计算 })
- 高阶写法:
get
返回的是计算属性的值,set
监听计算属性的变化(v-model
绑定计算属性)
import { computed } from 'vue' const 计算属性名 = computed(() => { // 取值 get: () => { } // 赋值 set: (val) => { } })
- 普通写法
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ newAge }}</div> </template> <script> import { defineComponent, ref, computed } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) // 计算方法 const newAge = computed(() => { return age.value * 10 }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 } // 返回 return { age, newAge, touchButton } } }) </script>
- 高阶写法(
get
、set
组合使用)
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ newAge }}</div> </template> <script> import { defineComponent, ref, computed } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) // 计算方法 const newAge = computed({ // 取值 get: () => { return age.value * 10 }, // 赋值 set: (val) => { age.value = val / 10 } }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 } // 返回 return { age, newAge, touchButton } } }) </script>
七、watch
函数
- 监听深层次对象时,新旧对象的值都会发生变化,
watch
是惰性的,页面第一次加载时不触发watch
函数,只有监听的数据发生变化时,才会触发watch
函数。
- 使用
deep
为深层次监听 - 使用
immediate
为第一次直接触发,默认第一次不触发
- 监听格式
watch(监听参数, 新旧值变化回调, 配置)
// 监听普通属性 watch(age, (newVal, oldVal) => { console.log('变化了') }) // 监听 json 数据 watch(json, (newVal, oldVal) => { console.log('变化了') }, { immediate: true, deep: true }) // 监听多个数据 watch([age, json], (newVal, oldVal) => { console.log('变化了') }, { immediate: true, deep: true })
- 使用案例
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> </template> <script> import { defineComponent, ref, reactive, watch } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 监听普通数据 watch(age, (newVal, oldVal) => { console.log('Age 变化:', newVal, oldVal) }) // 监听深层次数据 watch(user, (newVal, oldVal) => { console.log('User 变化:', newVal, oldVal) }, { immediate: true, deep: true }) // 监听多个数据 watch([age, user], (newVal, oldVal) => { console.log('Age User 变化:', newVal, oldVal) }, { immediate: true, deep: true }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 返回 return { age, touchButton } } }) </script>
八、watchEffect
函数(高级监听)
watchEffect
是非惰性的,一开始就会自动调用一次,可配置执行时机
,还可以监听某个值之前,可以先执行一个函数。- 在页面第一次加载时就会触发,并且会一直监听追踪内部使用的响应式数据,只要追踪的响应式数据发生变化,
watchEffect
都会运行。 - 也可以监听多个参数,只是不能监听对象,因为他无法监测对象内部的变化,可能是watchEffect 无法实现深度监听吧(具体原因还不清楚)。
- 普通监听使用
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> <div @click="touchStop">停止监听</div> </template> <script> import { defineComponent, ref, reactive, watchEffect } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 普通监听使用 const stop = watchEffect(() => { console.log(age.value, user.id) }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 停止监听 function touchStop () { stop() } // 返回 return { age, touchButton, touchStop } } }) </script>
- 高级监听使用
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> <div @click="touchStop">停止监听</div> </template> <script> import { defineComponent, ref, reactive, watchEffect } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 监听值变化之前,会先执行一下函数,无论什么位置,都会先执行函数,在执行其他内容,只有在监听值发生变化时,才会触发 const stop = watchEffect((oninvalidata) => { // 在函数前面输入一下 console.log('oninvalidata - 前', age.value, user.id) // 先执行这个函数,可以用来提前处理一些需要的东西 oninvalidata(() => { console.log('before') }) // 在函数后面输入一下 console.log('oninvalidata - 后', age.value, user.id) }, { // 执行时机: pre(组件更新前)、sync(强制效果始终同步)、post(组件更新后执行) flush: 'post', // dom 加载完毕后执行 // 数据发生变化时,比上面的 oninvalidata 函数还会先调用 onTrigger(e) { console.log('onTrigger') // console.log(e) // 数据变化信息,可以在这里拿到新旧值、变化对象、变化类型、变化key // debugger // 用于 debugger 调试 } }) // 这里是上面监听的输出结果: // 1、oninvalidata - 前 1 1 // 2、oninvalidata - 后 1 1 // 3、onTrigger // 4、onTrigger // 5、before // 6、oninvalidata - 前 2 2 // 7、oninvalidata - 后 2 2 // 第 1、2 行是初始化默认调用的第一次 // 第 3、4 行是监听到值变化之后调用的,如果多个数据同时发生改变,每个数据都会调用一遍 // 第 5、6、7 行是监听到变化之后调用的,如果多个数据同时发生改变,只触发一次 // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 停止监听 function touchStop () { stop() } // 返回 return { age, touchButton, touchStop } } }) </script>
九、emit
函数 与 emits
自定义事件注册
- 子组件中所有自定义事件都需要在
emits
中进行注册,不然会报警告:
[Vue warn]: Extraneous non-emits event listeners (success1, success2) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
- 子组件自定义事件
<template> <div @click="touchButton1">点击按钮1</div> <div @click="touchButton2">点击按钮2</div> </template> <script> export default { // 申明自定义事件 emits: ['success1', 'success2'], setup (props, context) { // 点击按钮1 function touchButton1 () { context.emit('success1') } // 点击按钮2 function touchButton2 () { context.emit('success2') } // 返回 return { touchButton1, touchButton2 } } } </script>
- 父组件接受事件
<template> <Temp @success1="touchButton1" @success2="touchButton2"></Temp> </template> <script> import Temp from './Temp.vue' import { defineComponent } from 'vue' export default defineComponent({ components: { Temp }, setup () { // 点击按钮1 function touchButton1 () { console.log('点击按钮1') } // 点击按钮2 function touchButton2 () { console.log('点击按钮2') } // 返回 return { touchButton1, touchButton2 } } }) </script>
十、readonly
与 shallowReadonly
函数
readonly
为深只读,shallowReadonly
为浅只读。
十、reactive
与 shallowReactive
函数
reactive
为深劫持,shallowReactive
为浅减持。- 按上面
九、
的同类型数据,被reactive
劫持的数据,修改任何层级的数据都会刷新页面,被shallowReactive
劫持的数据,修改深层级的数据都不会刷新页面,只有第一层次的会刷新页面。
十一、toRaw
函数
toRaw
: 将一个reactive
生成的响应式对象转换为普通对象。
<script> import { defineComponent, reactive, toRaw } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm', job: { salary: 30 } }) // 点击按钮 function touchButton () { console.log(user) // Proxy {id: 1, name: 'dzm', job: {…}} const user1 = toRaw(user) // 将一个 reactive 生成的响应式对象转换为普通对象 console.log(user1) // {id: 1, name: 'dzm', job: {…}} } // 返回 return { user, touchButton } } }) </script>
十二、markRaw
函数
markRaw
:标记一个对象,使其永远不再成为响应式的数据
<template> <!-- 模板中使用 --> <div @click="touchButton1">新增数据</div> <div @click="touchButton2">叠加数据 {{ user.job && user.job.salary }}</div> </template> <script> import { defineComponent, reactive, markRaw } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm' }) // 新增数据 function touchButton1 () { // 通过 reactive 包装后,默认增加数据,是支持响应式的 // user.job = { salary: 10 } // 但是增加的数据通过 markRaw 包装之后,是不支持响应式的 user.job = markRaw({ salary: 10 }) } // 增加数值 function touchButton2 () { // user.job.salary 无论是支持或不支持响应式,值都会变化 // 但是不支持响应式,则值变页面不变,支持响应式,则值变页面也变 if (user.job) { user.job.salary += 1 } // 无论支持与否,输出值是在增加变化的 console.log(user.job.salary) } // 返回 return { user, touchButton1, touchButton2 } } }) </script>
十三、其他的后续会持续更新
- 但是基本掌握上面的也就能进入正常开发了,其他情况的现查现用就行了!