新增
- setup:setup是新增的用于封装抽离代码的一个函数,他在实例组件渲染前执行,这个时候不能使用实例上的属性和方法
- setup生命周期:生命周期函数以on开头,不包含created之前的钩子,distroyed也变成了onUnmounted,新增调试钩子onRenderTracked, onRenderTriggered
- ref用于生成响应式数据,然后使用 ref返回值.value在setup中获取响应式数据
- 喜欢用单例模式开发项目同样也可以使用reactive生成响应式数据,如果需要重新解构,或者重新赋值的话,可以使用toRefs方法将数据响应化
- computed与watch无太大变化,知识形式长需要使用函数初始化,在回调用进行功能构建
<template>
<h1>{{ num }} | {{ doubleNum }}</h1>
<button @click="add">自增</button>
</template>
<script lang="ts">
<template>
<h1>{{ num }} | {{ doubleNum }}</h1>
<button @click="add">自增</button>
</template>
<script lang="ts">
import { ref, computed, reactive, toRefs, onMounted, onUpdated, onUnmounted, onRenderTracked, onRenderTriggered, watch } from 'vue'
interface DataProps {
num: number
add: () => void
doubleNum: number
}
export default {
setup() {
onMounted(() => {
console.log('mounted')
})
onUpdated(() => {
console.log('update')
})
onUnmounted(() => {
console.log('destroyed')
})
onRenderTracked((e) => {
console.log('onRenderTracked', e)
})
onRenderTriggered((e) => {
console.log('onRenderTriggered', e)
})
const data: DataProps = reactive({
num: 1,
add: () => {
data.num++
},
doubleNum: computed(() => data.num * 2),
})
let refData = toRefs(data)
watch([() => data.num], (n, o) => {
console.log(n, o)
})
return {
...refData,
}
},
}
</script>
使用
- 下例是vue3集合typescript实际中的使用,defineComponent函数的作用在于生成自定义组件时能够获得良好的代码提示
- /hooks/useLoading 文件是一个简单的获取点击事件的位置的抽离
- /hooks/useLoading文件是vue结合typescript的一个小案例,由于ts预先声明的null没有任何属性,同时我们也无法获取到接口的返回值信息,所以需要手动的设置泛型来约束result的返回值
-->test.vue
<template>
{{ x }}:{{ y }}
<div v-if="loading">loading....</div>
<div v-if="loaded">
<img :src="result[0].url" alt="" />
</div>
</template>
<script lang="ts">
import { watch, defineComponent } from 'vue'
import useMouseUpdate from '../hooks/useMouseUpdate'
import useLoading from '../hooks/useLoading'
interface DogResult {
message: string
status: string
}
interface CatResult {
id: string
url: string
width: number
height: number
}
export default {
setup() {
let { result, loading, loaded } = useLoading<CatResult[]>('http://api.thecatapi.com/v1/images/search?limit=1')
let { x, y } = useMouseUpdate()
watch(result, (n) => {
if (result.value) {
console.log(result.value[0].url)
}
})
return {
x,
y,
result,
loading,
loaded,
}
},
}
</script>
-->/hooks/useMouseUpdate
import { onMounted, onUnmounted, reactive, toRefs } from "vue";
function useMouseUpdate() {
const data = reactive({
x: 0,
y: 0
});
function updateMouseEvent(e: MouseEvent) {
data.x = e.pageX;
data.y = e.pageY;
}
onMounted(() => {
document.addEventListener("click", updateMouseEvent);
});
onUnmounted(() => {
document.removeEventListener("click", updateMouseEvent);
});
const refData = toRefs(data);
return {
...refData
};
}
export default useMouseUpdate;
-->/hooks/useLoading
import axios from "axios";
import { ref } from "vue";
function useLoading<T>(url: string) {
let result = ref<T | null>(null);
let loading = ref(true);
let loaded = ref(false);
let error = ref(null);
axios
.get(url)
.then(res => {
loading.value = false;
loaded.value = true;
result.value = res.data;
})
.catch(e => {
loading.value = false;
error.value = e;
});
return {
result,
loading,
loaded,
error
};
}
export default useLoading;
新增2
- teleport传送门,如下列所示如果在index.html中body元素下添加一个id为app的元素,那么下面这个组件就会被渲染到#modal这个元素下方,所以适合用来做模态框
- Suspense是一个用来做异步等待的内置组件,可以用于做调接口时的异步动画
- onErrorCaptured可以用来处理异常
-->test.vue
<template>
{{ error }}
<Suspense>
<template #default> <async-temp></async-temp></template>
<template #fallback>
<div>loading....</div>
</template>
</Suspense>
<button @click="openModal">打开按钮</button>
<Modal @handle-close="closeModal" :isOpen="isModalOpen">我是一个快乐的小猫咪</Modal>
</template>
<script >
import { ref, watch, defineComponent, onErrorCaptured } from 'vue'
import Modal from './Modal.vue';
import AsyncTemp from './AsyncTemp.vue';
export default {
components: { Modal, AsyncTemp },
setup (props, ctx) {
let error = ref(null)
let isModalOpen = ref(false)
onErrorCaptured(e => {
error.value = e
return true
})
const closeModal = () => {
isModalOpen.value = false
}
const openModal = () => {
console.log('open');
isModalOpen.value = true
}
return {
error,
isModalOpen,
closeModal,
openModal
}
},
}
</script>
-->/AsyncTemp.vue
<template>
<img :src="result && result.message" alt="" />
</template>
<script type="ts">
import { defineComponent } from 'vue'
import axios from 'axios'
export default defineComponent({
async setup (props, context) {
let rowData = await axios.get('https://dog.ceo/api/breeds/image/random')
return { result: rowData.data }
}
})
</script>
-->/Modal.vue
<template>
<teleport to="#modal">
<div class="modal" v-if="isOpen">
<div class="content">
<slot> 内容区域 </slot>
</div>
<button class="btn" @click="close">关闭</button>
</div>
</teleport>
</template>
<script>
import { defineComponent, watch } from 'vue'
export default defineComponent({
props: {
isOpen: {
type: Boolean,
default: true
}
},
emits: {
'handle-close': (payload) => {
return payload.type === 'close'
}
},
setup (props, context) {
function close () {
context.emit('handle-close', { type: 'close' })
}
return {
close
}
}
})
</script>
<style>
#modal .modal {
width: 300px;
height: 300px;
top: calc(50% - 150px);
left: calc(50% - 150px);
border: 1px solid #f40;
text-align: center;
position: absolute;
z-index: 999;
background-color: #fff;
}
#modal .modal .btn {
position: absolute;
bottom: 10px;
}
</style>