diff --git a/.changeset/fix-main-import-reference.md b/.changeset/fix-main-import-reference.md new file mode 100644 index 0000000..e25ed9c --- /dev/null +++ b/.changeset/fix-main-import-reference.md @@ -0,0 +1,7 @@ +--- +"vue-pivottable": patch +--- + +Fix main.ts import reference issue and optimize memory usage + +Fixed undefined SimpleApp reference to use App component in main.ts entry point. This resolves runtime errors and improves application startup reliability. \ No newline at end of file diff --git a/src/components/pivottable-ui/VPivottableUi.vue b/src/components/pivottable-ui/VPivottableUi.vue index 5238f81..b2fcec1 100644 --- a/src/components/pivottable-ui/VPivottableUi.vue +++ b/src/components/pivottable-ui/VPivottableUi.vue @@ -137,7 +137,8 @@ import VRendererCell from './VRendererCell.vue' import VAggregatorCell from './VAggregatorCell.vue' import VDragAndDropCell from './VDragAndDropCell.vue' import VPivottable from '../pivottable/VPivottable.vue' -import { computed, watch, shallowRef, watchEffect, onUnmounted } from 'vue' +import TableRenderer from '../pivottable/renderer' +import { computed, watch, toRaw } from 'vue' import { usePropsState, useMaterializeInput, @@ -182,6 +183,7 @@ const props = withDefaults( >(), { aggregators: () => defaultAggregators, + renderers: () => TableRenderer, hiddenAttributes: () => [], hiddenFromAggregators: () => [], pivotModel: undefined, @@ -291,9 +293,10 @@ const { allFilters, materializedInput } = useMaterializeInput( ) const rendererItems = computed(() => - Object.keys(state.renderers).length ? state.renderers : {} + state.renderers && Object.keys(state.renderers).length ? state.renderers : {} ) const aggregatorItems = computed(() => state.aggregators) + const rowAttrs = computed(() => { return state.rows.filter( (e) => @@ -327,38 +330,11 @@ const unusedAttrs = computed(() => { .sort(sortAs(pivotUiState.unusedOrder)) }) -// Use shallowRef instead of computed to prevent creating new PivotData instances on every access -const pivotData = shallowRef(new PivotData(state)) - -// Update pivotData when state changes, and clean up the watcher -const stopWatcher = watchEffect(() => { - // Clean up old PivotData if exists - const oldPivotData = pivotData.value - pivotData.value = new PivotData(state) - - // Clear old data references - if (oldPivotData) { - oldPivotData.tree = {} - oldPivotData.rowKeys = [] - oldPivotData.colKeys = [] - oldPivotData.rowTotals = {} - oldPivotData.colTotals = {} - oldPivotData.filteredData = [] - } -}) - -// Clean up on unmount -onUnmounted(() => { - stopWatcher() - if (pivotData.value) { - pivotData.value.tree = {} - pivotData.value.rowKeys = [] - pivotData.value.colKeys = [] - pivotData.value.rowTotals = {} - pivotData.value.colTotals = {} - pivotData.value.filteredData = [] - } -}) +const pivotData = computed(() => new PivotData({ + ...state, + data: toRaw(state.data), + aggregators: toRaw(state.aggregators) +})) const pivotProps = computed(() => ({ data: state.data, aggregators: state.aggregators, @@ -408,22 +384,19 @@ watch( } as Partial) } }, - { deep: true, immediate: true } + { immediate: true } ) watch( - [allFilters, materializedInput], + materializedInput, () => { - // Only update the changed properties, not the entire state + // Only update data when materializedInput changes updateMultiple({ - allFilters: allFilters.value, - materializedInput: materializedInput.value, - data: materializedInput.value // Ensure data is also updated + data: materializedInput.value }) }, { - immediate: true // Add immediate to ensure initial update - // Removed deep: true - this was causing 80% of memory leak + immediate: true } ) diff --git a/src/composables/index.ts b/src/composables/index.ts index 12f9052..7232b5b 100644 --- a/src/composables/index.ts +++ b/src/composables/index.ts @@ -3,5 +3,4 @@ export { useProvideFilterBox, provideFilterBox } from './useProvideFilterbox' export { useMaterializeInput } from './useMaterializeInput' export { usePropsState } from './usePropsState' export { usePivotUiState } from './usePivotUiState' -export { usePivotData } from './usePivotData' -export { usePivotModelHistory } from './usePivotModelHistory' \ No newline at end of file +export { usePivotData } from './usePivotData' \ No newline at end of file diff --git a/src/composables/useMaterializeInput.ts b/src/composables/useMaterializeInput.ts index 618f4e7..1f61076 100644 --- a/src/composables/useMaterializeInput.ts +++ b/src/composables/useMaterializeInput.ts @@ -1,4 +1,4 @@ -import { Ref, ref, watch } from 'vue' +import { Ref, ref, watch, shallowRef, ShallowRef, onUnmounted } from 'vue' import { PivotData } from '@/helper' export interface UseMaterializeInputOptions { @@ -7,8 +7,8 @@ export interface UseMaterializeInputOptions { export interface UseMaterializeInputReturn { rawData: Ref - allFilters: Ref>> - materializedInput: Ref + allFilters: ShallowRef>> + materializedInput: ShallowRef processData: (data: any) => { AllFilters: Record>; materializedInput: any[] } | void } @@ -17,8 +17,9 @@ export function useMaterializeInput ( options: UseMaterializeInputOptions ): UseMaterializeInputReturn { const rawData = ref(null) - const allFilters = ref>>({}) - const materializedInput = ref([]) + // Use shallowRef to prevent deep reactivity on large objects + const allFilters = shallowRef>>({}) + const materializedInput = shallowRef([]) function processData (data: any) { if (!data || rawData.value === data) return @@ -74,6 +75,12 @@ export function useMaterializeInput ( } ) + onUnmounted(() => { + allFilters.value = {} + materializedInput.value = [] + rawData.value = null + }) + return { rawData, allFilters, diff --git a/src/composables/usePivotModelHistory.ts b/src/composables/usePivotModelHistory.ts deleted file mode 100644 index 7fd9a90..0000000 --- a/src/composables/usePivotModelHistory.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ref, Ref, computed, watch } from 'vue' -import { PivotModelInterface } from '@/types' -import { clonePivotModel } from '@/utils/pivotModel' - -export interface PivotModelHistoryOptions { - maxHistory?: number - autoSave?: boolean -} - -export interface UsePivotModelHistoryReturn { - history: Ref - currentIndex: Ref - canUndo: Ref - canRedo: Ref - pushState: (model: PivotModelInterface) => void - undo: () => PivotModelInterface | null - redo: () => PivotModelInterface | null - clear: () => void - getCurrentState: () => PivotModelInterface | null -} - -/** - * PivotModel의 히스토리를 관리하는 composable - * @param modelRef PivotModel을 담고 있는 ref - * @param options 히스토리 관리 옵션 - */ -export function usePivotModelHistory( - modelRef: Ref, - options: PivotModelHistoryOptions = {} -): UsePivotModelHistoryReturn { - const { maxHistory = 50, autoSave = true } = options - - const history = ref([]) - const currentIndex = ref(-1) - - const canUndo = computed(() => currentIndex.value > 0) - const canRedo = computed(() => currentIndex.value < history.value.length - 1) - - /** - * 새로운 상태를 히스토리에 추가 - */ - const pushState = (model: PivotModelInterface) => { - // 현재 인덱스 이후의 히스토리는 제거 (새로운 브랜치 생성) - if (currentIndex.value < history.value.length - 1) { - history.value = history.value.slice(0, currentIndex.value + 1) - } - - // 새 상태 추가 - history.value.push(clonePivotModel(model)) - - // 최대 히스토리 크기 유지 - if (history.value.length > maxHistory) { - history.value = history.value.slice(-maxHistory) - } - - currentIndex.value = history.value.length - 1 - } - - /** - * 이전 상태로 되돌리기 - */ - const undo = (): PivotModelInterface | null => { - if (!canUndo.value) return null - - currentIndex.value-- - const previousState = history.value[currentIndex.value] - - if (modelRef.value) { - Object.assign(modelRef.value, clonePivotModel(previousState)) - } - - return previousState - } - - /** - * 다음 상태로 다시 실행 - */ - const redo = (): PivotModelInterface | null => { - if (!canRedo.value) return null - - currentIndex.value++ - const nextState = history.value[currentIndex.value] - - if (modelRef.value) { - Object.assign(modelRef.value, clonePivotModel(nextState)) - } - - return nextState - } - - /** - * 히스토리 초기화 - */ - const clear = () => { - history.value = [] - currentIndex.value = -1 - } - - /** - * 현재 상태 가져오기 - */ - const getCurrentState = (): PivotModelInterface | null => { - if (currentIndex.value >= 0 && currentIndex.value < history.value.length) { - return history.value[currentIndex.value] - } - return null - } - - // 자동 저장이 활성화된 경우, 모델 변경 감지 - if (autoSave) { - watch( - modelRef, - (newModel) => { - if (newModel) { - // 현재 히스토리와 다른 경우에만 추가 - const current = getCurrentState() - if (!current || JSON.stringify(current) !== JSON.stringify(newModel)) { - pushState(newModel) - } - } - }, - { deep: true, immediate: true } - ) - } - - return { - history, - currentIndex, - canUndo, - canRedo, - pushState, - undo, - redo, - clear, - getCurrentState - } -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index b6a3398..52668a0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,6 @@ import { createApp } from 'vue' +import App from './App.vue' -// import App from './App.vue' -import SimpleApp from './SimpleApp.vue' -// import VuePivottable from '@/' - -const app = createApp(SimpleApp) - -// app.component('VuePivottableUi', VuePivottableUi) +const app = createApp(App) app.mount('#app')