Open 02020 opened 3 years ago
function renderSlot(name, fallback, props, bindObject) {
var scopedSlotFn = this.$scopedSlots[name];
var nodes;
if (scopedSlotFn) {
// scoped slot
props = props || {};
if (bindObject) {
if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
warn('slot v-bind without argument expects an Object', this);
}
props = extend(extend({}, bindObject), props);
}
nodes = scopedSlotFn(props) || fallback;
} else {
nodes = this.$slots[name] || fallback;
}
var target = props && props.slot;
if (target) {
return this.$createElement('template', { slot: target }, nodes);
} else {
return nodes;
}
}
function renderSlot(name) {
var scopedSlotFn = this.$scopedSlots[name];
return scopedSlotFn ? scopedSlotFn({}) : this.$slots[name];
}
export { renderSlot };
export default {
name: 'e-btns',
props: {
value: {}, // model
config: {
type: Array,
required: true,
},
size: String,
shape: String,
vertical: Boolean,
},
methods: {
onCommand(...args) {
this.$emit('cmd', ...args);
},
},
render(h) {
const child = ({ label, key, name, type, icon, disabled, cmd }) =>
h(
'Button',
{
props: { type, icon, disabled },
on: {
click: cmd || (() => this.onCommand(key || name)),
},
},
label
);
return h(
'ButtonGroup',
{
props: {
size: this.size,
shape: this.shape,
vertical: this.vertical,
},
},
this.config.map((x) => child(x))
);
},
};
配置
const config = [
{ key: '1', type: 'primary', icon: 'md-cloud-upload', disabled: true },
{ key: '2', type: 'error', icon: 'ios-create', disabled: false },
{ key: '3', type: 'warning', icon: 'md-cloud-download' },
];
使用
export const renderTest = {
name: 'renderTest',
components: { btns },
props: {
msg: String,
},
data() {
return { config };
},
methods: {
onCMD(...args) {
console.log(this.msg, ...args);
},
},
render(h) {
return h('btns', {
props: {
config: this.config,
},
on: {
cmd: this.onCMD,
},
});
},
};
import { createRenderFnc } from 'vue-hoc';
// 定义 组件A的渲染传参
const renderOptions = {
props: {
config() {
// this = 父组件组件
console.log('config 被过滤了');
return this.config.filter((x) => x.disabled);
},
},
listeners: {
cmd(arg) {
console.log('cmd', arg);
this.$emit('cmd', arg);
},
},
scopedSlots: {
default: (me) => {
console.log('入参', me);
console.log('此上下文: 空', this);
return '我是默认';
},
named_slot: function (me) {
console.log('此上下文: 父组件组件(调用处)', this);
return 'named_slot';
},
other_slot: (me) => 'sss',
},
};
// 定义组件A
const Component = {
props: {
config: {
type: Array,
},
},
render(h) {
const slots = this.$scopedSlots;
return h('div', [
...this.config.map((x) => h('div', [JSON.stringify(x)])),
h('div', ['----以下是子组件----']),
slots['default'](this),
h(
'div',
{
staticClass: 'named',
on: {
click: () => this.$emit('cmd', '组件named点击了'),
},
},
[slots['named_slot'](this)]
),
h('div', { staticClass: 'other' }, [slots['other_slot'](this)]),
]);
},
};
// 在组件A的基础上构建组件B 即 .vue 文件
const enhanced = {
props: {
config: {
type: Array,
required: true,
},
},
name: 'HOC',
render: createRenderFnc(renderOptions)(Component),
};
export default enhanced;
针对 iviewui
export default {
name: 'collapse-builder',
props: {
value: {}, // model
config: {
type: Array,
required: true,
},
accordion: Boolean,
simple: Boolean,
},
methods: {
onCommand(...args) {
this.$emit('cmd', ...args);
},
__input(v) {
this.$emit('input', v);
},
},
render(h) {
this.config.forEach((group, index) => {
group.name = group.name || `panel_${index}`;
});
const panel = ({ label, name, content }) =>
h(
'Panel',
{
props: { name },
scopedSlots: {
content: () => this.$scopedSlots.default(content),
},
},
label // 增加按钮及图标
);
return h(
'Collapse',
{
on: {
'on-change': this.onCommand,
},
model: {
value: this.value,
callback: this.__input,
expression: 'value',
},
props: {
accordion: this.accordion,
simple: this.simple,
},
},
this.config.map(panel)
);
},
};
const data = config.map((x) => {
return {
label: x.label,
name: x.value,
content: x.children.map((x) => {
const { label, value } = x;
return { label, value };
}),
};
});
<builder :config="config" @cmd="onCommand" v-model="value">
<template v-slot="data">
<template v-for="(item, index) in data">
<Button @click="onClick(index)" :key="index" type="text">{{
item.label
}}</Button>
</template>
</template>
</builder>
<div id="app">
<hoc msg="msg" @change="onChange">
<template>
<div>I am slot</div>
</template>
<template v-slot:named>
<div>I am named slot</div>
</template>
</hoc>
</div>
new Vue({
el: '#app',
components: { hoc },
methods: { onChange() {} },
});
var view = {
props: ['result'],
data() {
return {
requestParams: {
name: 'ssh',
},
};
},
methods: {
reload() {
this.requestParams = {
name: 'changed!!',
};
},
},
template: `
<span>
<span>{{result?.name}}</span>
<slot></slot>
<slot name="named"></slot>
<button @click="reload">重新加载数据</button>
</span>
`,
};
const withPromise = (promiseFn) => (wrapped) => {
return {
data() {
return {
loading: false,
error: false,
result: null,
};
},
methods: {
async request() {
this.loading = true;
// 从子组件实例里拿到数据
const { requestParams } = this.$refs.wrapped;
// 传递给请求函数
const result = await promiseFn(requestParams).finally(() => {
this.loading = false;
});
this.result = result;
},
},
async mounted() {
// 立刻发送请求,并且监听参数变化重新请求
this.$refs.wrapped.$watch('requestParams', this.request.bind(this), {
immediate: true,
});
},
render(h) {
const args = {
props: {
// 混入 $attrs
...this.$attrs,
result: this.result,
loading: this.loading,
},
// 传递事件
on: this.$listeners,
// 传递 $scopedSlots
scopedSlots: this.$scopedSlots,
ref: 'wrapped',
};
const wrapper = h('div', [
this.loading ? h('span', ['加载中……']) : null,
this.error ? h('span', ['加载错误']) : null,
h(wrapped, args),
]);
return wrapper;
},
};
};
const withLog = (wrapped) => {
return {
mounted() {
console.log('I am mounted!');
},
render(h) {
return h(wrapped, normalizeProps(this));
},
};
};
const request = (data) => {
return new Promise((r) => {
setTimeout(() => {
r(data);
}, 1000);
});
};
const composed = compose(withLog, withPromise(request));
var hoc = composed(view);
// 通用函数
function compose(...funcs) {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
function normalizeProps(vm) {
return {
on: vm.$listeners,
attr: vm.$attrs,
// 传递 $scopedSlots
scopedSlots: vm.$scopedSlots,
};
}
vue-hoc hoc-promise-compose