教程使用的版本
vue: 3.2.31
typescript: 4.1.6
前言
Options API 约定:
我们需要在 props 里面设置接收参数
我们需要在 data 里面设置变量
我们需要在 computed 里面设置计算属性
我们需要在 watch 里面设置监听属性
我们需要在 methods 里面设置事件方法
你会发现 Options APi 都约定了我们该在哪个位置做什么事,这反倒在一定程度上也强制我们进行了代码分割。现在用 Composition API,不再这么约定了,于是乎,代码组织非常灵活,我们的控制代码写在 setup 里面即可。
setup函数提供了两个参数 props和context,重要的是在setup函数里没有了this,在 vue3.0 中,访问他们变成以下形式: this.xxx=》context.xxx
我们没有了 this 上下文,没有了 Options API 的强制代码分离。Composition API 给了我们更加广阔的天地,那么我们更加需要慎重自约起来。
之前
我们之前的组件可能是这样的:
<template>
<div class="flex items-center justify-center h-screen bg-gray-50">
<Card>{{msg}}</Card>
</div>
</template>
<script lang="ts">
import { ref, defineComponent } from "vue";
import Card from "./components/Card.vue";
export default defineComponent({
components: {
Card,
},
setup() {
const msg = ref("setup script");
return { msg };
}
});
</script>
这里做了三件事:
第一件事lang="ts"
支持typescript
第二个是导入并注册组件
第三个是导出一个字符串给template使用。
现在
启用<script lang="ts" setup>
之后是这样的:
<template>
<div class="flex items-center justify-center h-screen bg-gray-50">
<Card>{{msg}}</Card>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import Card from "./components/Card.vue";
const msg = ref("setup script");
</script>
在<script lang="ts" setup>
里面定义的所有变量都会自动导出非常方便,而且所有的组件导入即可自动注册从而直接就可以使用了
注意事项:所有变量尽量都要转换为对象形式变量,不然无法动态改变
常用的方法
<script lang="ts" setup>
import { ref,reactive,defineProps,defineEmits,defineExpose ,useSlots,useAttrs,getCurrentInstance } from "vue";
</script>
- ref,toRef,toRefs,reactive,将变量转换为引用类型
- defineProps 获取父组件传值
- defineEmits 调用父组件事件
- defineExpose 来暴露组件内部属性给父组件使用
- useSlots 获取插槽数据
- useAttrs用来获取 attrs 数据的(也就是非 props 的属性值)如果当前组件里没有将某个属性指定为 props,那么父组件绑定下来的属性值,都会进入到 attrs 里,通过这个新 API 来拿到。
- getCurrentInstance 类似this,里面有当前组件的全部信息
常用的钩子和事件
除去 beforeCreate 和 created 之外() setup 方法中,有9个旧的生命周期钩子, 而这两个钩子被setup初始化代替了
<script lang="ts" setup>
import {onMounted,onBeforeUpdate,onUpdated,onBeforeMount,onBeforeUnmount,onUnmounted,computed,watch } from "vue";
</script>
- onMounted : 初始化完成事件之后
- onBeforeUpdate : 数据修改前
- onUpdated : 数据修改后
- onBeforeMount: 初始化完成事件之前
- onBeforeUnmount: 组件销毁前
- onUnmounted: 组件销毁后
- computed: 监听数据的计算
- watch : 监听数据的变化
- nextTick : 用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick,则可以在回调中获取更新后的DOM
演示
指令OR组件的导入
指令和组件一样,导入自动注册直接就能使用:
组件
<script lang="ts" setup>
import {helloWorld} from './HelloWorld.vue'
</script>
<template>
<helloWorld></helloWorld>
</template>
指令
<script lang="ts" setup>
import {color} from './v-color'
</script>
<template>
<div v-color />
</template>
使用props
使用props需要用到defineProps来定义,具体用法跟之前的props写法类似:
<script lang="ts" setup>
import { defineProps } from "vue";
//定义父组件传递进来的参数
const props = defineProps(['title', 'content']);
</script>
给props定义类型:
<script lang="ts" setup>
const props = defineProps({
title: String,
content: {
type: Stirng,
required: true
}
});
</script>
使用emits
使用defineEmits对组件里面使用到的事件进行验证和定义:
<script lang="ts" setup>
import { defineEmits} from 'vue'
//声明父组件方法
//const emit = defineEmits(['onHeaderClick'])
// 调用父组件的方法,和给方法传参
//emit('onHeaderClick', 'params')
//绑定多个事件
//const emit = defineEmits(['update', 'delete'])
// 绑定父组件的事件,对事件进行验证
//const emit = defineEmit({
// onHeaderClick: ({title}) => {
// if(!title) {
// console.warn('Invalid title')
// return false
// }
// return true
// }
//})
// 调用父组件的方法,和给方法传参
//emit('onHeaderClick', 'params')
</script>
使用$refs
你应该学过jquery吧,没用vue之前,我前端框架是用JS+Jquery+Bootstrap,因为不是数据驱动,为了获取某些元素的value,我常常会使用Jquery
$("#id").text('xxx') // 使用Jquery
document.getElementById("id") // 使用原生Dom
现在我们牛逼了,我们用vue。那vue中,如果我要获取Dom,该怎么做?
这就进入本文的主题ref, $refs,官网解释:
在setup中是没有this的所有下面的操作会有些变化
ref作用于子组件
获取子组件的方法属性…
<template>
<ScriptSetup ref="scriptSetupRef"></ScriptSetup>
</template>
<script lang="ts" setup>
import ScriptSetup from "./ScriptSetup"
import {getCurrentInstance,onMounted} from "vue";
const {proxy} = getCurrentInstance()
//ref初始化时候是没有的所以必须等到初始化之后才能使用, 两种办法,1.创建函数绑定到指定的事件中,2.等待初始化之后
onMounted(()=>{
//使用方法
proxy.$refs.scriptSetupRef.hello()
//拿属性
console.log(proxy.$refs.scriptSetupRef.options)
//拿其他.只要子组件暴露出来的都能拿到
})
</script>
<style scoped>
</style>
注意: 子组件需要暴露自己的属性和方法
defineExpose({
hello,options
})
ref: 就好比是之前jquery使用的id选择器一样,能拿到元素的dom对象,而在vue中
同步方法和异步组件问题
使用钩子
<script lang="ts" setup>
import {onBeforeMount} from "vue";
onBeforeMount(async () => {
const body = await Get(`/api/test/await`).then((r) => r.data)
console.log(body)
});
</script>
使用函数
<script lang="ts" setup>
import { ref } from "vue";
let testData = ref({});
//声明同步函数 ,不要直接初始化调用,要绑定到指定的事件上比如click
const getClickTest = async () => {
const body = await Get(`/api/test/await`).then((r) => r.data)
testData.value = body ;
}
</script>
使用suspense
在 script-setup 模式下,如果内部初始化时候有使用await 了(没执行的不算,比如被函数包裹住了,而这个函数只是定义了但没有运行),这种情况下,组件的 setup内部解析时候会自动变成 async setup (异步组件)
async setup() {
const post = await withAsyncContext(
fetch(`/api/post/1`).then((r) => r.json())
)
}
然后使用这个组件就会出现问题,页面空白或者部分数据显示不出来,可以使用suspense将异步组件包裹起来,直到默认内容准备就绪。然后,它将自动切换以显示我们的异步组件
//直接包裹ScriptSetup组件
<suspense>
<ScriptSetup></ScriptSetup>
</suspense>
// 包裹router-view视图,只要是这个视图下面的所有异步组件加载都会等待加载完毕后在显示
<router-link to="/scriptSetup">ScriptSetup</router-link>
<suspense>
<router-view></router-view>
</suspense>