Appearance
前言
vue 中组件复用的方式有 mixin,extend,高阶组件和组合组件三种方式
基础
如何创建一个全局单例组件
const instance = new Vue(options), 通过这种方式能够实现部分命令方式调用组件。
mixin 和 extend 的区别
mixin 是对选项的混合,发生在生成 vue 实例之前,extend 是对 vue 实现继承,返回的是一个 vue 实例, 类似于 Object.create()
HOC 实现一个 promise 组件
代码中经常有请求一个 api,然后根据 api 显示不同逻辑的需求
vue
<template>
<div>
<div v-if="loading">loading</div>
<div v-if="!loading">
{{ content }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
content: "",
loading: true,
};
},
methods: {
getContent() {
this.loading = true;
this.$api.get("/some").then((content) => {
this.loading = false;
this.content = content;
});
},
},
};
</script>通过 HOC 将该组件的请求逻辑提取出来
js
export const withPromise = (component, promiseFn) => {
return {
data() {
return {
loading: true,
result: null,
error: null,
};
},
mounted() {
this.loading = true;
promiseFn()
.then((data) => {
this.result = data;
})
.catch((error) => {
this.error = error;
});
},
render(h) {
const loading = () => h();
const error = () => h();
const content = () =>
h(component, {
loading: this.loading,
content: this.content,
});
return h("div", null, [
this.loading ? loading() : null,
this.error ? error() : null,
!this.loading && !this.error ? content() : null,
]);
},
};
};
const newComponent = withPromise(view, getContent);上面实现还有几个问题没有解决
- 请求函数的参数还没有,不能通过子组件自定义
- 子组件参数如果有变化,触发新的查询
- 外部组件对于子组件的 prop 等参数没有传递到子组件,包括插槽等
第一个文件有两种方式拿到子组件的请求参数,第一个是静态的,通过在子组件选项或者定义上面绑定一个特殊的键值实现,或者将值绑定到子组件的实例上,通过 this.$refs 拿到子组件的实例后获取。
第二个问题需要在子组件上动态的添加 watch 函数,watch 函数监听子组件的查询参数并且触发的回调是父组件的请求函数
第三个问题,直接将 hoc 组件上的$attrs 和 $listeners 绑定到子组件的 props 和 $listeners 中,其中有个 v-bind 绑定的点,v-bind 总是会属性绑定到 props 中,对是否是 props 的处理,都是子组件的关心的,而不是父组件关心的。
js
export const withPromise = (component, promiseFn) => {
return {
data() {
return {
loading: false,
content: null,
};
},
render(h) {
return h(component, {
props: {
...this.attrs,
loading: this.loading,
content: this.content,
},
on: this.$listeners,
ref: "component", // 用于在mounted时获取实例
scopedSlot: this.$scopedSlot
}, this.$children); // 注意 $children 和 $slot的区别
},
methods: {
getContent() {
this.loading = true;
promiseFn(this.$refs.component.requestParams)
.then((content) => (this.content = content))
.finally(() => (this.loading = false));
},
},
mounted() {
this.getContent();
this.$refs.component.$watch('requestParams', this.getContent())
},
};
};
const newComponent = withPromise(view, getContent)
// <newComponent onClick="xxx">xxxx</newComponent>其实还有个问题,发现没有,就是还是没法将ref进行传递。 要想获取到子组件需要一直ref来使用。
新增props处理
上面代码中对于props处理在每个HOC组件中都差不多类似。简单一点通过
js
const normalProps = (vm) => {
return {
attrs: this.vm.$attrs,
on: this.vm.$listeners,
slotScopeds: this.vm.$slotScopeds
}
}
h(compoennt, {...normalProps(this), props: {}}, this.$children)组合优于HOC
组合的意识是通过compose的方式将原有的HOC fn3(fn2(fn1))的方式变成compose(fn1, fn2, fn3),将组件作为传递。如果withPromise没有参数,都不需要包装 compose(() => withPromise(), withLog)
