vue3项目常用功能分享

Vue3常用功能分享

本文主要分享一下在使用vue3开发项目时的一些常用功能

一、自动注册全局组件

自动注册components目录下所有vue组件并以组件的文件名为组件的名称

// components/index.ts

import { type App, defineAsyncComponent } from 'vue'
const components = Object.entries(import.meta.glob('./**/*.vue'))
const preFix = 'Es'
export default {
  // use 的时候
  install: (app: App) => {
    components.forEach(([key, comp]) => {
      // 得到组件的名称
      const name = getCompName(comp.name || key)
      app.component(preFix + name, defineAsyncComponent(comp as any))
    })
  }
}

function getCompName(key: string) {
  const nameReg = //(\w+).vue/
  return nameReg.test(key) ? key.match(nameReg)![1] : key
}
  1. 使用 import.meta.glob 动态导入所有以 .vue 结尾的文件,并将它们作为键值对的形式存储在 components 变量中。
  2. install 方法中,通过遍历 components 数组,对每个组件进行注册
  3. 通过 comp.name 获取组件的名称,如果名称不存在,则使用组件的路径 key。然后,使用 defineAsyncComponent 将组件定义为异步组件
  4. getCompName 函数用于从组件路径中提取组件的名称。使用正则对组件路径进行匹配,提取出路径中最后一个斜杠后的单词作为组件名称

使用这个插件:

import { createApp } from 'vue'
import App from './App.vue'

import MyComponents from './components'

createApp(App).use(MyComponents).mount('#app')

自动注册全局组件虽然很方便,但在使用时缺少了ts类型提示,下面介绍一下为全局组件添加类型提示

为全局组件添加类型提示

这需要我们自己编写.d.ts声明文件

// src/typings/component.d.ts
export {}

declare module 'vue' {
  export interface GlobalComponents {
    EsDialog: typeof import('../components/Dialog.vue')['default']
  }
}

在这里插入图片描述

二、函数式图片预览

图片预览是一个比较常用的功能,封装成函数调用可以简化我们使用的方式

基于 element-plus 的 ElImageViewer 组件:对于有类似的功能都可以使用这种方式,例如我们想使用函数调用的方式弹窗

// utils/preview.ts
import { createVNode, render } from 'vue'
import { ElImageViewer, ImageViewerProps } from 'element-plus'

type PreviewOption = Partial<ImageViewerProps>

export function preview(option: PreviewOption) {
  const container = document.createElement('div')
  let vm = createVNode(ElImageViewer, {
    ...option,
    onClose() {
      render(null, container)
    }
  })

  // 将组件渲染成真实节点
  render(vm, container)
  document.body.appendChild(container.firstElementChild!)
}
  1. preview 函数接受一个 option 参数,它是 PreviewOption(ImageViewerProps 类型的部分可选类型) 类型的对象,用于配置图片预览的选项。
  2. 函数内部,首先创建了一个 div 元素作为容器,用于渲染预览组件。
  3. 使用 createVNode 创建了一个 ElImageViewer 组件实例 vm
  4. 使用 render 方法将 vm 渲染为真实的节点,并将其挂载到之前创建的容器中,最后添加到body页面上
  5. 传入的 props中 监听 close 事件移除节点

调用

preview({
  urlList: ['https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg'],
  initialIndex: 0
})

三、手动封装 svgIcon 组件

这节主要是vangle组件库的icon组件封装方式的介绍,为想自己手动封装svgIcon组件的朋友可以作个参考

以下下是代码实现

  • Icon.vue
<template>
  <i class="es-icon" :style="style" v-html="icon"></i>
</template>

<script lang="ts" setup>
import { computed, CSSProperties } from 'vue'
import { IconProps, getIcon } from './icon'

const props = defineProps(IconProps)

const icon = computed(() => getIcon(props.name))
const style = computed<CSSProperties>(() => {
  if (!props.size && !props.color) return {}

  return {
    fontSize: typeof props.size === 'string' ? props.size : `${props.size}px`,
    '--color': props.color,
  }
})
</script>

<style scoped lang="scss">
.es-icon {
  --color: inherit;
  height: 1em;
  width: 1em;
  line-height: 1em;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  position: relative;
  fill: currentColor;
  color: var(--color);
  font-size: inherit;
  font-style: normal;
  svg {
    height: 1em;
    width: 1em;
  }
}
</style>
  • 以下是getIcon的实现 icon.ts
