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))
})
