Vue3 <script setup>语法糖+ts+Hook实践探索

5,757 阅读5分钟

前言

最近的一个项目从熟悉的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需要使用默认值就得用withDefaultsapi,下面是官方的例子

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>()

还以一点是,官方文档说definePropsdefineEmits这两个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是函数,我们可以清晰的看到变量的来源,编写也很方便。

image.png

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就可以了。

注意点

definePropsdefineEmits这两个api都是只能在<script setup> 中使用的,可千万别在hook中定义了噢(别忘我咋知道的)

总结

使用Vue3 进行开发对于js的要求确实是有所提升,最初我也是因为vue3对ts的支持比较好才去学习,但是在使用后发现确实相比vue2在vue3中可以写出更加清晰直观的代码,这对于团队的协作开发来说是大有好处的!

我是新人oil希望我的文章对你有帮助噢!