export const svgs = import.meta.glob('./svg/*.svg', { eager: true, as: 'raw' })
export const IconProps = {
  name: String,
  color: String,
  size: [String, Number]
}

export const getIcon = (name?: string) => {
  if (!name) return ''
  return svgs[`./svg/${name}.svg`]
}

使用 import.meta.glob 动态导入svg目录下所有以 .svg 结尾的文件,as: 'raw' 表示导入的文件内容以原始字符串形式保存

getIcon 根据name获取svg的内容

  • 直接使用
<es-icon name="add-location" />
<es-icon name="add-location" color="pink" />
<es-icon name="add-location" color="pink" :size="30" />

四、封装拖拽钩子函数

import { onBeforeUnmount, onMounted, watchEffect, Ref } from 'vue'

export const useDraggable = (
  targetRef: Ref<HTMLElement | undefined>,
  dragRef: Ref<HTMLElement | undefined>,
  draggable: Ref<boolean>
) => {
  // 保存偏移量
  let transform = {
    offsetX: 0,
    offsetY: 0,
  }

  const onMousedown = (e: MouseEvent) => {
    const downX = e.clientX
    const downY = e.clientY
    const { offsetX, offsetY } = transform

    // 获取拖拽目标的位置和尺寸信息
    const targetRect = targetRef.value!.getBoundingClientRect()
    const targetTop = targetRect.top
    const targetWidth = targetRect.width
    const targetHeight = targetRect.height

     // 计算拖拽目标在页面中的可移动范围
    const clientWidth = document.documentElement.clientWidth
    const clientHeight = document.documentElement.clientHeight

    const minLeft = -targetRect.left + offsetX
    const minTop = -targetTop + offsetY
    const maxLeft = clientWidth - targetRect.left - targetWidth + offsetX
    const maxTop = clientHeight - targetTop - targetHeight + offsetY

    const onMousemove = (e: MouseEvent) => {
      // 计算移动后的位置
      /**
       * offsetX + e.clientX - downX: 初始偏移量 offsetX 加上 e.clientX - downX 移动的距离得到拖拽元素在水平方向上的新位置。
       * Math.max(offsetX + e.clientX - downX, minLeft) 确保新位置大于等于最小可移动位置 minLeft,即不超出左边界
       * Math.min(..., maxLeft) 确保新位置小于等于最大可移动位置 maxLeft,即不超出右边界。这样做的目的是防止拖拽元素移出指定的范围。
       */
      const moveX = Math.min(
        Math.max(offsetX + e.clientX - downX, minLeft),
        maxLeft
      )

      // 和上面同理
      const moveY = Math.min(
        Math.max(offsetY + e.clientY - downY, minTop),
        maxTop
      )
      
      // 更新偏移量和元素位置
      transform = {
        offsetX: moveX,
        offsetY: moveY,
      }
      targetRef.value!.style.transform = `translate(${moveX}px, ${moveY}px)`
    }

    const onMouseup = () => {
      // 移除事件监听
      document.removeEventListener('mousemove', onMousemove)
      document.removeEventListener('mouseup', onMouseup)
    }

    // 监听鼠标移动和鼠标抬起事件
    document.addEventListener('mousemove', onMousemove)
    document.addEventListener('mouseup', onMouseup)
  }

  const onDraggable = () => {
    if (dragRef.value && targetRef.value) {
      dragRef.value.addEventListener('mousedown', onMousedown)
    }
  }

  const offDraggable = () => {
    if (dragRef.value && targetRef.value) {
      dragRef.value.removeEventListener('mousedown', onMousedown)
    }
  }

  onMounted(() => {
    watchEffect(() => {
      if (draggable.value) {
        onDraggable()
      } else {
        offDraggable()
      }
    })
  })

  onBeforeUnmount(() => {
    offDraggable()
  })
}

useDraggable 的函数,接受三个参数:

targetRef:拖拽目标Ref dragRef:拖拽触发区域Ref draggable: 开启/关闭拖拽

在函数内部定义了一个变量 transform,用于保存拖拽过程中的偏移量。

  1. 在 onMousedown 函数中,首先记录下鼠标按下时的坐标 downX 和 downY,以及当前的偏移量 offsetX 和 offsetY
  2. 获取拖拽目标元素的位置和尺寸信息,计算出拖拽目标在页面中可移动的范围。
  3. 移动时 onMousemove 中,根据鼠标移动的位置计算出新的偏移量 moveX 和 moveY,并更新 transform 对象和拖拽目标元素的位置
  4. 最后,定义了一个 onMouseup 函数,当鼠标抬起时执行的事件处理函数。在函数内部,移除鼠标移动和鼠标抬起事件的监听。

