📺 开篇词 - ⚡戳我⚡ 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状态。

这个问题在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>
复制代码
近期文章(感谢掘友的鼓励与支持🌹🌹🌹)
欢迎拍砖,一块儿探讨更优雅的实现