一次对图钉affix的改造

2,214 阅读5分钟

前言

早几天在进行个人博客的搭建,技术上用到了vue+nuxtjs。众所周知,身为一个成熟的脚手架,必然附带着很多可以选择的工具,就好像去菜市场带个菜筐,挑挑拣拣是免不了的。

脚手架命令一敲,让我选择的就很多,其中有一条是选择一个UI框架。

其中有几个熟悉的UI,如bootstrap,elementui。每一个我都去官网看了一遍,看了一下大家的功能和样式。其实大同小异,无非有一些组件的写法和实现方式有点差距,更大的还是样式风格的区分。

最终查看官网,满足于iview的ui组件,风格简洁,而且有一个affix(图钉)的一个组件。倒不是什么新鲜的东西,实现也不会很难,但刚好需要,它刚好在,正好就剖析一遍他们的代码,满足我博客上一个小小的需求加以改造!

为什么要看iview的源码?

一套完整的高质量组件库,一定有值得我学习的地方。

一方面是为了学习,一方面也是了解内部构造,方便接下来的改造。

我想改造个什么东西嘞?

就是一个markdown文本的导航条,类似掘金的这个:

lfUpFA.png

只不过我目前实现的是一个很丑丑的...

lfUFQf.png

样式什么的后期慢慢补吧,主要是想拥有一个固定位置的,能够像affix所提供的功能一样。

能够把markdown解析后生成的dom元素获取出来,生成一份目录,就可以满足文章页的需求啦。

affix的实现过程

看源码肯定要先clone一份,这里是iview源码链接

下载后找到src/components/affix目录,发现里面有两个文件

  • affix.vue
  • index.js

首先打开index.js看个究竟

import Affix from './affix.vue';
export default Affix;

原来只是一个导出的文件,为了方便在src/index.js文件里能够统一的注册组件。

这个文件里写了一个install方法,可以自动把各个目录下的文件注册。

那么接下来具体就看一下 affix.vue 文件怎么写的:

    <div>
        <div ref="point" :class="classes" :style="styles">
            <slot></slot>
        </div>
        <div v-show="slot" :style="slotStyle"></div>
    </div>
    
    ---
    
    const prefixCls = 'ivu-affix';
    
    ---
    computed: {
        classes () {
            return [
                {
                    [`${prefixCls}`]: this.affix
                }
            ];
        }
    },

首先是template,两个div,第一个里面包含插槽,方便外部直接使用,样式什么的也有。

classes是一个计算属性,根据this.affix布尔值的转变,赋予div自身的class。

至于styles这个变量,全组件一共有两个地方会修改

// Fixed Top

    if ((elOffset.top - this.offsetTop) < scrollTop && this.offsetType == 'top' && !affix) {
        /**中间的逻辑**/
        this.styles = {
            top: `${this.offsetTop}px`,
            left: `${elOffset.left}px`,
            width: `${this.$el.offsetWidth}px`
        };
        this.$emit('on-change', true);
    } else if ((elOffset.top - this.offsetTop) > scrollTop && this.offsetType == 'top' && affix) {
            /**中间的逻辑**/
            this.styles = null;
            this.$emit('on-change', false);
    }
    
// Fixed Bottom

    if ((elOffset.top + this.offsetBottom + elHeight) > (scrollTop + windowHeight) && this.offsetType == 'bottom' && !affix) {
        this.affix = true;
        this.styles = {
            bottom: `${this.offsetBottom}px`,
            left: `${elOffset.left}px`,
            width: `${this.$el.offsetWidth}px`
        };
        this.$emit('on-change', true);
    } else if ((elOffset.top + this.offsetBottom + elHeight) < (scrollTop + windowHeight) && this.offsetType == 'bottom' && affix) {
        this.affix = false;
        this.styles = null;
        this.$emit('on-change', false);
    }

代码的逻辑就是说,如果代码触碰到顶部或底部的临界点时,将this.affix属性更改为true,那么同时更改的还有,soltStyle以及styles两个属性,两个属性写好了模版上的div的样式

模版里的第二个div也就是

<div v-show="slot" :style="slotStyle"></div>

这段代码,其中slotStyle起到一个防止塌陷的功能,因为第一个divfixed状态下,会突然减少一段文档的高度和宽度,造成奇怪的bug,比如疯狂闪现,或者无法成功进行fixed定位。

在到达触发fixed条件时,会发出一个emit事件,传给父组件,目前究竟是否还在fixed状态,实现一个事件的回调函数,方便外部处理事件。

affix解决的问题

值得一提的是,iview针对ts做了一些处理,在type目录里写了所有的d.ts文件,完善的 TypeScript 支持

我对affix的改造

  • 原本的图钉,是固定在一个位置不动的,现在需要它到一个固定的位置,消失或出现,稍微修改了判断条件
  • markdown文件计算出所需要创建的子节点,作为content属性直接传入,直接渲染出目录结构
  • 把之前图钉存在的一些功能剔除,保留部分计算函数
  • 样式文件是重写的,因为这个组件很简单,没必要复用iview的affix
  • 直接对affix进行绝对定位,如果屏幕不满足展示条件就不展示

总结

这是几个月之前存在草稿箱的一次阅读源码的经历,之后也读了其他的源码,看到大佬们实现的效果和我自己写的有什么差别。

  • 很多时候都是遇到问题先谷歌,然后去git搜英文单词,分析一下好不好用,拉下来试试。逐渐的觉得这种方式能解决问题,但并不快乐。

  • 为了学习更多东西,在拉代码之前,先偷师一下整体结构,看看我想要的核心功能,大佬是怎么实现的。其实除了很硬核的功能,看一看还是可以看懂的,不论要不要自己去实现,偷到东西就是让人快乐的。

  • 这次学习之旅是对一个小组件进行分析,像elementui,antd都有很多人去分析,看到写的文章也收获颇多,确实很多框架的架构和技巧都是值得学习的,之前的一些感悟可能也有不对的地方。

至于为什么会把这个小过程写出来,并且发出来?因为好多时候确实写得代码比这个逻辑复杂的多,其实并没能总结成文字,虽然会有肌肉记忆,但是写出来的文章,很多时候还会想再回头看一看,那就会有收获。虽然这篇水文不是为了读者哦~但如果能有一些帮助(毕竟当时就想找这么一篇文章的来着)那就更好了。

贴出来的代码片段有点散乱,酌情观看。