如何在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組件

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