目录
[9、watch vs watchEffect监听方式对比](#9、watch vs watchEffect监听方式对比)
[11、watch vue3与vue2的对比](#11、watch vue3与vue2的对比)
[3、Provide / Inject(祖先传后代)](#3、Provide / Inject(祖先传后代))
一、ref、reactive创建响应式对象
1、ref()
在Vue 3中,ref是一个函数,用于创建一个响应式引用。它可以定义基本数据类型(如字符串、数字、布尔值等)。虽然也可用于创建对象和数组,但更推荐使用reactive。使用ref时,需要通过.value属性来访问和修改数据。
const copy_address_type = ref('') // 地址同步文案
const exemption_fax = ref(0.00) //消费税
const exemption_no_show = ref(false)
2、reactive()
reactive它更适用于创建对象和数组。reactive会将整个对象或数组转换为响应式的,这意味着对象或数组中的每个属性都会被代理。
const form_data = reactive({
id: '', type:'', first_name: '',last_name: '',email: '',phone: '',institution: '',
postcode: '',address: '',city: '',state_input: '',state: '',country: '', set_copy_address: ''
})
const country_data = ref([])
3、ref和reactive的区别
a.数据类型:ref适用于基本数据类型及复杂对象,而reactive主要用于复杂对象及嵌套数据结构。
****b.****访问方式:ref通过.value属性访问,而reactive直接通过属性访问。
****c.****响应性追踪:ref追踪单个独立的引用,reactive追踪整个对象及其内部属性。
****d.****可变性:ref的引用值可以重新赋值,而reactive对象本身是不可重新赋值的,只能修改其内部属性。
二、computed计算属性
1、什么是计算属性computed
computed是用于基于其他响应式数据动态计算新数据的工具,具有缓存机制,仅在依赖数据变化时重新计算,可显著提升性能,如果依赖的数据不变化,computed永远不会改变。
核心特性
依赖缓存:仅在依赖数据(如响应式引用)变化时重新计算,避免重复执行。
声明式定义:通过computed函数定义,支持复杂逻辑复用。
性能优化:减少模板渲染时的计算开销,提升渲染效率。
<template>
<div>{{ double }}</div>
<input v-model="count" />
</template>
<script setup>
import { ref, computed } from 'vue';
const count = ref(1);
const double = computed(() => count.value * 2);
</script>
上面的例子,当文本框的count发生改变时,double数据也会发生变化。
2、计算属性computed和函数方法的区别
function doubleFunc(count) {
let double = count * 2
return double
}
若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 double 都会立即返回先前的计算结果,而不用重复执行doubleFunc函数。
这也很好的解释了下面的计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖,它不会变化。
const now = computed(() => Date.now())
3、计算属性computed的优势
A.简洁高效:通过计算属性computed可以简洁高效地实现基于其他属性计算的属性,避免了重复计算和代码冗余。
B.响应式更新:计算属性computed会自动响应依赖的变化而更新,保持界面和数据的同步。
C.缓存机制:计算属性computed会缓存计算结果,只有在相关依赖发生改变时才会重新计算,提高了性能和效率。
三、watch监听函数
1、什么是watch?
watch是Vue中用于监听数据的变化并执行相应的操作。
当被监听的数据发生变化时,会触发一个回调函数,我们可以在这个回调函数中执行一些逻辑操作。
watch适用于需要在数据变化时执行异步或较复杂的场景。
2、基本语法
export default {
setup() {
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`);
});
return { count,};
}
};
3、深度监听
对于对象或数组的监视,默认情况下,Vue不会递归地监视对象内部属性的变化。如果你需要深度监视,vue2中可以设置deep选项为true。Vue3中你可以使用watchEffect或者watch函数,并通过提供一个回调函数来访问和监视这些深层属性。
Vue2深度监听
export default {
data() {
return {
user: {
name: '张三',
age: 25
}
}
},
watch: {
user: {
handler(newValue, oldValue) {
console.log('user对象变化了', newValue, oldValue)
},
deep: true // 开启深度监听
}
}
}
VUE3深度监听
import { ref, watchEffect } from 'vue';
const obj = ref({
nested: {
prop: 'value'
}
});
watchEffect(() => {
console.log(obj.value.nested.prop); // 访问深层属性
});
// 更新深层属性时,控制台将显示新值
obj.value.nested.prop = 'new value';
深度监听原理:

4、使用字符串路径监听嵌套属性
const city = computed(() => state.user.address.city);
watch(city, (newValue, oldValue) => {
console.log(`City changed from ${oldValue} to ${newValue}`);
});
5、动态添加watcher
如果你需要在组件的生命周期内动态添加watcher,可以使用Vue实例的$watch方法
export default {
mounted() {
this.$watch('message', (newVal, oldVal) => {
console.log(`message changed from ${oldVal} to ${newVal}`);
});
}
6、立即执行watcher
默认情况下,watch只会在数据变化时触发回调函数,而不会在初始化时执行。
如果需要在初始化时就执行一次回调函数,可以设置immediate: true。
watch(
() => obj.value.nested.prop, // 监视深层属性
(newValue, oldValue) => {
console.log(`Prop changed from ${oldValue} to ${newValue}`);
},
{ immediate: true } // 立即执行一次回调
);
7、一次性监听
可以使用once: true选项来设置只监听一次变化
// 使用 once 选项让 watch 只监听一次
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
}, { once: true });
8、停止watcher
当你使用$watch方法添加watcher时,可以通过返回的取消函数来停止watcher:
const count = ref(0);
let stopWatch;
stopWatch = watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
});
onUnmounted(() => {
stopWatch(); // 确保在组件卸载时停止watcher
});
9、watch vs watchEffect监听方式对比
watch需要明确指定要监听的数据源,而watchEffect会自动收集其内部所使用的所有响应式依赖。
watch可以访问被监听状态的前一个值和当前值,而watchEffect只能访问当前值。
watchEffect会在组件初始化时立即执行一次,相当于设置了immediate: true的watch。
const count = ref(0)
const message = ref('Hello')
// watchEffect会自动监听回调函数中使用的所有响应式数据
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`)
})
// 等价于以下watch写法
watch([count, message], ([newCount, newMessage]) => {
console.log(`count: ${newCount}, message: ${newMessage}`)
}, { immediate: true })
10、watch与computed的区别
|------------|------------------|------------------|
| 特性 | watch | computed |
| 用途 | 监听数据变化并执行副作用 | 计算并返回新值 |
| 缓存 | 无缓存机制 | 有缓存,只在依赖变化时重新计算 |
| 返回值 | 无返回值 | 有返回值 |
| 适用场景 | 数据变化时执行异步操作或复杂逻辑 | 依赖其他数据计算出新值 |
| 执行时机 | 数据变化后执行 | 依赖变化时立即计算新值 |
11、watch vue3与vue2的对比
// Vue 2 选项式 API
watch: {
user: {
handler(newVal) { /*...*/ },
deep: true
}
}
// Vue 3 组合式 API(更灵活)
const user = reactive({/*...*/})
watch(user, (newVal) => {/*...*/}, { deep: true })
12、性能优化建议
避免过度深度监听:只对必要对象开启
使用精确监听路径
watch(() => user.address.city, (newCity) => {...})
及时清理监听
const stopWatch = watch(...)
onUnmounted(stopWatch) // 组件卸载时停止监听
13、完整示例
const user = reactive({
id: 1,
info: {
name: '张三',
address: {
city: '北京',
street: '朝阳区'
}
}
})
// 深度监听整个用户对象
watch(
user,
(newUser) => {
console.log('用户信息已修改,自动保存...')
autoSave(newUser)
},
{ deep: true, immediate: true }
)
// 精确监听城市变化
watch(
() => user.info.address.city,
(newCity) => {
updateMap(newCity)
}
)
四、组件之间的通信
1、Props(父传子)
父组件通过props向下传递数据给子组件。这是最常见的父子组件通信方式。
父组件
<template>
<ChildComponent :message="parentMessage" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from 'vue';
const parentMessage = ref('Hello from parent');
</script>
子组件
<template>
<div>{{ message }}</div>
</template>
<script setup>
defineProps({
message: String
});
</script>
2、Emit(子传父)
子组件通过emit向父组件发送事件和数据。
子组件
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['updateMessage']);
function sendMessage() {
emit('updateMessage', 'Hello from child');
}
</script>
父组件
<template>
<ChildComponent @updateMessage="handleMessage" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from 'vue';
const childMessage = ref('');
function handleMessage(msg) {
childMessage.value = msg;
}
</script>
3、Provide / Inject(祖先传后代)
provide和inject可以用于跨多级组件传递数据,非常适合在深层嵌套的组件结构中通信。
祖先组件
<template>
<DescendantComponent />
</template>
<script setup>
import { provide, ref } from 'vue';
import DescendantComponent from './DescendantComponent.vue';
const message = ref('Hello from ancestor');
provide('message', message); // 提供数据给后代组件使用
</script>
后代组件
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { inject } from 'vue';
const message = inject('message'); // 注入数据从祖先组件接收数据
</script>
4、Vuex 全局事件总线 (状态管理)
对于更复杂的应用,可以使用Vuex进行状态管理,实现任意跨组件的通信。Vuex提供了一个集中存储管理应用所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
使用:
第一步:#src/store/index.js 创建vuex模块文件,开始使用vuex
在一个模块化的打包系统中,您必须显式地通过 `Vue.use()` 来安装 Vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
第二步:#实例化Vuex.store ,并进行相关配置
export default new Vuex.Store({
state: {
//存储状态
},
mutations: {
//变更store中的状态
},
actions: {
//类似于mutation,
//action提交的是mutation,而不是直接变更状态
//action可以包含异步操作
},
getters:{
//state的派生状态
},
modules: {
//将store分割成模块
}
})
第三步:#在main.js中,vue实例对象中注册store
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
每一个 Vuex 应用的核心就是 store(仓库)。"store"基本上就是一个容器,它包含着你的应用中大部分的状态 (state) 。