如何在开发中更加优雅的使用Vue3.0

一. 前言

在日常开发中,Vue3.0已经用的很常见了。但是在最近接手一个多人迭代的“code hell”项目中,我发现一些非常匪夷所思的写法。在这里总结一下,我个人感觉比较好用的Vue3.0的特性,以及一些在ts方面更加合理的规范吧。仅作为个人日常总结。

注意: 以下默认setup的语法糖写法

二. defineProps

1. 定义

定义组件的属性。通过调用该函数,可以声明组件的属性,并且将属性的类型、默认值等信息进行声明。 在子组件内部通过props属性来接收父组件传递的属性值。

这里注意是defineProps是个函数

可以看一下在Vue源码中的定义:

    export declare function defineProps<TypeProps>(): Readonly<TypeProps>;

这里面牵涉到runtime以及一些宏函数的问题(之后有空再聊一下吧)

2. 写法

通过使用范型参数定义

    const props = defineProps<{T}>()

如下例子:

    const props = defineProps<{
        setUpPropsConfig: {
            title: string,
            value: number,
            isOK: boolean,
            list: string | number [],
            myObject: { name: string, age: number},
            func?: () => void
        }
    }>()

还可以把当前的范型参数抽出去编写成一个接口

    interface ISetUpProps{
        ····
    }
    const props = defineProps<ISetUpProps>()

这里要注意一个问题,这个接口文件不能通过import从外部文件引入,不然会报错

[@vue/compiler-sfc] type argument passed to defineProps() must be a literal type,
or a reference to an interface or literal type.

加入默认值:

withDefaults(defineProps, {})

    const props = withDefaults(defineProps<{
        title: string,
        value: number
    }>(), {
        title: 'name',
        value: 1
    })

三. defineEmits

用于定义组件的事件。通过调用该函数,可以声明组件所触发的事件,并且将事件的名称、参数等信息进行声明。

    export declare function defineEmits<TypeEmit>(): TypeEmit;

类似于defineProps也是一个函数,可以通过范型参数定义

    const emit = defineEmits<{ 
        (e: 'change', id: number): void 
        (e: 'update', array: number[]): void }
    >()

其实这两个方法本质上来说就是函数function() , 跟范型函数的写法是一致的:

    // 范型函数
    const emitFunc = <T>(args: T): void => {
    }
    
    function emitFunc2<T>(args: T): void {
    }
    
    // defineProps
    function defineProps<TypeProps>(): Readonly<TypeProps>
    
    // defineEmits
    function defineEmits<TypeEmit>(): TypeEmit

四. defineExpose

在setup语法糖下,显式暴露组件的公共属性和方法。简单来说,就是允许父组件访问子组件的特定属性或方法

这个方法之前我用的不多,最近在项目中发现还真的挺好用的,挺香 !!!

举个例子: 项目中有很多表单弹窗的封装。怎么封装呢?

4.1 update:modelValue

这个方法就是通过传入一个 modelValue 的prop,在需要更新值的时候,通过触发 update:modelValue, 完成组件值的更新。

简单封装一个弹窗:

代码如下:

 // myDialog.vue
<template>
  <div class="mask" v-if="visiable">
    <div class="modal-content">
      <div class="dialog-header">{{ title }}</div>
      <div class="dialog-body">{{ content }}</div>
      <div class="dialog-footer">
        <el-button @click="onConfirm" type="primary">确定</el-button>
        <el-button @click="onCancel">取消</el-button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const props = withDefaults(
    defineProps<{
        visiable: boolean,
        title: string,
        content: string
    }>(), {
        visiable: false,
        title: '我的标题',
        content: 'xxxxxxx'
})

const emit = defineEmits<{
    (e: 'update:visiable', val: boolean): void
}>()

const onConfirm = () => {
  emit('update:visiable', false)
}

const onCancel = () => {
  emit('update:visiable', false)
}
</script>

<style scoped lang="scss">
.mask {
      width: 100%;
      height: 100%;
      position: fixed;
      top: 0;
      left: 0;
      .modal-content {
          width: 400px;
          padding: 16px;
          margin: 200px auto 0;
          position: relative;
          background: #fff;
      }
      .dialog-header {
          padding-bottom: 16px;
          color: #303133;
          line-height: 24px;
      }
      .dialog-body {
          color: 606266;
          font-size: 14px;
      }
      .dialog-footer {
          margin-top: 18px;
          text-align: right;
      }
}
</style>

在父组件里面引入一下,然后调用一下:

<template>
    <myDialog v-model:visiable="visiable" />
    <button @click="clickMe">点我</button>
</template>

<script lang="ts" setup>
    import myDialog from '../src/myDialog/index.vue'
    import { ref } from 'vue'

    const visiable = ref(false)

    const clickMe = () => {
        visiable.value = true
    }
</script>

4.2 defineExpose

