在 Vue3.x 版本Composition API 出現之前使用的 Vue2.x 版本的都是一些選項式的API,如有data,methods,computed 和 watch 等這些選項,在data選項中來去定義一個變量為msg,在 methods , computed 等這些選項中來去用一下,或者要來進行一個排查,排查變量msg的問題,可能需要到 這些選項當中去查詢,如果這些選項下面的代碼量一多的話混在一起可能維護起來相對比較困難了,所以在 Vue3.x 當中推出了 Composition API ,它不是選項式而是組合式的API,通過 setup() 來進行處理的。
setup()
如以下選擇通過選項式API來操作,像使用data,methods或watch這些
<div id="app"></div>
<script>
const app = Vue.createApp({
data(){
return {
msg: 'lhxz'
}
},
template:`
<div>
{{msg}}
</div>
`
})
</script>
在 Componsition API 組合式API中就儘可能不要再用像data,methods,watch等這些選項。使用setup()。
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props, context){
return {
msg: 'lhxz', // 屬性
sayHello: ()=>{ // 方法
console.log('Hello');
}
}
},
template:`
<div>
{{msg}}
<button @click="sayHello">打招呼</button>
</div>
`
})
</script>
使用setup() 同樣可以完成,在setup()中可定義屬性和方法返回,選項式API生命週期和組合式API生命週期之間的映射關係是發生了改變,這點在對比講Vue2.x和Vue3.x的時候講過了,如果使用CompositionAPI之後,先前的選項式API中的 beforeCreate 和 created 生命週期就沒有了而是被setup()取代了, 同時在setup裏面能不能拿到這個this,這個在之前的篇目也提及過了,是不能拿到這個this的,因為它沒有被掛載,下面來一個例子:
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(){
this.sayHi();
},
methods:{
sayHi(){
console.log('Hi');
}
},
template:`
<div>
<button @click="sayHi">打招呼</button>
</div>`
})
</script>
可以看到報錯誤了,this.sayHi is not a function,setup()是在 beforeMount 被掛載之前就已經調用了setup() ,[ *提示:Vue的生命週期,beforeCreate,created[setup],beforeMount,.... ] ,此時的methods是還沒有被創建出來,而在setup裏面來去調用是不可能的,所以在setup中是不能夠去調用任何生命週期當中的任何內容,包括了optionAPI選項式API中的一些東西,你會發現setup的執行是非常早的對吧。
那麼反過來我可以在其他生命週期函數或者像optionAPi中的選項來去訪問 setup 裏面的一些東西呢?其實是可以。
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(){
return{
msg: 'lhxz'
}
},
methods:{
looking(){
console.log(this.$options.setup())
}
},
template:`
<div>
{{msg}}
<button @click="looking">打招呼</button>
</div>
`
})
app.mount('#app')
</script>
以上就是Composition API裏面的setup()的內容,那麼學了組合式API之後就儘可能的少用選項式API中的內容,像上面我們在組合式API中來去使用選項式API的內容,雖然兩者可以兼容,但是使用組合式API就不要再用選項式API了,如data和methods這些選項的使用。
ref
CompositionAPI 中 ref : 讓基礎類型的數據具備響應式。
在選項式API中就不需要,因為底層已經完成了具備響應式的特點,而組合式API則需要我們來去給數據包裝成響應式,下面來看一下:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
let msg = 'Hello'
return{
msg
}
},
template:`
<div>
{{msg}}
</div>
`
}).mount('#app')
</script>
</body>
如果是選項式API的方式,那麼可以在控制枱上使用 app.msg 是可以拿到 msg ,以及使用app.msg = "xxxx" 的命令可以來去修改頁面數據的,而現在使用組合式API就不起作用了。
OptionsAPI能夠響應式的處理是因為底層做了處理而CompositionAPI沒有,這個底層原理後續我們再詳講,那麼CompositionAPI如何具備響應式的特點,那麼就可以使用ref,ref可以讓基礎數據類型具備響應式的特點,下面我們來編寫一下:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 1.引入ref
const {ref} = Vue
// let msg = "Hello"
let msg = ref('Hello');
return{
msg
}
},
template:`
<div>
{{msg}}
</div>
`
}).mount('#app')
</script>
</body>
在講Vue2到Vue3的過度的時候,就有提及到這個Vue3.x將這個ProxyAPI替代definedProretyAPI了,大家可以回顧一下。在optionsAPI當中我們知道具備這種響應式式通過代理的方式,它是通過代理的方式,definedProperty,那麼CompositionAPI也是通過底層代理Proxy,它內部包裝成一個對象 proxy({value: 'val'}) ,那麼在 setup() 需要注意的是不能直接為msg賦值,而是msg.value,下面我們來演示一下:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 1.引入ref
const {ref} = Vue
// let msg = "Hello"
let msg = ref('Hello');
// 兩秒後來修改
setTimeout(()=>{
msg = 'HI'
},2000)
return{
msg
}
},
template:`
<div>
{{msg}}
</div>
`
}).mount('#app')
</script>
</body>
那麼如果我們將setTimeout中的msg改為msg.value再來測試一下:
...
setTimeout(()=>{ msg.value = "HI" },2000)
...
這個是需要注意的內容,當你使用CompositionAPI的時候需要用到這個響應式數據的時候可以使用這個ref,ref可以使得基礎數據類型具備響應式的特點。當然了有基礎數據類型,當然了還有引用數據類型,像數組對象這些,那麼可以使用下面我們要講的 reactive。
reactive
CompositionAPI 中 reactive : 讓引用類型的數據具備響應式。
下面我們直接來一個例子就可以知道 reactive 的使用:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 1.引入reactive
const {reactive} = Vue
// 對象
let obj = reactive({name: 'syan', sex: '女'});
// 數組
let arr = reactive(['1','2','3','4'])
return{
obj,
arr
}
},
template:`
<div>
<p>對象</p>
<span>{{obj}}</span><br/>
<span>名稱:{{obj.name}}</span><br/>
<span>性別:{{obj.sex}}</span><br/>
<p>數組</p>
<span>{{arr}}</span><br/>
<span>arr[0] - {{arr[0]}}</span><br/>
<span>arr[1] - {{arr[1]}}</span><br/>
</div>
`
}).mount('#app')
</script>
</body>
下面來通過控制枱進行修改以上的這些數據信息:
reactive的底層也是通過proxy,那麼ref的轉化是
proxy:("xxxx") —— 》 proxy(value:"xxxx") 通過 ref.value
而reactive的轉化是什麼呢?
proxy:({xxx: "yyyy", xxxx: "yyyy"}) —— 》 proxy({xxx: "yyyy", xxxx: "yyyy"})
數組同理;
那麼下面我們來通過setTimeout來測試一下:這時我們就不需要和ref那樣需要進行一個".value"的方式來去響應式的修改。
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 1.引入reactive
const {reactive} = Vue
// 對象
let obj = reactive({name: 'syan', sex: '女'});
// 數組
let arr = reactive(['1','2','3','4'])
setTimeout(()=>{
obj.name = "zsen"
arr[1] = '888'
},2000)
return{
obj,
arr
}
},
template:`
<div>
<p>對象</p>
<span>{{obj}}</span><br/>
<span>名稱:{{obj.name}}</span><br/>
<span>性別:{{obj.sex}}</span><br/>
<p>數組</p>
<span>{{arr}}</span><br/>
<span>arr[0] - {{arr[0]}}</span><br/>
<span>arr[1] - {{arr[1]}}</span><br/>
</div>
`
}).mount('#app')
</script>
</body>
以上就是reactive的使用了!
readonly
readonly,字面意思 "只讀" 的,在Vue2.x的時候我們就已經講過了Vue的一個數據流它是一個單向數據流,從父組件到子組件傳遞信息,父組件可以更改子組件的數據,而子組件不能直接修改父組件的數據,而是通過請求父組件對自己數據的更改,那麼在CompositionAPI當中數據具備響應式能夠被修改,那麼如何來去規避數據被修改呢?即父組件向子組件傳遞數據,子組件可以將數據拿來用但是不能對父組件的數據進行修改。
如果想讓數據不被修改可以使用readonly規避數據修改。
toRefs
toRefs是針對reactive包裝起來的引用數據類型,可以通過toRefs來將其結構出來。
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 引入toRefs
const {reactive, toRefs} = Vue
let obj = reactive({name: 'syan', sex: '女'});
// 解構obj
let { name , sex } = toRefs(obj);
setTimeout(()=>{
// 結構出來的屬性也具有響應式的特點
name.value = "lhxz",
sex.value = '男'
},2000)
return{
obj,
name,
sex
}
},
template:`
<div>
<p>對象</p>
<span>{{obj}}</span><br/>
<span>名稱:{{name}}</span><br/>
<span>性別:{{sex}}</span><br/>
</div>
`
}).mount('#app')
</script>
</body>
父組件傳遞這樣一個reactive的obj給到子組件,那麼子組件可以通過這個toRefs來進行解構使用,同時toRefs解構的屬性像案例中的name和sex屬性都是具備響應式的特點的,需要注意的是解構之後進行賦值修改的時候需要 ".value" 的方式,來看一下toRefs()它到底做了什麼工作,同上面的 ref,reactive 一樣都是通過這個proxy,toRefs的轉化是:
proxy({ name:"syan" ,sex: "女" })
-》 name: proxy({ value: "syan" })
-》 sex: proxy({ value: "女" })
以上就是toRefs()的一個使用,下面來看另外一個toRef()。
toRef
在上面我們看到了toRefs對reactive引用數據的解構,那麼下面我們介紹另外一種情況:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
setup(props,context){
// 引入toRefs
const {reactive, toRefs} = Vue
let obj = reactive({name: 'syan', sex: '女'});
// 解構obj
let { name , sex ,age } = toRefs(obj);
setTimeout(()=>{
// 結構出來的屬性也具有響應式的特點
name.value = "lhxz",
sex.value = '男',
age.value = 18
},2000)
return{
name,
sex
}
},
template:`
<div>
<span>名稱:{{name}}</span><br/>
<span>性別:{{sex}}</span><br/>
<span>年齡:{{age}}</span><br/>
</div>
`
}).mount('#app')
</script>
</body>
父組件像子組件傳遞這樣的一個obj,然後在子組件當中被toRefs所解構,那麼這個age在obj當中是沒有的,但是我可能在組件開發的過程中以為這個age是有的,那麼name和sex屬性是必要的,而age屬性則可選的,那麼我們可以通過toRef來處理。
以上的這塊就toRef的一個使用了。
context
attrs
context 可以拿到 attrs 、solts、emit,attrs拿到的就是這個no-props,父傳子可以在props中拿到,下面來簡單看一下:
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
template:`
<div>
<zj-compt></zj-compt>
</div>
`
})
// 子組件
app.component('zj-compt',{
setup(props,context){
console.log(context)
const { attrs, solts, emit } = context
console.log(attrs)
},
template:`
<div style="border:1px solid red"> 我是子組件 </div>
})
app.mount('#app')
</script>
</body> `
在子組件上設置 sex="女" ,通過context中的attrs來拿到:
<script>
const app = Vue.createApp({
template:`
<div>
<zj-compt sex="女"></zj-compt>
</div>
`
})
// 子組件
app.component('zj-compt',{
setup(props,context){
console.log(context)
const { attrs, solts, emit } = context
console.log(attrs)
console.log(attrs.sex)
},
...
可以看到效果拿到了,在子組件上的 template 來使用原先自己的 style,當然這裏我們在 <zj-compt> 上添加一個style屬性,然後通過attrs來被子組件獲取,接着通過$attrs.屬性即可使用。
<body>
<div id="app"></div>
<script>
const app = Vue.createApp({
template:`
<div>
<zj-compt sex="女" style="border: 2px solid blue"></zj-compt>
</div>
`
})
// 子組件
app.component('zj-compt',{
setup(props,context){
console.log(context)
const { attrs, solts, emit } = context
console.log(attrs)
console.log(attrs.sex)
console.log(attrs.style)
},
template:`
<div :style="$attrs.style"> 我是子組件 </div>
`
})
app.mount('#app')
</script>
</body>
父組件向子組件傳遞一些no-props的東西如 sex=val 這些通過attrs來去獲取,attrs.屬性可以打印出對應的值,在使用的使用需要使用 $attrs.屬性。
slots
context中的第二個屬性slots,應該不陌生,源於插槽,通過它來拿到插槽內中的一些元素來讓我們進行操作,如先來看一下:
<body>
<div id="app">
<l-h>
<button type="button">按鈕</button>
</l-h>
<l-h>
<input type="text" placeholder="輸入....">
<button type="button">提交</button>
</l-h>
<l-h>
<img src="https://www.baidu.com/img/flexible/logo/pc/result.png" alt="圖片">
</l-h>
</div>
<!-- 模板 -->
<template id="box">
<div style="border: 5px solid green;">
<slot>默認內容</slot>
</div>
</template>
<script>
const LHXZ = {
template: '#box'
}
const app = Vue.createApp({
components: {
'l-h': LHXZ
}
})
app.mount('#app')
</script>
</body>
下面來看看如何在setup()函數裏面來去使用:
<script>
const LHXZ = {
setup(props, context){
// const {attrs, solts, emit} = context
const {slots} = context
console.log(slots) // proxy
console.log(slots.default()) // 具體取出
console.log(slots.default()[0]) // 具體取出下標為[1]
},
template: '#box'
}
....
同時還可以拿到它的Props屬性對象內容:
console.log(slots.default()[0].props) // 具體取出下標為[1]的props
通過slots可以拿到插槽內的一些元素,拿到這些元素可供我們實現頁面一些數據可用,下面來來看emit,主要是自定義事件。
emit
下面先來一個自定義事件,從父組件到子組件,Vue是單項數據流,但可以通過請求父組件對自身進行修改。
<body>
<!-- 父組件 -->
<div id="app" style="width: 200px; height: 200px; background-color: red;">
<l-h @out-click="handleFunc"></l-h>
</div>
<!-- #small -->
<template id="small">
<div style="width:100px; height: 100px; background-color: rebeccapurple">
<button @click="btnClick">按鈕</button>
</div>
</template>
<script>
// 子組件
const LHXZ = {
// 待寫
methods:{
btnClick(){
alert('點擊');
const dataObj = {
name: 'Gridfriend',
info: 'Good Night!'
};
this.$emit('outClick', dataObj)
}
},
template: '#small'
}
// 父組件
const app = Vue.createApp({
data() {
return {
msg: "INFO-信息"
}
},
components: {
'l-h': LHXZ
},
methods:{
handleFunc(item){
console.log('子組件中按鈕發生點擊,觸發父組件');
console.log(item)
}
}
})
app.mount('#app')
</script>
</body>
這種方式也是我們之前使用 optionsAPI 選項式API採用的方式,現在使用CompositionAPI呢也就沒有了這些methods選項了,而是就一個setup()函數來進行操作了。
下面我們來就子組件中的內容來進行修改,如下(可以和註釋的地方進行一個對比):
<script>
// 子組件
const LHXZ = {
setup(props,context) {
const { emit } = context
// 處理業務邏輯
function btnClick() {
alert('點擊');
const dataObj = {
name: 'Gridfriend',
info: 'Good Night!'
};
// this.$emit('outClick', dataObj)
emit('outClick', dataObj)
}
return {
btnClick
}
},
// methods:{
// btnClick(){
// alert('點擊');
// const dataObj = {
// name: 'Gridfriend',
// info: 'Good Night!'
// };
// this.$emit('outClick', dataObj)
// }
// },
template: '#small'
}
以上就是關於context中的 attrs / slots / emit 的用法了。