如何在Vue中更好的复用组件

前言

大多数前端框架都提供了组件化的能力,在日常开发中,我们经常会用组件去封装自己的业务逻辑,那么如何去写一个复用率高的组件就变成了一个重要的技巧。这里总结一下在使用Vue时几种复用组件的方式。

场景

表单验证是一个经常碰到的场景,表单内的不同组件常常需要有相同的验证逻辑,比如验证值非空,这里就按照这个场景来举几个例子。

使用Mixin

Vue提供了 “混入” 这个方案来达到一个复用的效果,所以我们可以写一个表单校验相关的mixin,并且混入到各个组件中去。

// validate-mixin.js
const validateMixin = {
    props: {
        required: {
            type: Boolean,
            default: false
        }
    },
    methods: {
        validate(value) {
            if (this.required && (value === undefined || value === null || value === '')) {
                this.isError = true;
            } else {
                this.isError = false;
            }
        }
    }
};
export default validateMixin

然后我这边有两个组件,分别是文本输入框和数字输入框

// input.js
<template>
    <div id="ka-input">
        <input type="text" @input="onInput">
        <p v-show="isError" class="err-msg"> {{errMsg}} </p>
    </div>
</template>

<script>
import validateMixin from '@/common/mixin/validate-mixin.js'

export default {
    name: 'KaInput',
    mixins: [validateMixin],
    methods: {
        onInput(e) {
            this.validate(e.target.value);
        }
    },
    data() {
        return {
            isError: false,
            errMsg: '不能为空'
        };
    }
};
</script>

// number-input.js
<template>
    <div id="ka-number-input">
        <input type="number" @input="onInput">
        <p v-show="isError" class="err-msg"> {{errMsg}} </p>
    </div>
</template>

<script>
import validateMixin from '@/common/mixin/validate-mixin.js'

export default {
    name: 'KaNumberInput',
    mixins: [validateMixin],
    methods: {
        onInput(e) {
            this.validate(e.target.value);
        }
    },
    data() {
        return {
            isError: false,
            errMsg: '不能为空'
        };
    }
};
</script>


混入的优点在于使用简单,将共同的属性或者方法封装在这个mixin对象之中,其他需要共享该属性或者方法的组件只要引入这个mixin即可。
但是从上述代码中可以看出,混入有几个明显的缺点。

  1. 模板的内容需要维护在各个组件当中,得不到复用。
  2. 属性和方法放在mixin中,后期不方便他人维护,需要手动梳理源组件和混入对象之间的逻辑。
  3. mixins会导致属性冲突,并且相同名称的生命周期会被合并,这可能带来冗余的逻辑,同时也可以看出mixin的侵入性较强。

实际上在开发过程中,如何组织mixin的目录结构也是个问题,要区分是一个页面级别的mixin,还是部分页面的mixin,甚至是全站的mixin,划分不清楚会导致后期mixin的职能混乱,不利于代码维护。

RenderLess

renderLess 本意就是不渲染内容的组件,在Vue 中我们可以使用插槽来实现,和React中的HOC有异曲同工之妙。

// validate-render-less.vue
<template>
    <div class="validate-item">
        <slot name="wrapped" :validate="validate"></slot>
        <p v-show="isError" class="err-msg">{{ errMsg }}</p>
    </div>
</template>
<script>
export default {
    data: () => {
        return {
            isError: false,
            errMsg: 'can not be null'
        };
    },
    props: {
        required: {
            type: Boolean,
            required: false
        }
    },
    methods: {
        validate(e) {
            const value = e.target.value;
            if (
                this.required &&
                (value === undefined || value === null || value === '')
            ) {
                this.isError = true;
            } else {
                this.isError = false;
            }
        }
    }
};
</script>

上面这个模块本质上是一个vue组件,他有模板,有逻辑,并且利用插槽在模板中预留了一个“占位符”,其他组件可以往这里添加自己的内容。

// input.js
<template>
    <div id="ka-input">
        <r-l-validate :required="required">
            <template v-slot:wrapped="{ validate }">
                <input type="text" @input="validate($event)" />
            </template>
        </r-l-validate>
    </div>
</template>

<script>
import validate from '@/common/render-less/validate.vue';

const kaInput = {
    props: {
        required: {
            type: Boolean
        }
    },
    name: 'ka-input',
    components: {
        'r-l-validate': validate
    }
};

export default kaInput;
</script>

// number-input.js
<template>
    <div id="ka-number-input">
        <r-l-validate :required="required">
            <template v-slot:wrapped="{ validate }">
                <input type="number" @input="validate($event)">
            </template>
        </r-l-validate>
    </div>
</template>

<script>
import validate from '@/common/render-less/validate.vue'

export default {
    name: 'KaNumberInput',
    props: {
        required: {
            type: Boolean,
            required: false
        }
    },
    components: {
        'r-l-validate': validate
    }
};
</script>

使用render-less组件有这么几个优点。

  1. 可以复用模板的逻辑,公共的部分都可以提取到renderless中
  2. 不会出现命名冲突

简单总结

对于简单的场景,mixin可以解决大部分问题,但是要注意他的一些缺点;其他的场景下,如果需要复用模板,并且组件的逻辑较为复杂,那应该优先考虑使用render-less组件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章