五、vscode 中 vue3 代码片段

针对 Vue 3.2+ 的代码片段模板,用于在 VSCode 中快速生成 Vue 组件的模板代码

在项目的 .vscode 目录下创建一个名为 vue3.2.code-snippets 的文件,它是一个 JSON 格式的代码片段文件

{
  "Vue3.2+快速生成模板": {
    "prefix": "Vue3.2+",
    "body": [
      "<template>",
      "\t<div>\n",
      "\t</div>",
      "</template>\n",
      "<script setup lang='ts'>",
      "</script>\n",
      "<style lang='scss' scoped>\n",
      "</style>",
      "$2"
    ],
    "description": "Vue3.2+"
  }
}

prefix:定义了在代码编辑器中触发该代码片段的前缀,这里设定为 “Vue3.2+” body:定义了代码片段的主体部分,它是一个数组,包含多行模板代码 description:对该代码片段的描述

在vue文件中输入 “Vue3.2+”(会有自动提示) 按下 Tab 键,就会自动插入这段模板代码。你可以根据需要自行修改和完善这个模板。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/136283.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sprint Boot 学习路线 5

Spring MVC Spring MVC是Spring框架的一部分&#xff0c;是一个Web应用程序框架。它旨在使用Model-View-Controller&#xff08;MVC&#xff09;设计模式轻松构建Web应用程序。 在Spring MVC中&#xff0c;应用程序被分为三个主要组件&#xff1a;Model、View和Controller。M…

2023年09月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 用枚举算法求解“100以内既能被3整除又能被4整除的元素”时,在下列数值范围内,算法执行效率最高的是?( ) A:1~101 B:4~100 C:12~100 D:12~96 答案:D 题目要求找出在 100…

什么是α测试β测试和灰度测试?

前言 看这个标题 α测试 β测试 &#xff0c;应该读做&#xff1a;阿尔法测试&#xff0c;贝特测试。 这样大家应该就都听过了吧&#xff0c;听过的同学举个手哈&#xff0c;听过的人大体上知道俩个测试是干嘛的&#xff0c;但是具体标准的大流程估计没人研究过&#xff0c;所…

VUE组件的生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xff0c;比如设置好数据侦听&#xff0c;编译模板&#xff0c;挂载实例到 DOM&#xff0c;以及在数据改变时更新 DOM。在此过程中&#xff0c;它也会运行被称为生命周期钩子的函数&#xff0c;让开发者有机会在特定阶…

数据结构 | 图

最小生成树算法 Prime算法 算法思路&#xff1a;从已选顶点所关联的未选边中找出权重最小的边&#xff0c;并且生成树不存在环。 其中&#xff0c;已选顶点是构成最小生成树的结点&#xff0c;未选边是不属于生成树中的边。 例子&#xff1a; 第一步&#xff1a; 假设我们从顶…

docker搭建mysql主从复制

1. 基础环境 环境 名称描述CentOS 7.6Linux操作系统版本docker 20.10.5docker版本mysql 8.0.29mysql镜像版本 节点 节点名称读写/主从地址端口master读节点/主节点192.168.1.6:3306slave1写节点/从节点192.168.1.6:3307slave2写节点/从节点192.168.1.6:3308 2. 主节点 使…

【Redis】Redis-Key的使用

上一篇&#xff1a; redis-server和redis-cli https://blog.csdn.net/m0_67930426/article/details/134361885?spm1001.2014.3001.5501 官网 命令 |雷迪斯 (redis.io) 设置key set name xxxxx 查看key keys * 再设置一个key并且查看 这里查看了两个key&#xff08;name a…

什么是代理模式,用 Python 如何实现 Proxy(代理 或 Surrogate)对象结构型模式?

什么是代理模式&#xff1f; 代理&#xff08;Proxy&#xff09;是一种结构型设计模式&#xff0c;其目的是通过引入一个代理对象来控制对另一个对象的访问。代理对象充当目标对象的接口&#xff0c;这样客户端就可以通过代理对象间接地访问目标对象&#xff0c;从而在访问过程…

