目錄
一、ref、reactive創建響應式對象
1、ref()
2、reactive()
3、ref和reactive的區別
二、computed計算屬性
1、什么是計算屬性computed
2、計算屬性computed和函數方法的區別
3、計算屬性computed的優勢
三、watch監聽函數
1、什么是watch?
2、基本語法
3、深度監聽
4、使用字符串路徑監聽嵌套屬性
5、動態添加watcher
6、立即執行watcher
7、一次性監聽
8、停止watcher
9、watch vs watchEffect監聽方式對比
10、watch與computed的區別
11、watch vue3與vue2的對比
12、性能優化建議
13、完整示例
四、組件之間的通信
1、Props(父傳子)
?2、Emit(子傳父)
3、Provide / Inject(祖先傳后代)
4、Vuex全局事件總線(狀態管理)
一、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 * 2return 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)。