探索Vue的组件世界-组件复用
目录
Mixin【混入】
缺陷
HOC(higher order component)【高阶组件】
相比较Mixin的优点:
不足:
Renderless组件【函数式组件,无渲染组件,Vue社区使用比较多的一种业务复用模式】
优点:
- Mixins:混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
- HOC(higher order component)【高阶组件】:高阶组件(HOC)是React中用于复用组件逻辑的一种高级技巧。HOC自身并不是React API的一部分,它是一种基于React的组合特性而形成的设计模式。高阶组件听起来挺唬人的,只看名字恐怕不是那么容易明白究竟是何物,而且通常来讲高阶组件并不是组件,而是接受组件作为参数,并且返回组件的函数。
- Renderless组件:函数式组件,无渲染组件
Mixin【混入】
<template><div><input type="text" @blur="blur" />{{ errmsg }}</div>
</template>
<script>
import validateMixin from "./mixin";
export default {mixins: [validateMixin],// 之影响这一个页面,单个页面混入data: () => ({ errmsg: "" }),methods: {blur() {this.validate(); } }
}
</scrpit>
validateMixin
export default {methods: {validate() {/*** 校验逻辑*/return true; } }
}
- 同名钩子函数将合并为一个数组,混入对象的钩子将在组件自身钩子之前调用。
- 二者的methods、components和directives【自定义指令】,将被合并为同一个对象。若对象键名冲突时,取组件对象的键值对(mixin里面的同名【键名】会丢失)
缺陷
- 打破了原有组件的封装
- 增加组件复杂度
- 可能会出现命名冲突的问题
- 仅仅只是对逻辑的复用,模板不能复用(假如什么示例中errmsg由minxin中的validate返回,那就需要在每个用到的页面写模板)
HOC(higher order component)【高阶组件】
高阶函数的应用,装饰者模式的一种实现
HOC:函数接收一个组件作为参数,并返回一个新组件;可复用的逻辑在函数中实现
App.vue文件
<template><div id="app"><img width="25%" src="./assets/logo.png"><validate-input :rules="rules"/></div>
</template>
<script>
import CustomInput from "./components/CustomInput";
import ValidateHoc from "./components/hoc.js";
const ValidateInput = ValidateHoc(CustomInput);
export default {name: "App",data() {return {rules: [{test: function(value) {return /\d+/.test(value);},message: "请输入一个数字"}]};},components: {ValidateInput}
};
</script>
<style>
#app {font-family: "Avenir", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>
CustomInput.vue文件
<template><input type="text" @blur="$emit('blur', value)" v-model="value">
</template>
<script>
export default {props: ["initValue"],data() {return {value: this.initValue};}
};
</script>
hoc.js文件
const ValidateHoc = Component => ({name: `hoc-${Component.name}`,props: ["rules"],data() {return {errMsg: "",value: ""};},methods: {validate(value) {this.value = value;let validate = this.rules.reduce((pre, cur) => {let check = cur && cur.test && cur.test(this.value);this.errMsg = check ? "" : cur.message;return pre && check;}, true);return validate;}},render() {console.log(this.value);return (<div><Component on-blur={this.validate} initValue={this.value} />{this.errMsg || ""}</div>);}
});
export default ValidateHoc;
相比较Mixin的优点:
- 模板可复用
- 不会出现命名冲突(本质上是HOC是套了一层父组件)
不足:
- 组件复杂度高,多层嵌套,调试会很痛苦
Renderless组件【函数式组件,无渲染组件,Vue社区使用比较多的一种业务复用模式】
- 复用的逻辑沉淀在包含slot插槽的组件
- 接口由插槽Prop来暴露
左面子组件,右面是复用逻辑
SBody.vue文件【子组件】
<template><div>// 暴露出插槽Prop方法【validate】<s-validate #default="{ validate }" :value="value" :rules="rules">// 调用暴露出插槽Prop方法【validate】<input type="text" @blur="validate" v-model="value" /></s-validate><s-validate #default="{ validate }" :value="text" :rules="textRules"><textarea type="text" @blur="validate" v-model="text" /></s-validate></div>
</template>
<script>
import SValidate from "./SValidate";
export default {data: () => ({value: "hi",text: "hi",rules: [{test: function(value) {return /\d+/.test(value);},message: "请输入一个数字"}],textRules: [{test: function(value) {return value;},message: "请输入一个非空的值"}]}),components: {SValidate}
};
</script>
SValidate.vue【复用逻辑】
<template><div><slot :validate="validate"></slot>{{ errMsg }}</div>
</template>
<script>
export default {props: ["value", "rules"],data() {return { errMsg: "" };},methods: {validate() {let validate = this.rules.reduce((pre, cur) => {let check = cur && cur.test && cur.test(this.value);this.errMsg = check ? "" : cur.message;return pre && check;}, true);return validate;}}
};
</script>
优点:
- 模板可复用
- 不会出现命名冲突
- 符合依赖倒置原则
- 复用的接口来源清晰