【C++笔记】二叉搜索树的模拟实现

【C笔记】二叉搜索树的模拟实现 一、二叉搜索树的概念二、二叉搜索树的模拟实现2.0、定义二叉树节点2.1、非递归接口实现2.1.1、插入2.1.2、查找2.1.3、删除 2.2、递归接口实现2.2.1、插入2.2.2、查找2.2.3、删除 三、升级为K-V模型 一、二叉搜索树的概念 二叉搜索树的概念&am…

systemd-timesyncd

介绍 systemd-timesyncd 是一个用于跨网络同步系统时钟的守护服务。它实现了一个 SNTP 客户端。与NTP的复杂实现相比&#xff0c;这个服务简单的多&#xff0c;它只专注于从远程服务器查询然后同步到本地时钟。除非你打算为客户端提供 NTP 服务器或者连接本地硬件时钟&#xff…

【C++】C++的介绍及其发展史

初识C 一、什么是C&#xff0c;为什么会出现C二、C的发展史三、C的重要性3.1 语言的使用广泛度3.2 在工作领域 四、C的学习路径 及 书籍推荐 一、什么是C&#xff0c;为什么会出现C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。 对于复杂的问题&#xff…

【python】sys-psth和模块搜索路径

我们在导入一个模块的时候&#xff0c;比如说&#xff1a; import math它必然是有搜索路径的&#xff0c;那到底是在哪个目录下面找呢&#xff1f;Python解释器去哪里找这个文件呢&#xff1f;只有找到这个文件才能读取、装载运行该模块文件。 它一般按照如下路径寻找模块文件…

混合A Star算法

混合A Star算法就是将A*与Lattice Graph相关联。 使用栅格地图&#xff0c;保持在栅格地图的这个节点方格里都只记录一个机器人的State&#xff0c;例如图上的弯弯的线&#xff0c;进行剪枝&#xff0c;看一个节点的Cost&#xff08;时间、消耗的能量、路径的长度、终端状态惩…

ElasticSearch学习和使用 (使用head软件可视化es数据)

使用步骤 直接使用 Elasticsearch的安装和使用 下载Elasticsearch6.2.2的zip包&#xff0c;并解压到指定目录&#xff0c;下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2运行bin目录下的elasticsearch.bat启动Elasticsearch安…

五个做原型的好处和意义

绘制原型不仅是产品开发的重要基础&#xff0c;也是UI设计师在设计过程中应该采用的主要路径标识。绘制原型的重要性不亚于建筑师手中的设计图纸&#xff01;虽然原型设计可能会给产品开发带来一些误解&#xff0c;但毫不夸张地说&#xff0c;任何开发人员都可以将优秀的开发产…

SMART PLC MODBUSTCP速度测试

SMART PLC MODBUSTCP通信详细介绍请参看下面文章链接: S7-200SMART PLC ModbusTCP通信(多服务器多从站轮询)_matlab sumilink 多个modbustcp读写_RXXW_Dor的博客-CSDN博客文章浏览阅读6.4k次,点赞5次,收藏10次。MBUS_CLIENT作为MODBUS TCP客户端通过S7-200 SMART CPU上的…

《015.SpringBoot+vue之音乐网》【前后端分离】

《015.SpringBootvue之音乐网》【前后端分离】 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatisMySQL; 前台&#xff1a;Vue3.0 TypeScript Vue-Router Vuex Axios …

C++ 运算符重载(Complex复数类)

Complex复数类 Complex.h #ifndef COMPLEX_H #define COMPLEX_H#include <iostream> using namespace std;class Complex { private:double real 0; //复数的实部double imag 0; //复数的虚部 public:Complex() default; /*无参构造*/Complex(double r); …

今起不再“没完没了的接龙斗嘴”

今天本“人民体验官”推广人民日报官方微博&#xff08;转央视网&#xff09;的文化产品《数字减负不能比减脂还难》。 截图&#xff1a;来源“人民体验官”推广平台 在时下的一些网络自媒体平台之上&#xff0c;的确存在“越拉越多的群&#xff0c;没完没了的接龙&#xff0c…

微信支付平台C#SDK_微信支付.net SDK

一、微信支付平台C# SDK V3 https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat/blob/main/docs/WechatTenpayV3 接口对应整理&#xff1a; https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat/blob/main/docs/WechatTenpayV3/Basic_Mod…