02020 / vite-kit

0 stars 0 forks source link

# Render #5

Open 02020 opened 3 years ago

02020 commented 3 years ago

使用 Render 构建的组件

1. 组件-按钮组

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))
    );
  },
};

应用demo

// 配置-数据结构
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      },
    });
  },
};

2. 组件 - 折叠面板1.0


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((x) => panel(x))
    );
  },
};

应用demo

<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>

3. 组件-折叠面板2.0

// 向渲染的组件传递作用域插槽
const slotsC = (slots, scopedSlots) => {
  return Object.keys(slots).reduce((acc, key) => {
    acc[key] = (props) => {
      if (Array.isArray(slots[key])) {
        return slots[key].map(scopedSlots[key]);
      } else {
        return scopedSlots[key](slots[key]);
      }
    };
    return acc;
  }, {});
};

// 组件
const collapseBuilder = {
  name: 'collapse-builder-2.0',
  props: { config: { type: Array}  },
  render(h) {
    const scopedSlots = this.$scopedSlots;
    const panel = ({ label, name, slots }) => {
      return h('Panel',{
          props: { name },
          // 向渲染的组件(Panel)传递作用域插槽
          scopedSlots: slotsC(slots, scopedSlots),
        },
        label
      );
    };
    const child = this.config.map((x) => panel(x));
    return h('Collapse', child);
  },
};

// 应用
export default {
  components: { collapseBuilder },
  data() {
    return {
      config: [{
          label: '我是标题',
          name: 'name1',
          // 需要向子组件传递的插槽
          slots: {
            content: [{ name: 'dd1', value: 'dd1', label: '自定dom' },
              { name: 'dd2', value: 'dd2', label: '自定dom' },
            ],
          },
        },
      ],
    };
  },
  methods: {
    onClick(name) {      console.log('onClick', name);    },
  },
};

应用demo


<template>
  <collapse-builder :config="config">
    <template v-slot:content="item">
      <Button @click="onClick(item.name)" :key="item.value" type="text">
        {{ item.label }}
      </Button>
    </template>
  </collapse-builder>
</template>
02020 commented 3 years ago

h 的使用范围, 当前组件的上下文

构建思路

// 渲染-01
const render_01 = {
    Icon: (h, data, slots = []) => h('Icon', data, slots),
    Button: (h, data, slots = []) => h('Button', data, slots),
};
// 渲染-02
const render_02 = function (h) {
  return {
    Icon: (data, slots = []) => h('Icon', data, slots),
    Button: (data, slots = []) => h('Button', data, slots),
  };
};
// 渲染-03 遍历
const render_03 = function (h) {
   return ['Icon', 'Button'].reduce((acc, key) => {
    acc[key] = (data, slots = []) => h(key, data, slots);
    return acc;
  }, {});
};
// 使用方式
const r = renderFn(h)
r.Button({props: {type: 'primary'}},["ddd"])

渲染按钮、图标

const render = {
  Icon: (cmd, ...args) => (h, key, props) => {
    return h('Icon', {
      props: props,
      on: {
        click: (e) => {
          e.stopPropagation();
          cmd(key, args);
        },
      },
    });
  },
  Button: (cmd, ...args) => (h, key, props) => {
    return h('Button', {
      props: props,
      on: {
        click: (e) => {
          e.stopPropagation();
          cmd(key, args);
        },
      },
    });
  },
};

渲染目录树的内容-01

// 渲染目录树的内容
const renderContent = ({ cmd, btns }) => (h, { root, node, data }) => {
  if (node.parent === undefined) {
    return h(
      'span',
      {
        style: {
          display: 'inline-block',
          width: '100%',
        },
      },
      data.title
    );
  }

  const Icon = renderC.Icon(cmd, root, node, data);

  return h(
    'span',
    {
      style: {
        display: 'inline-block',
        width: '100%',
      },
    },
    [
      h('span', [
        h('Icon', {
          props: {
            type: 'ios-podium',
          },
          style: {
            marginRight: '8px',
          },
        }),
        h('span', data.title),
      ]),
      h(
        'span',
        {
          style: {
            display: 'inline-block',
            float: 'right',
            marginRight: '2px',
          },
        },

        btns.map((x) => Icon(h, x.key, x))
      ),
    ]
  );
};
02020 commented 3 years ago

div-for 渲染优化思路 已经废弃

[采用 grade-build 组件渲染]()

初始版本

<template>
  <div style="width: 100%" class="border border-blue-500">
    <div :key="ri" v-for="(row, ri) in _list" class="grid grid-cols-6">
      <div
        :key="ci"
        v-for="(col, ci) in row"
        :class="[col.class]"
        @click="onClick(ri, ci)"
        class="text-sm font-semibold text-center py-2 m-px"
      >
        {{ col.title }}
      </div>
    </div>
  </div>
</template>

<script>
import { buildStyleCell } from './utils';

export default {
  name: 'v-table',
  props: {
    list: {
      type: Array,
    },
    pairs: {
      type: Array,
    },
  },
  computed: {
    _list() {
      return buildStyleCell(this.list, this.pairs);
    },
  },
  methods: {
    onClick(r, c) {
      console.log(this.list[r][c]);
    },
  },
  mounted() {},
};
</script>

改版-01

import { renderFn, buildStyleCell } from './utils';

export default {
  name: 'v-table',
  props: {
    list: Array,
    pairs: Array,
  },
  render(h) {
    const div = renderFn(h).div;
    const vm = this;
    // 渲染单元格
    const renderCol = (ri) => (col, ci) =>
      div(col.title, {
        key: ci,
        staticClass: 'text-sm font-semibold text-center py-2 m-px',
        class: [col.class],
        on: {
          click: function () {
            return vm.onClick(ri, ci);
          },
        },
      });
    // 渲染行
    const renderRow = (row, ri) => {
      const col = renderCol(ri);
      return div(row.map(col), {
        key: ri,
        staticClass: 'grid grid-cols-6',
      });
    };

    // 最终结果
    const render = (list) =>
      div(list.map(renderRow), {
        staticClass: 'border border-blue-500',
        staticStyle: {
          width: '100%',
        },
      });

    const list = buildStyleCell(this.list, this.pairs);

    return render(list);
  },
  methods: {
    onClick(r, c) {
      // 单元格 行 所有数据
      this.$emit('cmd', this.list[r][c], r, this.list);
    },
  },
};