简单对上的组件进行修改一下:

    <template>
      <div class="mask" v-if="visiable">
        <div class="modal-content">
          <div class="dialog-header">{{ modalContent.title }}</div>
          <div class="dialog-body">{{ modalContent.content }}</div>
          <div class="dialog-footer">
            <el-button @click="onConfirm" type="primary">确定</el-button>
            <el-button @click="onCancel">取消</el-button>
          </div>
        </div>
      </div>
    </template>

<script setup lang="ts">
    import { ref } from 'vue'

    const visiable = ref(false)
    const modalContent = ref<{ title: string, content: string }>({title: '', content: ''})
    const init = (item: { title: string, content: string }) => {
      visiable.value = true
      modalContent.value = item ?? {}
    }

    const closeModal = () => visiable.value = false

    const onConfirm = () => {
      closeModal()
    }

    const onCancel = () => {
      closeModal()
    }

    defineExpose(
      {
        init
      }
    )
</script>

也比较容易理解,不再依靠外部传入的prop,通过内部定义的ref来控制显示,然后把控制显示的方法在这里是 init,通过defineExpose暴露出去,在父组件中调用。

在父组件中:

<template>
    <!-- <myDialog v-model:visiable="visiable" /> -->
    <defineDialog ref="defineDialogRef" />
    <button @click="clickMe">点我</button>
</template>

<script lang="ts" setup>
    import defineDialog from '../src/myDialog/defineDialog.vue'
    import { ref } from 'vue'

    const defineDialogRef = ref()

    const clickMe = () => {
        defineDialogRef.value.init({title: '新的标题', content: '这是个内容'})
    }
</script>

通过ref拿到子组件对象,然后调用init方法,同时可以传值。

可以试一下,没有什么问题。

两种方法没有什么好坏之分,就是一种写法上的不同。个人觉得第二种更好用一点,特别是只需要处理子组件上的值的时候。

五. 获取 DOM 节点

有个疑问,这么多年了,vue3.0项目中还会有人用getElementById等方法获取节点么?这不是兜兜转转还是拉了一裤子翔么?

除了一些特殊的情况外,都可以使用内置的templatereference(简称 ref)获取、操作dom元素。

如下的例子:

<template>
    <div ref="divRef" style="width: 200px; height: 400px;"></div>
    <input ref="inputRef" value="value的内容" />
</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue'

// 可以设置不同的元素类型
const divRef = ref<HTMLDivElement>()
const inputRef = ref<HTMLInputElement>()

onMounted(() => {
    console.log(divRef.value)
    // 为了严格的类型安全,这里最好通过可选链来限制。
    console.log(inputRef.value?.value)
})
</script>

ref如果用于普通 DOM 元素,引用将是元素本身;如果用于子组件,引用得到的值将是子组件的实例对象

六. pinia

合理的使用一些pinia的api可以很好的简化代码、优化代码。

6.1 基本使用

新建一个store.ts

import { defineStore } from "pinia"

type TStore = {
    counter: number
    name: string
  }
export const useStore = defineStore('myStore', {
    state: (): TStore => {
        return {
            counter: 1,
            name: '小美老师'
        }
    },
    getters: {
        addCount: (state) => {
            return state.counter++
        },
        addCount2(): number {
            return this.counter * 3;
        }
    },

    actions: {
        induce() {
            this.counter--
        }
    }
})

要注意两点:

  1. state是一个函数,所以类型推断的时候是需要按照函数的类型推断方式,推荐使用箭头函数更方便。
  2. 有两种getters的写法,但是个人更推荐通过常规函数用this访问整个store的实例。

6.2 state的获取

  1. 通过store实例访问 state,可以直接读取和写入,store.counter++。
  2. 通过 store.$reset() 方法可以将 state 重置为初始值。
  3. 通过 store.$patch() 方法可以完成state值的批量修改。
  4. 通过 store.$subscribe() 订阅 state 的变化,在 patches 修改之后订阅只会触发一次。

这个2和3其实真的很好用,但是我在项目中看到用的不多。

如上代码,我们在项目中引入一下:

   <template>
    <div>{{ name }}</div>
    <div> {{ counter }}</div>

    <button @click="patch">patch</button>

    <button @click="reset">reset</button>

</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../src/store'

const store = useStore()
const { name, counter } = storeToRefs(store)
const reset = () => {
  store.$reset(); // 重置成功
};

const patch = () => {
   // 直接修改state里面的值,不用在批次修改。
  store.$patch({
    counter: store.counter + 1,
    name: "啊,小美老师",
  });
}

store.$subscribe(
    () => {
        console.log("啊,小美老师发生了变化");
    },
    { detached: true }
);

</script>

这里的detached属性可能是用来指示这个监听器是否应该与组件的生命周期解绑。如果detachedtrue,那么即使组件被销毁,这个监听器也会继续监听状态变化。

