Vue3 新特性
1、初始化开发环境
代码语言:javascript复制// 安装 / 更新 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 侦听变化
代码语言:javascript复制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
代码语言:javascript复制import { ref, onMounted, onUnmounted } from 'vue'
function useMousePosition() {
const x = ref(0)
const y = ref(0)
const updateMouse = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
document.addEventListener('click', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse)
})
return { x, y }
}
export default useMousePosition;
使用
代码语言:javascript复制import { useMousePosition } from './hooks/useMousePosition.ts'
export default {
setup() {
const { x, y } = useMousePosition()
return {
x,
y
}
}
}
useURLLoader
代码语言:javascript复制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 = false
data.result = res.data.message
}).catch(e => {
data.loadState = false
data.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向父组件触发事件
代码语言:javascript复制// 例子
<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) => { // 进行类型的校验,没有就写 null
return payload.type === "close";
},
},
setup(props, context) {
const buttonClick = () => {
context.emit("close-modal", {
type: "hello",
});
};
return {
buttonClick
}
},
});
</script>
10、 Suspense 组件
- 解决异步请求的困境
- Suspense是Vue3推出的一个内置的特殊组件
- 如果使用Suspense,要返回一个promise
使用Promise:
代码语言:javascript复制<template>
<div>{{result}}</div> <!--返回后的结果可以直接使用 -->
</template>
<script>
import {defineComponent} from 'vue'
export default defineComponent({
setup() {
return new Promise((resolve) => { // 返回的必须是Promise
setTimeout(() => {
return resolve({
result: 10
})
},1000)
})
}
})
</script>
<!--在父组件中使用该组件 -->
<Suspense>
<template #default>
<AsyncTest />
</template>
<!--当还没有请求到结果时显示 -->
<template #fallback>
<h1>loading!!!</h1>
</template>
</Suspense>
使用 async:
代码语言:javascript复制<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()
生命周期函数监听网络请求的错误