vue2的響應式是通過object.defineproperty實現的,JavaScript對象傳入vue實例時,vue會遍歷對象的所有property,並通過object.defineproperty把這些property轉化為getter和setter,數據發生變化時,就會觸發視圖的更新;不過這種方式有點缺陷,就是不能監測到對象屬性的添加和刪除,因此需要用vue.set()來添加和刪除。並且也不能監聽數組的變化;
而vue3則很好的解決了這個問題,Vue3 利用 Proxy(代理) 實現數據讀取和設置攔截,在攔截 trap 中實現數據依賴收集和觸發視圖更新的操作。
vue2和vue3在寫法上有什么變化?
90%的寫法完全一致,只是部分發生了改變:
1、Vue 3 的 Template 支持多個根標簽,Vue 2 不支持
2、Vue 3 有 createApp(),而 Vue 2 的是 new Vue()
createApp(組件),new Vue({template, render})
3、v-model代替以前的v-model和.sync
vue3中v-model的用法要求:props屬性名任意,這里假設為 x,那么事件名必須為“update:x”
> <Switch :value="y" @update:value="y=$event"/>
> vue2中的寫法:<<Switch:value.sync=“y”/>>
> vue3中的寫法:<<Switch v=model:value="y">>
4、context.emit
新增context.emit,與this.$emit(vue3中只能在方法里使用)作用相同
context.emit的用法
setup(props, context) { const toggle = () => { context.emit('update:value', !props.value) } return { toggle }
5. Vue3中的屬性綁定
默認所有屬性都綁定到根元素
使用inheritAttrs: false可以取消默認綁定
使用attrs或者context.attrs獲取所有屬性
使用v-bing="$attrs"批量綁定屬性
使用 const {size, level, …rest} = context.attrs 將屬性分開
使用場景
在vue2中我們在父組件綁定click事件,子組件必須內部觸發click,而vue3中在父組件綁定子組件的根元素上也會跟着綁定
ButtonDemo.vue
<div> <Button @click="onClick" @focus="onClick" size="small">你好</Button> </div> setup() { const onClick = () => { console.log("aaa") } return {onClick} },
Button.vue
<template> <div> <button> <slot/> </button> </div> </template>
上面的代碼Button的click事件會默認傳給最外層元素div上綁定,怎么取消這個默認綁定呢?我們就需要使用inheritAttrs:false,使用v-bind=”$attrs”可以把內層的事件綁定到外層想綁定的元素上
Button.vue
<template> <div> <button v-bind="$attrs"> <slot/> </button> </div> </template> <script lang="ts"> export default { inheritAttrs: false } </script>
如果想要一部分屬性綁定在button上一部分在div上就需要在setup里設置
Button.vue
<template> <div :size="size"> <button v-bind="rest"> <slot/> </button> </div> </template> <script lang="ts"> import {SetupContext} from 'vue' export default { inheritAttrs: false, setup(props: any, context:SetupContext ) { const {size, ...rest} = context.attrs return {size, rest} } } </script>
props和attrs的區別
props要先聲明才能取值,attrs不用先聲明
props不包含事件,attrs包含
props沒有聲明的屬性,會跑到attrs里
props支持string以外類型,attrs只有string類型
export default{ inheritAttrs:false, setup(props:any,context:SetupContext){ console.log({...props}) **==>>{}** const {size,...rest} = context.attrs return {size,rest} export default{ inheritAttrs:false, props:{ size:String } setup(props:any,context:SetupContext){ console.log({...props}) **==>>{size:small}**
第一張圖里因為沒有聲明props所以是空對象,第二個聲明了size,所以只得到了size
6.slot具名插槽的使用
vue2中的用法
子組件
<slot name="title">
父組件
<template slot="title"> <h1>哈哈哈</h1> </template>
vue3中子組件用法不變,父組件需要使用v-slot:插槽名
父組件
<template v-slot:title> <h1>哈哈哈</h1> </template>
7. Teleport傳送門組件
<Teleport to="body"> 需要傳送到body下面的內容 </Teleport>
8. vue3中動態掛載組件的方法
通過引入h函數第一個參數是組件,第二個是元素的屬性(第一個參數組件的props,也就是直接可以在使用組件的時候傳入的屬性),第三個是插槽的屬性。
其中我們在render里監聽我們v-model綁定的update事件的時候,需要使用onUpdate:屬性名
import {createApp, h} from 'vue' import Dialog from './Dialog.vue' export const openDialog = (options: Options) => { const {title, content} = options const div = document.createElement('div') document.body.append(div) const app = createApp({ render() { return h(Dialog, { visible: true, cancel: () => {}, 'onUpdate:visible': (newValue: boolean) => { if (newValue === false) { app.unmount(div) } } }, {title, content}) } }) app.mount(div) }
9. watchEffect用來代替生命周期里的onMounted和onUpdated
初始化頁面的時候watchEffect里的代碼會執行,當watchEffect里的數據有更新的時候同樣會執行
const count = ref(0) watchEffect(() => console.log(count.value)) // -> logs 0 setTimeout(() => { count.value++ // -> logs 1 }, 100)
注意watchEffect第一次運行是在組件掛載之前,如果需要訪問DOM需要將我們的watchEffect放在onMounted里
onMounted(() => {
watchEffect(() => console.log(count.value))
})