vue3 新特性
1、初始化开发环境
// 安装 / 更新 vue-cli (保证版本在4.5.0以上)npm install -g @vue/cli// 创建
vue create vue3-basic// 然后选择 Manually select features (因为我们需要使用TS),进入界面后选择TS(按空格选择)// 然后选择Vue3, 后面都保持默认和选择 No
2、ref 的使用
在 vue3 的 script
中不再使用 data
和 methods
,而是使用 setup()
方法
ref
, computed
等都是属于 composition API
<template><img alt="Vue logo" src="./assets/logo.png"><div>{
{count}}</div> <!-- 此处直接写 count 就能获取到值 --><div>{
{double}}</div><button @click="increase">点击</button>
</template><script lang="ts">
import { ref, computed } from "vue";export default ({name: 'App',setup() {const count = ref(0) // ref 接受一个参数,返回的是一个响应式对象const double = computed(() => {return count.value * 2})const increase = () => {count.value ++ // count是对象,这样才能获取到值}return {count,double,increase}}});
</script>
3、reactive 的使用
reactive
可以将需要导出的数据都包裹在一个对象中,而不是单独存在
<template><img alt="Vue logo" src="./assets/logo.png"><div>{
{count}}</div> <!-- 此处直接写 count 就能获取到值 --><div>{
{double}}</div><button @click="increase">点击</button>
</template><script lang="ts">
import { computed, reactive, toRefs } from "vue";interface DataProps { // 此处是为了解决 data 对象显示为 any 类型报错count: number;increase: () => void;double: number;
}export default ({name: 'App',setup() {const data: DataProps = reactive({ // reactive 是一个方法,接受一个对象count: 0,increase: () => {data.count ++},double: computed(() => {return data.count * 2})})// 如果仅仅将 data 进行展开会丧失响应式,所以用 toRefs把每一项都转化为响应式对象const refData = toRefs(data) return {...refData // 展开之后再模板中就不用写 data. 的前缀了}}});
</script>
同时, vue3 还支持直接新增 data 中的数据以及修改 data 中数组和对象的每一项,这样的修改也是响应式的。
4、生命周期钩子
在旧的 beforeCreate
钩子函数之后和 created
的钩子函数之前立即调用 setup
方法。因此,我们不再需要这两个钩子,我们可以简单地将本应有的代码放在 setup()
方法中。
此外,还有 9 个旧的生命周期函数可以在 setup()
中使用,使用前必须先导入
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
- onActivated
- onDeactivated
- onErrorCaptured
// 例子import { onMounted, onUpdated } from 'vue'export default {setup() {onMounted(() => {// ... })onUpdated(() => {// ... })}
}
5、 Watch 侦听变化
import { watch } from 'vue'export default {setup() {// 1. 当监听的是 ref 返回的响应式对象watch(data, (newValue, oldValue) => {// ...})// 2. 当监听的是多个数据watch([data1, data2], (newValue, oldValue) => {// ...})// 3. 当监听的是多个对象,且包含 reactive 对象内的数据时watch([data1, () => data.data2], (newValue, oldValue) => {// ...})}
}
6、模块化
以下是两个例子:
鼠标追踪器
由于将代码都写入 setup()
会使代码过于冗余和复杂,故对多次需要使用的代码实现模块化
src / hooks / useMousePosition.ts
import { ref, onMounted, onUnmounted } from 'vue'function useMousePosition() {const x = ref(0)const y = ref(0)const updateMouse = (e: MouseEvent) => {x.value = e.pageXy.value = e.pageY}onMounted(() => {document.addEventListener('click', updateMouse)})onUnmounted(() => {document.removeEventListener('click', updateMouse)})return { x, y }
}export default useMousePosition;
使用
import { useMousePosition } from './hooks/useMousePosition.ts'export default {setup() {const { x, y } = useMousePosition()return {x,y}}
}
useURLLoader
import { reactive, toRefs } from 'vue'
import axios from 'axios'function useURLLoader(url: string) {const data = reactive({result: null,loadState: true,errInfo: null})axios.get(url).then((res) => {data.loadState = falsedata.result = res.data.message}).catch(e => {data.loadState = falsedata.errInfo = e})const refData = toRefs(data)return {...refData}
}export default useURLLoader
7、defineComponent 的使用
使用 defineComponent
能够让传入的对象获得类型以及能够获得自动提示
<script lang="ts">
import { defineComponent } from 'vue';export default defineComponent({name: 'HelloWorld',props: {msg: String,},setup(props, context) {...}
});
</script>
8、Teleport (瞬间移动) 组件的使用
有时在组件中需要用到弹窗时,将弹窗的代码全部写到层层嵌套的组件中,不太符合逻辑也不太方便查看。使用Teleport
能将组件的内容挂在到其他组件上
// 首先需要在父组件中引入该组件// 使用
<template><teleport to="#model"> <!-- 挂载到id为 model 的节点上 --><div>this is a model</div></teleport>
</template>// 挂载
// 在index.html中
<div id="model"></div>
9、 emits向父组件触发事件
// 例子
<template><teleport to="#model"><div v-if="isOpen"><div>this is a model</div><button @click="buttonClick">关闭</button></div></teleport>
</template><script>
import { defineComponent } from "vue";export default defineComponent({name: "Model",props: {isOpen: Boolean,},emits: {"close-modal": (payload: any) => { // 进行类型的校验,没有就写 nullreturn payload.type === "close";},},setup(props, context) {const buttonClick = () => {context.emit("close-modal", {type: "hello",});};return {buttonClick}},
});
</script>
10、 Suspense 组件
- 解决异步请求的困境
- Suspense是Vue3推出的一个内置的特殊组件
- 如果使用Suspense,要返回一个promise
使用Promise:
<template><div>{
{result}}</div> <!--返回后的结果可以直接使用 -->
</template><script>
import {defineComponent} from 'vue'export default defineComponent({setup() {return new Promise((resolve) => { // 返回的必须是PromisesetTimeout(() => {return resolve({result: 10})},1000)})}
})
</script><!--在父组件中使用该组件 -->
<Suspense><template #default><AsyncTest /></template><!--当还没有请求到结果时显示 --><template #fallback><h1>loading!!!</h1></template>
</Suspense>
使用 async:
<template><div><img :src="result && result.message" alt="123" /></div>
</template><script>
import { defineComponent } from "vue";
import axios from "axios";export default defineComponent({async setup() {const rawData = await axios.get("https://www.example.com/"); // 测试使用return {result: rawData.data,};},
});
</script><!--在父组件中使用该组件同上面的 Promise -->
可以使用 onErrorCaptured()
生命周期函数监听网络请求的错误