用vue的抽象组件来做一个防止img标签url为空或url地址出错的验证

看了网上文章学习了下vue的抽象组件,感觉就跟react的高阶组件一样的使用场景,只是更加面向vue的底层编程
,网上介绍的抽象组件一般有2种用法,1 用来加防抖和节流 2 用来控制按钮是否允许点击做权限效验,这2个对于
目前我所做的一些项目用处不大,这里先介绍下我用抽象组件的使用场景,总不能说学习了抽象组件不管是否适用就
强行加到项目里吧。。。

1. 基本上公司项目所有用到动态地址的地方都要做2个效验,1 判断这个从后台返回的url是否为空,相信大家
     经常写这种代码 :src="imgUrl|| defUrl", 2 如果这个地址存在,那么还要做验证,防止地址报错,这个
     代码大家也经常写吧  @error="fixUrl"。 如果每个用动态img的地方都这么写,一个项目下来我都要吐了。

2. 就算你愿意这么写,还是有一个问题,在 @error="fixUrl"里,一般的处理方式是在方法里把imgUrl替换成
  defUrl,这样就会有另一个问题,显示的图片是出来了,但是如果你的imgUrl不仅用来展示要在其他地方用,你总不能
  拿替换过的imgUrl用在其他地方吧,针对这种情况,大部分人(之前的我)的处理情况就是要2套数据放到data里,一套用来
  展示,一套用来内部传值使用,很不优雅对吧。

3. 我也想过,做一个组件,然后所有需要验证的地方都不用img都用这个img组件,但是img的样式,和绑定的方法,这些你都要
  从外边取,然后绑定到真实img上,我只想处理个图片验证啊,怎么还有这么多需要兼容的地方要关注,好麻烦啊。

所以遇到了抽象组件,稍微思考了一下,觉得可以完美解决以上3个问题。真香,直接看抽象组件的代码吧。


<script>
export default {
    name: "imgFix",
    abstract: true, //标记为抽象组件
    props: {
        // 要替换的图片,如果是服务器下的写url地址,public目录下的写地址记得加process.env.BASE_URL,原因请看
        // https://www.cnblogs.com/wzcsqaws/p/11283228.html
        // assets 目录下,记得用require包裹一下
        replaceImg: {
            type: String
        }
    },
    created() {
        // 为了解决如果replaceImg 也报错就会重复死循环调用error方法
        this.replaceObj = {
            originalSrc: "",
            isReplace: false
        };
    },
    render() {
        let replaceObj = this.replaceObj;  // 闭包在错误函数里锁住这个对象
        let replaceImg = this.replaceImg; // 要替换的图片
        let vnode = this.$slots.default[0]; // 子组件的vnode
        let that = vnode.context; // 子组件的vm也就是this
        let on = vnode.data.on || (vnode.data.on = {}); // 获取img标签的vnode上的绑定事件,没有绑定会为空
        let error = on.error; // 获取报错事件
        let originalSrc = vnode.data.attrs.src;
        if (!originalSrc) {
            // 如果动态获取的url为空,这里之间替换为replaceImg
            console.log("url is null");
            vnode.data.attrs.src = replaceImg;
        }

        // 写一个error事件防止报错,图片没有替换
        function errorFix(e) {
            // 已经替换过了,还报错,说明替换的url也有问题,就renturn,避免再次赋值死循环
            if (
                replaceObj.isReplace &&
                replaceObj.originalSrc === originalSrc
            ) {
                console.log("replaceImg is also error");
                return;
            }
            console.log(e, "url is error");
            // 注意这里是之间改变dom上的src没有修改vue里的
            // 值。就可以解决我上面说的第二个问题
            vnode.elm.src = replaceImg;
            replaceObj.isReplace = true;
            replaceObj.originalSrc = originalSrc;
            if (error) {
                // 做个兼容,如果原文件也绑定了error事件,在替换图片后也会触发,并且手动绑定上下文和event
                // 如果原error事件也改变了url,那么会以原文件的url来显示
                error.call(that, e);
            }
        }
        on.error = errorFix;
        return vnode;
    }
};
</script>

功能上还有一个细节没有处理到位,就是我在抽象组件里改了url,原文件的error事件也改了url,那么如果抽象
组件的url加载快会先显示出来,等error事件的url加载好了再替换,这样子就会闪一下,体验不好,目前的解决
办法是使用抽象组件的开发者通过规范不这么做,但是从代码角度解决最好.

原文地址:https://www.cnblogs.com/wzcsqaws/p/12988285.html