Vue3 <script setup>语法糖+ts+Hook实践探索
前言
最近的一个项目从熟悉的vue2.x版本直接转向了vue3.2版本,最初的时候及其不习惯,而且文档比较少,但是在一阵子探索之后,发现用vue3 + script setup语法糖 + hook真的是特别舒适,下面讲一下开发的实践方式和一些注意点,官方文档随便看的东西我就多说啦
Vue script setup官方文档
实践探索
组件的引入
首先是组件的引入无须注册,引入就可以直接使用。
下面的示例代码可以看到我在模板中使用组件时的是大驼峰命名,这是因为在vscode中对一些代码提示工具比较友好,也是vue的风格指南中所倡导的,最初我在开发时写的是横杠命名但是遇到了eslint的报错,提示使用了未引入的组件,应该是新特性代码检查工具还不太兼容吧
<template> <LeftBar></LeftBar> </template> <script setup lang="ts"> import LeftBar from "./components/LeftBar/index.vue" </script> 复制代码
模板中变量引用
在最初vue3版本推出的时候去使用感觉一个一个变量去return简直麻烦到爆炸,但现在 script setup中定义的变量都会自动return出去,这一点对于开发体验来说简直爽到飞起,但是这一点也对开发者的能力提高了要求,不能胡乱定义一些变量了。
<template> <div>{{a}}</div> </template> <script setup lang="ts"> const a:string = "我是a" </script> 复制代码
组件参数、方法的定义
现在的组件定义参数需要使用 defineProps
api,定义方法使用defineEmits
api,使用在defineProps
中定义的参数在模板中使用的时候不需要写props,具体使用方式如下
<template> <div>{{a}}</div> </template> <script setup> import { defineEmits,defineProps,onMounted } from "vue" const props = defineProps({ a:{ type:String, default:'我是a' } }) const emit = defineEmits(['funcA']) onMounted( ()=>console.log(props.a) emit('funcA') ) </script> 复制代码
在ts中我们还可以直接通过类型声明定义props或emits,直接在defineProps
方法泛型传入类型就声明,defineEmits
也是同理,中下面用项目中的一个例子
<script setup> const props = defineProps<{ handle: "add" | "update" parentId: number | null flag: boolean isDir: boolean }>() </script> 复制代码
如果props需要使用默认值就得用withDefaults
api,下面是官方的例子
interface Props { msg?: string labels?: string[] } const props = withDefaults(defineProps<Props>(), { msg: 'hello', labels: () => ['one', 'two'] }) 复制代码
注意点
在上面的代码中我们可以看到,如果使用TS的话可以直接传入类型进行参数声明,我们可能会去将参数的类型给抽离出来以重复利用,但是如果在 defineProps
使用外部引入的interface或者type会报错的,它提醒我们要使用字面量类型
,当前在github上vue的issue已经有这个问题了,目前还没解决,期待早日完善,目前的方法就是类型只能写在当前的vue文件中
//下面的代码会报错,type argument passed to defineProps() must be a literal type import type { PropsType } from "./type" const props = defineProps<PropsType>() 复制代码
还以一点是,官方文档说defineProps
和defineEmits
这两个api现在不需要引入就可以直接在<script setup>
中使用了,但是在实际开发中代码提示还是会报方法未定义的问题,这个也是只能先引入着,等后期兼容好了再修改
使用获取ref元素或子组件注册引用信息
在vue3中,想要实现vue2中$ref获取子组件数据的方式如下代码
<template> <ComponentA ref='aRef'>{{a}}</ComponentA> </template> <script setup> import ComponentA from "./ComponentA/index.vue" import {ref,onMounted} from "vue" const a = ref() onMounted(()=>{ console.log(a) }) </script> 复制代码
在实际使用中你会发现你拿不到组件的数据,因为在子组件中script setup
默认不暴露任何数据,如果需要用到子组件的值你需要使用 defineExpose
api
<script setup lang="ts"> //AComponents子组件 import {defineExpose} from "vue" const formData = {a:1,b:2} defineExpose({ formData, }) </script> 复制代码
在ts中使用的时候,你可能需要获取子组件的类型,你可以使用InstanceType<typeof 组件名称>
来获取组件的类型,以传入ref泛型
<a-component ref="aRef"> </a-component> <script setup lang="ts"> import AComponents from "./xxx" const aRef = ref<InstanceType<typeof AComponents>>() </script> 复制代码
Hook介绍
在最初写vue3.x版本的时候,也许有很多人和我一样在组件中把全部业务代码一把梭,然后写的又臭又乱,还贼多引入,还想着vue2不比这舒服多了。但是在了解了Hook之后才发现,vue3相比vue2最爽的地方就是这了!
前言万语不如放实例,先看一下我在项目中的使用方式,下面是我的一个单页面组件目录和入口文件代码,可以看到业务代码非常的少,主要就是归功于Hook,Hook类似于vue2的mixin,都是用于业务代码的抽离,但是mixin的副作用就是引用的多了变量的来源就不清晰了,而且还会有变量名重复的问题。而hook是函数,我们可以清晰的看到变量的来源,编写也很方便。
Hook就是例如下面的代码,这里是一个叫useState
的hook,用于创建页面所需要的变量,它写在单独的一个文件中,并放在与组件入口文件同级的hook
文件夹里
import { useStore } from "@/store" import { ref, watch, computed } from "vue-demi" function useState() { const store = useStore() const editorRef = ref() const content = computed({ get() { return store.state.markdown.content }, set(val: string) { store.commit("markdown/changeContent", val) }, }) return { editorRef, content } } export default useState 复制代码
入口文件 index.vue
,在入口文件中我引入了很多个hook,它们依赖我使用useState这个hook创建的变量
<!-- index.vue --> <template> <div class="flex w-full h-full"> <v-md-editor v-model="content" :mode="store.state.editor.editMode" :includeLevel="[1, 2, 3, 4]" height="calc(100vh - 60px)" :disabled-menus="[]" @upload-image="uploadImg" ref="editorRef" id="editor" ></v-md-editor> </div> </template> <script setup lang="ts"> import { useStore } from "@/store/index" import useState from "./hooks/useState" import useSaveDoc from "./hooks/useSaveDoc" import useUpload from "./hooks/useUpload" const store = useStore() const { editorRef, content } = useState() useSaveDoc() const { uploadImg } = useUpload(editorRef) </script> 复制代码
那么如果组件中要增加更多的方法要怎么做呢,只需要写更多hook就OK啦,使用hook可以将我们的逻辑上相关联的数据和方法抽离出来单独维护,需要增加功能的时候只需要在hook中修改代码或者添加hook就可以了。
注意点
defineProps
和defineEmits
这两个api都是只能在<script setup>
中使用的,可千万别在hook中定义了噢(别忘我咋知道的)
总结
使用Vue3 进行开发对于js的要求确实是有所提升,最初我也是因为vue3对ts的支持比较好才去学习,但是在使用后发现确实相比vue2在vue3中可以写出更加清晰直观的代码,这对于团队的协作开发来说是大有好处的!
我是新人oil希望我的文章对你有帮助噢!
作者:oil欧哟
链接:https://juejin.cn/post/7015587671019880478