Hi @space.chang
Thank you for creating your own thread. That seems to be a better idea.
To clarify, let me format the code, as the unformatted version is tough to read.
<script>
import 'handsontable/dist/handsontable.full.css';
import * as Vue from 'vue';
import Handsontable from 'handsontable';
import { HotTable } from '@handsontable/vue3';
function getDebugInfo() {
  let debug = "Handsontable:";
  debug += ` v${Handsontable.version}`;
  debug += ` (${HotTable.version}`;
  debug += " Vue:";
  debug += ` v${Vue.version}`;
  return debug;
}
function generateData() {
  let _data = [];
  let _mergeData = [];
  for (let i = 0; i < 10000; i++) {
    let _i = parseInt(i / 5);
    if (i % 5 == 0) {
      _mergeData.push({ row: i, col: 0, rowspan: 5, colspan: 1 });
      _mergeData.push({ row: i, col: 3, rowspan: 5, colspan: 1 });
    }
    _data.push([
      `A${_i}`,
      `B${i}`,
      `C${i}`,
      `E${i}`,
      `E${i}`,
      `A1${i}`,
      `B1${i}`,
      `C1${i}`,
      `E1${i}`,
      `E1${i}`,
      `A${i}`,
      `B${i}`,
      `C${i}`,
      `E${i}`,
      `E${i}`,
      `A1${i}`,
      `B1${i}`,
      `C1${i}`,
      `E1${i}`,
      `E1${i}`,
    ]);
  }
  return { data: _data, mergeData: _mergeData };
}
console.log(getDebugInfo());
let _ret = generateData();
export default {
  name: "App",
  data: function () {
    return {};
  },
  setup() {
    Vue.onMounted(() => {
      let div = document.getElementById('example');
      console.time('HandsonTable');
      let table = new Handsontable(div, {
        data: _ret.data,
        colWidths: [
          80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80
        ],
        colHeaders: false,
        rowHeaders: true,
        licenseKey: "non-commercial-and-evaluation",
        // mergeCells: _ret.mergeData,
        height: '100%',
        width: '100%',
        // dropdownMenu:['filter_by_value','filter_action_bar'],
        dropdownMenu: {
          items: {
            filter_by_value: {
              // hide the 'Filter by value' list from all columns but the first one
              hidden() {
                return this.getSelectedRangeLast().to.col > 0;
              },
            },
            filter_action_bar: {
              // hide the 'OK' and 'Cancel' buttons from all columns but the first one
              hidden() {
                return this.getSelectedRangeLast().to.col > 0;
              },
            }
          }
        },
      },
      filters: true,
      afterGetColHeader(col, TH) {
        // remove the column menu button from the 'Brand', 'Price', and 'Date' columns
        if (col > 0) {
          const button = TH.querySelector('.changeType');
          if (!button) {
            return;
          }
          button.parentElement.removeChild(button);
        }
      }
    });
    console.timeEnd('HandsonTable');
    table.batch(() => {
      console.time('B');
      table.updateSettings({ mergeCells: _ret.mergeData });
      console.timeEnd('B');
    });
  }
};
</script>
<style>
body {
  height: 100%;
  width: 100%;
  display: block;
}
</style>
Now, it will be easier to analyze what is done.
ps. didnāt you say that the
 _mergeData.push({ row: i, col: 0, rowspan: 5, colspan: 1 });
 _mergeData.push({ row: i, col: 3, rowspan: 5, colspan: 1 });
is already out of the generateData() function?