- Vue 3 的 Template 支持多個根標簽,Vue 2 不支持
- Vue 3 有 createApp(),而 Vue 2 的是 new Vue()
createApp(組件),new Vue({template, render}) - v-model代替以前的v-model和.sync
vue3中v-model的用法
要求:
3.1. props屬性名任意,假設為x
3.2. 事件名必須為"update:x"
效果:
<Switch :value="y" @update:value="y=$event"/>
vue2中的寫法
<Switch :value.sync="y"/>
vue3中的寫法
<Switch v-model:value="y"/>
- context.emit
新增context.emit,與this.$emit(vue3中只能在methods里使用)作用相同- context.emit用法
import {SetupContext } from 'vue'
setup(props: Prop, context: SetupContext) {
const toggle = () => {
context.emit('input', !props.value)
}
return {toggle}
}
- Vue3中的屬性綁定
默認所有屬性都綁定到根元素
使用inheritAttrs: false可以取消默認綁定
使用attrs"批量綁定屬性
使用 const {size, level, ...rest} = context.attrs 將屬性分開
5.1 使用場景
在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上綁定,如果我們要指定click的區域為button元素的話我們就需要使用inheritAttrs
- 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>
5.2. props和context.attrs的區別
- props要現在當前組件props屬性里聲明才能取值,attrs不用先聲明
第張圖里因為沒有聲明props所以是空對象,第二個聲明了size,所以只得到了size
props不包含事件,attrs包含
我們沒有辦法在props里聲明click這一類的事件props沒有聲明的屬性,會跑到attrs里
上圖中我們在props里聲明了size,所以attrs里就沒有size了
- 當我們在html標簽中只寫屬性而不賦值的時候,props支持string以外的類型,attrs只有string類型
上面第一張圖里我們在html里只寫屬性不賦值的情況下其實disabled是Bolean類型的,但我們通過attrs卻得到的是空字符串,而第二張我們通過props指定了diabled為Boolean值我們就可以通過props拿到我們需要的true
6.slot具名插槽的使用
vue2中的用法
子組件
<slot name="title">
父組件
<template slot="title">
<h1>哈哈哈</h1>
</template>
vue3中子組件用法不變,父組件需要使用v-slot:插槽名
父組件
<template v-slot:title>
<h1>哈哈哈</h1>
</template>
- Teleport傳送門組件
<Teleport to="body">
需要傳送到body下面的內容
</Teleport>
- 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)
}
- 父組件里獲取子組件內容,渲染子組件
在父組件的setUp里通過context.slots.default()
拿到子組件數組,然后通過component組件渲染
比如:
- TabsDemo.vue
<Tabs>
<Tab title="導航1">內容1</Tab>
<Tab title="導航2">內容2</Tab>
</Tabs>
- Tabs.vue
<template>
<component v-for="(tab, index) in defaults" :key="index" :is="tab"></component>
</template>
<script lang="ts">
import {SetupContext} from 'vue'
export default {
setup(props, context: SetupContext) {
const defaults = context.slots.default()
return {
defaults
}
}
}
</script>
vue3中所有的組件最后都會導出一個對象這個對象就是我們的子組件里的type(context.slots.default()[0].type),所以我們可以通過type判斷子組件是不是我們要求的子組件,以Tabs組件為例我們需要用戶使用的時候下面的子組件全部都是我們的Tab組件
- Tabs.vue
import Tab from './Tab.vue'
export default {
setup(props, context: SetupContext) {
const defaults = context.slots.default()
defaults.forEach(tag => {
if (tag.type !== Tab) {
throw new Error('Tabs 子標簽必須是 Tab')
}
})
return {
defaults
}
}
}
- vue3中ref的使用
10.1.單個ref
<script>
import {
onMounted,
ref,
} from 'vue';
export default {
setup() {
const headline = ref(null);
// Before the component is mounted, the value
// of the ref is `null` which is the default
// value we've specified above.
onMounted(() => {
// Logs: `Headline`
console.log(headline.value.textContent);
});
return {
// It is important to return the ref,
// otherwise it won't work.
headline,
};
},
};
</script>
<template>
<div>
<h1 ref="headline">
Headline
</h1>
<p>Lorem ipsum ...</p>
</div>
</template>
10.2. v-for里的ref
<template>
// el當前元素,divs是存儲每個元素的數組
<div v-for="(item, index) in list" :ref="el => { divs[index] = el }">
{{ item }}
</div>
</template>
<script>
import {
onMounted,
ref,
} from 'vue';
export default {
setup() {
const divs = ref([]);
onMounted(() => {
console.log(divs.value)
});
return {
divs
};
},
};
</script>
11.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))
})