📺 開篇詞 - ⚡戳我⚡ javascript
📺 VueMastery原版html
想象一下若是咱們編寫一個組件包含🔍搜索和排序另兩個功能vue
在傳統的OptionsAPI中咱們須要將邏輯分散到如下六個部分java
OptionsAPIreact
- components
- props
- data
- computed
- methods
- lifecycle methods
這樣會是咱們編輯一個邏輯不得不在代碼中反覆橫跳web
最佳的解決方法是將邏輯聚合就能夠很好的代碼可讀性。編程
這就是咱們的CompositionAPI語法可以實現的功能。CompositionAPI是一個徹底可選的語法與原來的OptionAPI並無衝突之處。他可讓咱們將相同功能的代碼組織在一塊兒,而不須要散落到optionsAPI的各個角落。設計模式
固然可使用符合API並非表明咱們整個頁面只須要使用一個組件徹底用複合API進行組裝。markdown
咱們仍是須要經過組件將頁面進行合理的分拆。
Vue2中的跨組件重用代碼,咱們大概會有四個選擇。
代碼混入其實就是設計模式中的混合模式,缺點也很是明顯。
能夠理解爲多重繼承,簡單的說就是一我的如何有兩個父親
❌沒法避免屬性名衝突 - 長鼻子隨誰
❌繼承關係不清晰
返回一個
✅代碼重用方便
✅繼承關係清洗
import {watch} from "vue"
export defalut {
props: {
name: String
},
setup(props) {
watch(() => {
console.log(props.name)
})
}
}
複製代碼
context 上下文對象 - 用於代替之前的this方法能夠訪問的屬性
setup (props,context) {
const {attrs,slots,parent,root,emit} = context
}
複製代碼
This wraps our primitive in an object allowing up to track。
對基本數據類型數據進行裝箱操做使得成爲一個響應式對象,能夠跟蹤數據變化。
可維護性明顯提升
能夠控制哪些變量暴露
能夠跟中哪些屬性被定義 (屬性繼承與引用透明)
添加方法以下:
這個地方實在沒什麼好講的,和Vue2沒變化
<template>
<div>
<div>Capacity: {{ capacity }}</div>
<p>Spases Left: {{ sapcesLeft }} out of {{ capacity }}</p>
<button @click="increaseCapacity()">Increase Capacity</button>
</div>
</template>
<script> import { ref, computed, watch } from "vue"; export default { setup(props, context) { const capacity = ref(3); const attending = ref(["Tim", "Bob", "Joe"]); function increaseCapacity() { capacity.value++; } const sapcesLeft = computed(() => { return capacity.value - attending.value.length; }); return { capacity, increaseCapacity, attending, sapcesLeft }; }, }; </script>
複製代碼
以前reactive 的 Ref 去聲明全部的響應式屬性
import { ref,computed } from 'vue'
export default {
setup(){
const capacity = ref(4);
const attending = ref(["Tim","Bob","Joe"]);
const spacesLeft = computed(()=>{
return capacity.value - attending.value.length
})
function increaseCapacity(){ capacity.value ++;}
return { capacity,increaseCapacity,attending,spacesLeft}
}
}
複製代碼
可是有另外一個等效的方法用它去代替 reactive 的Ref
import { reactive,computed } from 'vue'
export default {
setup(){
const event = reactive({
capacity:4,
attending:["Tim","Bob","Joe"],
spacesLeft:computed(()=>{
return event.capacity - event.attending.length;
})
})
}
}
複製代碼
過去咱們用vue2.0的data來聲明響應式對象,可是如今在這裏每個屬性都是響應式的包括computed 計算屬性
這2種方式相比於第一種沒有使用.
接下來 咱們再聲明method 這2種語法都ok,取決於你選擇哪種
setup(){
const event = reactive(){
capacity:4,
attending:["Tim","Bob","Joe"],
spacesLeft:computed(()=>{
return event.capacity - event.attending.length;
})
function increaseCapacity(){event.capacity++}
//return整個對象
return {event,increaseCapacity}
}
}
複製代碼
<p>Spaces Left:{{event.spacesLeft}} out of {{event.capacity}}</p>
<h2>Attending</h2>
<ul>>
<li v-for="(name,index) in event.attending" :key="index">
{{name}}
</li>
</ul>
<button @click="increaseCapacity()"> Increase Capacity</button>
複製代碼
在這裏咱們使用對象都是.屬性的方式,可是若是 這個結構變化了,event分開了編程了一個個片斷,這個時候就不能用.屬性的方式了
//在這裏可使用toRefs
import {reactive,computed,toRefs} from 'vue'
export default{
setup(){
const event = reactive({
capacity:4,
attending:["Tim","Bob","Joe"],
spacesLeft:computed(()=>{
return event.capacity -event.attending.length;
})
})
function increaseCapacity(){ event.capacity ++ }
return {...toRefs(event),increaseCapacity}
}
}
複製代碼
若是沒有 increaseCapacity() 這個方法 直接能夠簡化爲
return toRefs(event)
複製代碼
完整代碼
<div>
<p>Space Left : {{event.spacesLeft}} out of {{event.capacity}} </p>
<h2>Attending</h2>
<ul>
<li v-for="(name,index)" in event.attending :key="index">{{name}}
</li>
</ul>
<button @click="increaseCapacity">Increase Capacity</button>
</div>
</template>
<script> //第一種 import {ref,computed } from 'vue' export default { setup(){ const capacity = ref(4) const attending = ref(["Tim","Bob","Joe"]) const spaceLeft = computed(()=>{ return capacity.value - attending.value.length; }); function increaseCapacity(){ capacity.value++; } return {capacity,increaseCapacity,attending,spaceLeft} } } //返回一個響應式函數 第二種 import { reactive,computed } from 'vue' export default { setup(){ const event = reactive({ capacity:4, attending:["Tim","Bob","Joe"], spaceLeft:computed(()=>{ return event.capacity - event.attending.length; }) }) //咱們再也不使用.value function increaseCapacity() { event.capacity++; } //把這個event放入到template中 return { event,increaseCapacity} } } </script>
複製代碼
使用CompositionAPI的兩個理由
能夠按照功能組織代碼
組件間功能代碼複用
Vue2 | Vue3 |
---|---|
beforeCreate | ❌setup(替代) |
created | ❌setup(替代) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
🎉onRenderTracked | |
🎉onRenderTriggered |
setup中調用生命週期鉤子
import { onBeforeMount,onMounted } from "vue";
export default {
setup() {
onBeforeMount(() => {
console.log('Before Mount!')
})
onMounted(() => {
console.log('Before Mount!')
})
},
};
複製代碼
// 全部依賴響應式對象監聽
watchEffect(() => {
results.value = getEventCount(searchInput.value);
});
// 特定響應式對象監聽
watch(
searchInput,
() => {
console.log("watch searchInput:");
}
);
// 特定響應式對象監聽 能夠獲取新舊值
watch(
searchInput,
(newVal, oldVal) => {
console.log("watch searchInput:", newVal, oldVal);
},
);
// 多響應式對象監聽
watch(
[firstName,lastName],
([newFirst,newLast], [oldFirst,oldlast]) => {
// .....
},
);
// 非懶加載方式監聽 能夠設置初始值
watch(
searchInput,
(newVal, oldVal) => {
console.log("watch searchInput:", newVal, oldVal);
},
{
immediate: true,
}
);
複製代碼
編寫一個公共函數usePromise函數需求以下:
results : 返回Promise執行結果
loading: 返回Promise運行狀態
error : 返回執行錯誤
import { ref } from "vue";
export default function usePromise(fn) {
const results = ref(null);
// is PENDING
const loading = ref(false);
const error = ref(null);
const createPromise = async (...args) => {
loading.value = true;
error.value = null;
results.value = null;
try {
results.value = await fn(...args);
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
};
return { results, loading, error, createPromise };
}
複製代碼
應用
import { ref, watch } from "vue";
import usePromise from "./usePromise";
export default {
setup() {
const searchInput = ref("");
function getEventCount() {
return new Promise((resolve) => {
setTimeout(() => resolve(3), 1000);
});
}
const getEvents = usePromise((searchInput) => getEventCount());
watch(searchInput, () => {
if (searchInput.value !== "") {
getEvents.createPromise(searchInput);
} else {
getEvents.results.value = null;
}
});
return { searchInput, ...getEvents };
},
};
複製代碼
咱們考慮一下當你加載一個遠程數據時,如何顯示loading狀態
一般咱們能夠在模板中使用v-if
可是在一個組件樹中,其中幾個子組件須要遠程加載數據,當加載完成前父組件但願處於Loading狀態時咱們就必須藉助全局狀態管理來管理這個Loading狀態。
![image-20201201221336107](/Users/xiaran/Library/Application Support/typora-user-images/image-20201201221336107.png)
這個問題在Vue3中有一個全新的解決方法。
這就是Suspense Component,懸念組件。
<template>
<div>
<div v-if="error">Uh oh .. {{ error }}</div>
<Suspense>
<template #default>
<div>
<Event />
<AsyncEvent />
</div>
</template>
<template #fallback> Loading.... </template>
</Suspense>
</div>
</template>
<script> import { ref, onErrorCaptured, defineAsyncComponent } from "vue"; import Event from "./Event.vue"; const AsyncEvent = defineAsyncComponent(() => import("./Event.vue")); export default { components: { Event, AsyncEvent, }, setup() { const error = ref(null); onErrorCaptured((e) => { error.value = e; // 阻止錯誤繼續冒泡 return true; }); return { error }; }, }; </script>
複製代碼
相似React中的Portal, 能夠將特定的html模板傳送到Dom的任何位置
經過選擇器QuerySelector配置
<template>
<div>
<teleport to="#end-of-body" :disabled="!showText">
<!-- 【Teleport : This should be at the end 】 -->
<div>
<video src="../assets/flower.webm" muted controls="controls" autoplay="autoplay" loop="loop">
</video>
</div>
</teleport>
<div>【Teleport : This should be at the top】</div>
<button @click="showText = !showText">Toggle showText</button>
</div>
</template>
<script> import { ref } from "vue"; export default { setup() { const showText = ref(false); setInterval(() => { showText.value = !showText.value; }, 1000); return { showText }; }, }; </script>
複製代碼
近期文章(感謝掘友的鼓勵與支持🌹🌹🌹)
歡迎拍磚,一塊兒探討更優雅的實現