最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【element3-小哈喽参与日记】被摸手后,自己重写Link组件

    正文概述 掘金(Leo丨小哈喽)   2020-12-07   401

    背景

    在参加完开课吧的web全栈课程途中,得知了开课吧团队(现花果山团队:hug-sun)fork了一份element-ui,帮助开发者学习vue3、组件化后,我就偷摸混进了他们的内部群里。

    流程

    具体流程可以参考@春去春又来的手摸手教程 PS:就是他摸的我。

    需求

    我这个小菜鸟,当然是找简单的组件,开始捏软柿子啦。这不就盯上了Link组件! 【element3-小哈喽参与日记】被摸手后,自己重写Link组件

    TDD

    通过slot的方式,定义Link组件内容

    测试

    it('should show content', () => {
        const content = 'Link'
        const wrapper = mount(Link,{
            slots: {
                default: content
            }
        }
        expect(wrapper.text()).toContain('Link')
    })
    

    代码实现

    <template>
        <a>
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink'
    }
    </script>
    <style>
    </style>
    

    通过props.type控制Link组件的主题样式,可选值[primary/success/warning/danger/info]

    测试

    it('set the type, link displays the corresponding style', () => {
        const type = 'primary'
        const wrapper = mount(Link,{
            props: {
                type
            }
        }
        expect(wrapper.classes()).toContain(`el-link--${type}`)
    })
    

    代码实现

    <template>
        <a :class="classes">
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            }
        },
        setup(props){
            const classes = useClasses(props)
            return {
                classes
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : ''
        ]
    }
        
    </script>
    <style>
    </style>
    

    通过props.disabled控制Link组件是否禁用

    测试

    it('set the disabled, link displays the corresponding style', () => {
        const disabled = true
        const wrapper = mount(Link, {
            props: {
                disabled
            }
        })
        expect(wrapper.classes()).toContain('is-disabled')
        
    })
    

    代码实现

    <template>
        <a :class="classes">
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            },
            disabled: {
                type: Boolean,
                default: false
            }
        },
        setup(props){
            const classes = useClasses(props)
            return {
                classes
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : '',
            props.disabled && 'is-disabled'
        ]
    }
        
    </script>
    <style>
    </style>
    

    通过props.underline控制Link组件是否显示下划线

    测试

    it('set the underline, link displays the corresponding style', () => {
        const underline = true
        const wrapper = mount(Link, {
            props: {
                underline
            }
        })
        expect(wrapper.classes()).toContain('is-underline')
    })
    

    代码实现

    <template>
        <a :class="classes">
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            },
            disabled: {
                type: Boolean,
                default: false
            },
            underline: {
                type: Boolean,
                default: true
            }
        },
        setup(props){
            const classes = useClasses(props)
            return {
                classes
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : '',
            props.disabled && 'is-disabled',
            props.underline && !props.disabled && 'is-underline'
        ]
    } 
    </script>
    <style>
    </style>
    

    通过props.href制定Link组件的图片

    测试

    it('set the href', () =>{
        const href = 'https://element3-ui.com/'
        const wrapper = mount(Link, {
            props: {
                href
            }
        }
        expect(wrapper.attributes('href')).toBe(href)
    })
    

    代码实现

    <template>
        <a :class="classes" :href=" disabled ? null : href" v-bind="$attrs">
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            },
            disabled: {
                type: Boolean,
                default: false
            },
            underline: {
                type: Boolean,
                default: true
            },
            href: String
        },
        setup(props){
            const classes = useClasses(props)
            return {
                classes
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : '',
            props.disabled && 'is-disabled',
            props.underline && !props.disabled && 'is-underline'
        ]
    } 
    </script>
    <style>
    </style>
    

    通过props.icon指定Link组件的图片

    测试

    it('set the icon, link displays the corresponding style', () => {
        const icon = 'el-icon-search'
        const wrapper = mount(Link, {
            props: {
                icon
            }
        }
        const i = wrapper.find('i')
        expect(i.exists()).toBe(true)
        expect(i.classes()).toContain(icon)
    })
    

    代码实现

    <template>
        <a :class="classes" :href=" disabled ? null : href" v-bind="$attrs">
            <i :class="icon"></i>
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            },
            disabled: {
                type: Boolean,
                default: false
            },
            underline: {
                type: Boolean,
                default: true
            },
            href: String,
            icon: String
        },
        setup(props){
            const classes = useClasses(props)
            return {
                classes
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : '',
            props.disabled && 'is-disabled',
            props.underline && !props.disabled && 'is-underline'
        ]
    } 
    </script>
    <style>
    </style>
    

    用户可以自定义click事件,但组件设置禁用或href属性后,用户的自定义事件静默失效

    测试

    it('should captured click events emitted via click', () => {
        const wrapper = mount(Link)
        wrapper.trigger('click')
        expect(wrapper.emitted('click')).toBeTruthy()
    })
    
    it("when disabled prop is equal to true",() => {
        const wrapper = mount(Link, {
            props: {
                disabled: true
            }
        })
        wrapper.trigger('click')
        expect(wrapper.emitted('click')).toBeFalsy()
    })
    
    it('when href prop is to be truthy', () => {
        const wrapper = mount(Link, {
            props: {
                href: 'https://element3-ui.com/'
            }
        })
        wrapper.trigger('click')
        expect(wrapper.emitted('click')).toBeFalsy()
    })
    

    代码实现

    <template>
        <a :class="classes" :href=" disabled ? null : href" v-bind="$attrs" @click="handleClick">
            <i :class="icon"></i>
            <span>
                <slot></slot>
            </span>
        </a>
    
    </template>
    <script>
    export default {
        name: 'ElLink',
        props: {
            type: {
                type: String,
                default: 'default'
            },
            disabled: {
                type: Boolean,
                default: false
            },
            underline: {
                type: Boolean,
                default: true
            },
            href: String,
            icon: String
        },
        emits:['click'],
        setup(props, { emit }){
            const classes = useClasses(props)
            
            const handleClick = (event) => {
                if(props.disabled) return
                if(props.href) return
                
                emit('click', event)
            }
            return {
                classes,
                handleClick
            }
        }
    }
    const useClasses = (props) => {
        return [
            props.type ? `el-link--${props.type}` : '',
            props.disabled && 'is-disabled',
            props.underline && !props.disabled && 'is-underline'
        ]
    } 
    </script>
    <style>
    </style>
    

    TDD环节挺干的,你以为到这里就结束了吗,还有一些细节需要整理,比如用户没有指定icon,i标签需要加载吗?下面列出完整的代码跟测试

    完整代码

    <template>
      <a
        :class="classes"
        :href="disabled ? null : href"
        v-bind="$attrs"
        @click="handleClick"
      >
        <i v-if="icon" :class="icon"></i>
        <span v-if="$slots.default" class="el-link--inner">
          <slot></slot>
        </span>
      </a>
    </template>
    
    <script>
    export default {
      name: 'ElLink',
      props: {
        type: {
          type: String,
          default: 'default'
        },
        disabled: {
          type: Boolean,
          default: false
        },
        underline: {
          type: Boolean,
          default: true
        },
        href: String,
        icon: String
      },
      emits: ['click'],
      setup(props, { emit }) {
        const classes = useClasses(props)
    
        const handleClick = (event) => {
          if (props.disabled) return
          if (props.href) return
    
          emit('click', event)
        }
        return {
          classes,
          handleClick
        }
      }
    }
    const useClasses = (props) => {
      return [
        props.type ? `el-link--${props.type}` : '',
        props.disabled && 'is-disabled',
        props.underline && !props.disabled && 'is-underline'
      ]
    }
    </script>
    
    <style></style>
    
    

    完整测试代码

    import { mount } from '@vue/test-utils'
    import Link from '../Link.vue'
    describe('Link', () => {
      describe('props', () => {
        it('initialize the Link component', () => {
          const wrapper = mount(Link)
          expect(wrapper.find('i').exists()).toBe(false)
          expect(wrapper.find('span').exists()).toBe(false)
        })
        it('should show content', () => {
          const content = 'Link'
          const wrapper = mount(Link, {
            slots: {
              default: content
            }
          })
          expect(wrapper.text()).toContain('Link')
        })
    
        it('set the type, link displays the corresponding style', () => {
          const type = 'primary'
          const wrapper = mount(Link, {
            props: {
              type
            }
          })
          expect(wrapper.classes()).toContain(`el-link--${type}`)
        })
    
        it('set the disabled, link displays the corresponding style', () => {
          const disabled = true
          const wrapper = mount(Link, {
            props: {
              disabled
            }
          })
          expect(wrapper.classes()).toContain('is-disabled')
        })
    
        it('set the underline, link displays the corresponding style', () => {
          const underline = true
          const wrapper = mount(Link, {
            props: {
              underline
            }
          })
          console.log(wrapper.vm)
          expect(wrapper.classes()).toContain('is-underline')
        })
    
        it('set the href', () => {
          const href = 'https://element3-ui.com/'
          const wrapper = mount(Link, {
            props: {
              href
            }
          })
          expect(wrapper.attributes('href')).toBe(href)
        })
    
        it('set the icon, link displays the corresponding style', () => {
          const icon = 'el-icon-search'
          const wrapper = mount(Link, {
            props: {
              icon
            }
          })
          const i = wrapper.find('i')
          expect(i.exists()).toBe(true)
          expect(i.classes()).toContain(icon)
        })
    
        it('should get target attr value', () => {
          const wrapper = mount(Link, {
            props: {
              target: '_blank'
            }
          })
          expect(wrapper.attributes('target')).toBe('_blank')
        })
      })
    
      describe('click', () => {
        it('should captured click events emitted via click', () => {
          const wrapper = mount(Link)
          wrapper.trigger('click')
          expect(wrapper.emitted('click')).toBeTruthy()
        })
    
        describe("can't captured click event emitted", () => {
          it('when disabled prop is equal to true', () => {
            const wrapper = mount(Link, {
              props: {
                disabled: true
              }
            })
            wrapper.trigger('click')
            expect(wrapper.emitted('click')).toBeFalsy()
          })
    
          it('when href prop is to be truthy', () => {
            const wrapper = mount(Link, {
              props: {
                href: 'https://element3-ui.com/'
              }
            })
            wrapper.trigger('click')
            expect(wrapper.emitted('click')).toBeFalsy()
          })
        })
      })
    })
    

    覆盖率

    【element3-小哈喽参与日记】被摸手后,自己重写Link组件

    总结

    在这次重构组件中,体验到了测试的魅力,在有测试的保障下,我写代码的时候,逻辑更清晰了,如有神助,代码出错也能迅速定位,并解决。今天下单了《重构 改善既有代码的设计(第2版)》,让我们学起来,奥利给


    起源地下载网 » 【element3-小哈喽参与日记】被摸手后,自己重写Link组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元