elementui源码学习之仿写一个el-link

简介: elementui源码学习之仿写一个el-link
本篇文章记录仿写一个 el-link组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件。源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解。github仓库地址如下: https://github.com/shuirongshuifu/elementSrcCodeStudy

组件思考

简而言之,el-link组件就是给a标签包了一层,加上一些样式。故link组件除了拥有a标签的功能,还需要有一些能够使用的样式效果。所以,我们仿写这个组件标签的时候主要是复习一下传参的变量和样式的搭配,从而实现我们需要的效果

组件的需求

  • 给link组件加上不同类型的链接样式,比如普通样式、成功样式、警告样式、危险样式的链接样式
  • 给link组件加上鼠标悬浮时下划线。封装组件中使用的是伪元素搭配border-bottom实现的
  • 通过传参去控制link组件什么时候有下划线,什么时候没有下划线
  • 另外,也需要考虑组件会被禁用,禁用时,不能点击,不能跳转,且更换一下样式效果
  • 同时,link组件跳转链接时的一些操作
  • 也要是考虑到link组件需要搭配小图标去使用(本例中以饿了么icon为例)

组件的效果图

666.gif

组件实现分析

给link组件加上链接样式

这里使用动态class的数组用法去控制,如下简单例子:

// html
<a
  :class="[
    'myLink',
    type
]"
>

// js
props:{
    type: String, // 标签颜色的类型
}

// css
.primary { color: #2d8cf0; }
.success { color: #19be6b; }
.warning { color: #f90; }
.danger { color: #ed4014; }

由上,比如传递的type参数值为success,那么a标签就会加上一个success的类名,这样的话,就会找到对应css中的类名,从而呈现相应的颜色效果(当然type的值在底下css中要有对应的)

给link组件加上鼠标悬浮时下划线

其实类似这种,悬浮下划线,悬浮上划线,悬浮特殊背景控制。大致可以归纳为在主要dom旁边加上一个小东西,都可以考虑使用伪元素

关于啥是伪元素,这里不赘述

我们看下方的鼠标悬浮上划线和下划线的效果图,这个效果图就是使用伪元素搭配定位以及border去实现的

777.gif

代码:

// css
span {
     font-size: 24px;
     position: relative;
}
span:hover::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 0;
    /* 定位控制 */
    top: 2px;
    border-top: 1px solid red;
}
span:hover::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 0;
    /* 定位控制 */
    bottom: -2px;
    border-bottom: 1px solid red;
}

// html
<span>早上好,程序猿兽们</span>

通过传参控制是否加上下划线(即:是否加上这个下划线类名)

这里依然是使用动态class的数组用法,在数组中书写四元表达式,简约代码如下:

<a
    :class="[
      'myLink',
       hideUnderline ? '' : disabled ? '' : 'underline'
    ]"
  >

大意:即不去隐藏下划线,又不去禁用这个link组件的时候,才加上underline类名,才产生下划线

使用v-bind="$attrs"兜底a标签的其他的未在props中声明的参数

<a
    :href="disabled ? null : href"
    v-bind="$attrs"
  >

我们知道a标签中有很多原生属性,如下图:

888.png

这么多a标签的属性,我们不可能在封装的组件的时候,如果把每一个属性都在props中声明,倒是有些麻烦。所以此时$attr就应运而生了。关于$attr和$listener的用法,这里也不赘述,大家只要记得$attr是做参数属性兜底的即可。详情可看笔者之前写的文章:https://juejin.cn/post/6982727094937583647

组件需求由于篇幅原因就不赘述了,详情见代码中注释(毕竟这个组件其实也挺简单的),接下来上代码

代码

演示的话,直接复制粘贴即可,结合注释更好理解。注意my-divider组件是之前封装的组件。

笔者封装的组件和官方封装的组件在一些细节的地方可能略有不同,算是多一个解法思路吧 ^o^

使用代码

<template>
  <div class="box">
    <my-divider lineType="dashed" content-position="left"
      >五种链接样式</my-divider
    >
    <my-link>默认超链接</my-link>
    <my-link type="primary">primary超链接</my-link>
    <my-link type="success">success超链接</my-link>
    <my-link type="warning">warning超链接</my-link>
    <my-link type="danger">danger超链接</my-link>
    <my-divider lineType="dashed" content-position="left"
      >是否有下划线</my-divider
    >
    <my-link>默认有下划线</my-link>
    <my-link hideUnderline>也可隐藏下划线</my-link>
    <my-divider lineType="dashed" content-position="left"
      >禁用链接样式</my-divider
    >
    <my-link disabled>禁用|默认超链接</my-link>
    <my-link type="primary" disabled>禁用|primary超链接</my-link>
    <my-link type="success" disabled>禁用|success超链接</my-link>
    <my-link type="warning" disabled>禁用|warning超链接</my-link>
    <my-link type="danger" disabled>禁用|danger超链接</my-link>
    <my-divider lineType="dashed" content-position="left"
      >添加href和target原生属性</my-divider
    >
    <my-link type="primary" href="https://www.baidu.com/">默认self跳转</my-link>
    <my-link type="success" target="_blank" href="https://cn.bing.com/"
      >支持原生属性target等</my-link
    >
    <my-divider lineType="dashed" content-position="left"
      >通过icon属性或者直接在默认插槽中添加图标</my-divider
    >
    <my-link type="primary" icon="el-icon-share">默认图标在前方</my-link>
    <my-link type="success" icon="el-icon-share" back
      >back属性控制图标在后方</my-link
    >
    <my-link type="primary"
      ><i class="el-icon-goods"></i>默认插槽前方加图标</my-link
    >
    <my-link type="success"
      >默认插槽后方加图标 <i class="el-icon-goods"></i
    ></my-link>
    <my-divider lineType="dashed" content-position="left"
      >绑定事件使用</my-divider
    >
    <my-link @click="clickThis" type="danger" icon="el-icon-aim"
      >点击这个哦</my-link
    >
  </div>
</template>

<script>
export default {
  methods: {
    clickThis() {
      console.log("注意:当myLink禁用或有href属性时,点击事件失效");
    },
  },
};
</script>

封装组件代码

<template>
  <!-- 
       hideUnderline ? '' : disabled ? '' : 'underline',
       先看hideUnderline是否为true,为true则要隐藏下划线,即不加underline类名
       再看disabled是否为true,为true则禁用,禁用也要隐藏下划线,即也不加underline类名
       若既不隐藏下划线又不禁用链接,则加上underline类名用于显示下划线
   -->
  <a
    :href="disabled ? null : href"
    :class="[
      'myLink',
      hideUnderline ? '' : disabled ? '' : 'underline',
      type,
      disabled ? 'toDisabled' : '',
    ]"
    v-bind="$attrs"
    @click="handleClick"
  >
    <!-- 
       这里加上v-bind="$attrs"是为了传递更多的属性,做一个参数兜底的功能效果。因为a标签还有其他很多属性,如:
       target、download、type等。props中未声明的参数,会被$attr兜底交给a标签使用。故加了v-bind="$attrs"以后,
       在外层my-link标签上,我们便可正常使用除props中声明的属性了,如使用target原生属性:<my-link target="_blank" href="xxx"/>
    -->
    <!-- 内容区 -->
    <span class="aContent" :class="{ spaceC: icon, back: back }">
      <i :class="icon" v-if="icon"></i>
      <span><slot></slot></span>
    </span>
  </a>