这里面尤其是store.$subscribe我在项目中很少看到,都是在通过watch监听store里面值的改变。这个是可以推广使用的。

六. 总结

简单介绍了一下项目中个人比较常用的几个特性,需要的同学配合项目食用。多谢大家了!

偏爱前端的晓羽:称作2024很强的前端面试场景题,成功帮助532人拿到offer

给你们推荐一篇我帮助500+名同学完成改造的文章,希望大家看完以后都可以领取到心仪的offer哦

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在Vue3.0使用ant-design-vue,需要先安装ant-design-vue和Vue3.0。可以使用npm或yarn来安装这些依赖项。 安装完成后,需要在Vue应用程序引入ant-design-vue组件。可以在main.js文件导入ant-design-vue并注册组件,如下所示: ``` import { createApp } from 'vue'; import App from './App.vue'; import Antd from 'ant-design-vue'; import 'ant-design-vue/dist/antd.css'; const app = createApp(App); app.use(Antd); app.mount('#app'); ``` 在这个例子,我们导入了App.vue组件和ant-design-vue库。我们还导入了ant-design-vue的CSS文件。然后,我们使用Vue的createApp方法创建了一个Vue应用程序实例,并使用Antd插件注册了所有的ant-design-vue组件。最后,我们将应用程序挂载到DOM元素上。 现在,您可以在Vue组件使用ant-design-vue组件了。例如,在App.vue组件,您可以添加一个Button组件,如下所示: ``` <template> <div> <a-button type="primary">Primary Button</a-button> </div> </template> ``` 这将在页面上显示一个蓝色的主要按钮。您可以使用其他ant-design-vue组件来构建您的Vue应用程序界面。 ### 回答2: Ant Design Vue是一个优秀的Vue UI组件库,具有丰富的组件和可定制性。在Vue3.0与ant-design-vue一起使用时,需要遵循以下几个步骤: 1. 使用Vue CLI 5.0创建一个Vue3.0项目:可以使用Vue CLI 5.0创建一个新的Vue3.0项目,确保已安装Vue CLI 5.0,并在命令行输入以下命令: ``` vue create my-project ``` 2. 安装ant-design-vue:在项目目录,可以通过npm或yarn安装ant-design-vue,输入以下命令: ``` npm install ant-design-vue --save ``` 或 ``` yarn add ant-design-vue ``` 安装完成后,需要在main.js引入并使用它,输入以下代码: ``` import { createApp } from 'vue' import App from './App.vue' import Antd from 'ant-design-vue'; import 'ant-design-vue/dist/antd.css'; const app = createApp(App) app.use(Antd) app.mount('#app') ``` 3. 使用ant-design-vue组件:在Vue3.0,可以使用ant-design-vue组件来构建UI界面,例如: ``` <template> <a-button type="primary"> Primary Button </a-button> </template> <script> import { Button } from 'ant-design-vue'; export default { name: 'MyComponent', components: { 'a-button': Button } } </script> ``` 在代码导入Button组件,然后在模板通过自定义标签来使用Button组件。 除了以上步骤,还可以通过自定义主题和按需加载来进一步优化ant-design-vue的使用。总之,在Vue3.0使用ant-design-vue可以帮助开发人员快速构建出美观、易用的Web应用程序。 ### 回答3: Vue3.0是目前最新的Vue框架版本,它与前几个版本相比,在性能和开发效率上都有了很大的提高。而Ant Design Vue是一个非常流行的UI框架,也是许多Vue开发者所青睐的选择之一。在Vue3.0使用Ant Design Vue的方法如下: 1. 安装Ant Design Vue 在命令行输入以下命令安装Ant Design Vue: ```bash npm install ant-design-vue --save ``` 2. 在Vue项目引入Ant Design Vue 在main.js文件导入Ant Design Vue: ```javascript import { createApp } from 'vue'; import Antd from 'ant-design-vue'; import App from './App.vue'; import 'ant-design-vue/dist/antd.css'; const app = createApp(App); app.use(Antd); app.mount('#app'); ``` 在上述代码,我们通过import语句将Ant Design Vue导入到了我们的Vue项目,并使用app.use(Antd)将其引入到了Vue实例。我们还通过import 'ant-design-vue/dist/antd.css'语句引入了Ant Design Vue的CSS样式文件,以便在项目使用Ant Design Vue的样式。 3. 使用Ant Design Vue组件 现在,我们已经可以在Vue项目使用Ant Design Vue组件啦!例如,我们可以在App.vue这样使用Button组件: ```vue <template> <div> <a-button type="primary">Primary Button</a-button> </div> </template> <script> export default { name: 'App', }; </script> ``` 最后,我们需要运行npm run serve命令以启动Vue项目。在浏览器打开http://localhost:8080,应该就可以看到一个使用Ant Design Vue Button组件的页面啦! 总之,使用Ant Design Vue可以让我们快捷、高效地构建Vue项目。希望这篇回答能够帮助到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值