</template>

<script>
export default {
  name: "myLink",
  props: {
    href: String, // a标签的href属性,用于跳转
    disabled: Boolean, // 是否禁用超链接
    type: String, // 标签颜色的类型
    // 是否隐藏下划线
    hideUnderline: {
      type: Boolean,
      default: false, // 默认不隐藏下划线,默认展示下划线
    },
    icon: String, // 使用饿了么UI的小图标
    back: Boolean, // 设置图标在内容后方
  },
  methods: {
    handleClick(event) {
      // 禁用状态下不允许传递事件
      if (this.disabled) {
        return;
      }
      // 有链接了也不允许传递事件
      if (this.href) {
        return;
      }
      // 没有禁用没有链接,便可正常传递事件
      this.$emit("click", event);
    },
  },
};
</script>

<style lang="less" scoped>
// 默认样式
.myLink {
  display: inline-block;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  color: #666; // 默认颜色,当然也可以使用type类型的配色
  text-decoration: none; // 去除a标签默认的下划线(不使用自带的下划线效果)
  position: relative; // 定位搭配伪元素实现悬浮下划线效果
  margin: 4px;
  .aContent {
    display: flex;
    align-items: center;
  }
  // 当传的有图标的时候,把第一个元素加上一个右边距,产生间距
  .spaceC > :first-child {
    margin-right: 4px;
  }
  // 控制弹性盒方向,控制图标在后方位置(默认前方位置)
  .back {
    flex-direction: row-reverse;
  }
  // 注意这里要覆盖上方spaceC的样式
  .back > :first-child {
    margin: 0 0 0 4px;
  }
}
// 通过变量控制是否有这个underline类名,从而控制是否有下划线
.underline:hover::after {
  // 这样的话,元素在哪,下划线就在哪里
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  bottom: -2px;
  // 注意,这里不设置颜色,就会跟随参考定位的元素的颜色了
  border-bottom: 1px solid;
}
// 下方的配色是抄iview的
.primary {
  color: #2d8cf0;
}
.success {
  color: #19be6b;
}
.warning {
  color: #f90;
}
.danger {
  color: #ed4014;
}
// 通过透明度模拟禁用的颜色样式效果,要不然要写两份颜色会稍微麻烦一些
.toDisabled {
  opacity: 0.36;
  cursor: not-allowed;
}
</style>
相关文章
|
3月前
|
人工智能 前端开发 Java
AGENTS.md跨工具标准实战:终结AI编码配置碎片化,Cursor/Claude/Codex全兼容落地手册
随着AI编程助手大规模进入研发工作流,Cursor、Claude Code、Codex、Gemini CLI等工具被团队同时使用的场景越来越普遍。不同工具各自定义专属规则配置文件,Cursor依赖`.cursorrules`,Claude Code使用`CLAUDE.md`,Codex早期使用`AGENT.md`。同一套编码约束、架构规则、项目构建命令,团队需要维护多份内容高度重合的文档。修改一条编码规范,就要同步更新全部配置,极易出现内容不一致,导致AI获取到的项目信息出现偏差,生成的代码质量参差不齐,大量时间耗费在人工修改AI产出的不合规代码上。
481 0
|
3月前
|
人工智能 自然语言处理 安全
阿里云Qoder CN产品简介:AI智能体产品,基于千问大模型等,支持代码智能生成、研发智能问答
本文介绍了阿里云Qoder CN(原灵码)这款面向AI原生时代的全栈智能开发与生产力提效产品。其深度集成通义千问等主流大模型,以代码智能生成、研发智能问答、工程级智能体交互为核心能力,覆盖从个人开发者到企业研发团队的全场景需求。产品矩阵包含Qoder Desktop、QoderWork、QoderWake等多元形态,支持端到端工程级交付、企业级知识增强与数据安全。文章详细梳理了从免费社区版到企业专属版的分层订阅方案,并重点解析了当前一周年庆限时福利。
|
8月前
|
SQL 监控 数据可视化
5 步搞定 MySQL 数据差异对比 + 修复,NineData 手把手教您
做 MySQL 数据迁移、数据备份,怎么快速完成数据一致性对比?发现差异后怎么高效修复?很多 DBA 仍在通过脚本和人工操作完成数据校验,步骤繁琐且易出现人为误差。通过 NineData 平台,即可按照上述教程完成 MySQL 数据对比与修复,实现数据一致性校验的自动化与高效化,解锁 MySQL 数据对比的高效方式,支持核心对比功能,让数据一致性校验更简单!
|
网络协议 API PHP
探索PHP的异步编程:使用ReactPHP实现非阻塞I/O
【8月更文挑战第4天】在传统的同步编程模型中,PHP脚本按顺序执行,每个任务必须等待前一个任务完成后才能开始。这种模式在处理I/O密集型操作时,如网络请求或文件读写,会导致性能瓶颈。异步编程提供了一种解决方案,允许多个操作同时进行,从而提高效率。本文将介绍如何使用ReactPHP库在PHP中实现异步编程,并通过代码示例展示其如何优化I/O操作。
770 1
|
JavaScript
Vue3倒计时(Countdown)
这篇文章介绍了如何在Vue 3中创建一个可自定义的倒计时组件(Countdown),允许设置标题、前缀、后缀、格式和样式,并提供了组件的实现代码和使用示例。
1548 2
Vue3倒计时(Countdown)
|
小程序 前端开发 Java
支付宝小程序 PHP 获取用户手机号 验签解密 RSA解密 AES解密
支付宝小程序 PHP 获取用户手机号 验签解密 RSA解密 AES解密
2190 0
|
存储 前端开发 安全
常见的前端加密方式都有哪些?
【4月更文挑战第19天】本文介绍了前端加密的四种常见方法:哈希加密、对称加密、非对称加密和混合加密。哈希加密适用于密码存储,但存在哈希碰撞风险;对称加密速度快,适合安全通信;非对称加密更安全,但速度慢;混合加密结合两者优点,常用于 SSL/TLS。前端加密是数据安全的重要一环,但需与后端安全措施配合以构建全面防护体系。示例代码展示了 JavaScript 中的 SHA-256、AES 和 RSA 加密。
2510 7
|
Go 开发者
GVM:Go语言版本和包管理的神器!
GVM,Go版本管理器,简化了在单机上切换不同Go版本的任务。
1110 0
|
存储 SQL 关系型数据库
MySQL5.7到8.0的升级迁移!高效的MySQL跨版本迁移技术解读
NineData是一种高效、稳定且易于使用的迁移工具,可帮助用户将MySQL 5.7迁移到MySQL 8.0。由于MySQL 5.7已经结束生命周期并不再获得技术支持,因此迁移至MySQL 8.0是必要的。NineData提供了数据复制功能,包括数据迁移和实时同步,还支持双向复制。与传统迁移方法相比,NineData具有简单易用、数据一致、强劲性能和高可靠性的优势。它提供了完善的观测和干预能力,保障迁移的成功。此外,NineData还提供了对比功能,确保数据的一致性。通过简单的配置过程,用户可以实现自动化的数据迁移。NineData还提供了完善的观测和干预能力,帮助用户追踪迁移进展并诊断和修复
2307 3