4***@qq.com
4***@qq.com
  • 发布:2025-08-21 09:52
  • 更新:2025-08-21 10:52
  • 阅读:36

Maximum recursive updates exceeded in component

分类:uni小程序sdk

错误信息:Maximum recursive updates exceeded in component <index>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.

vue3 小程序错误,在h5模式下是运行正常的


import { ref, reactive, shallowReactive, computed, toRefs } from 'vue'  
/**  
 * 表单重置  
 * @param {*} form  
 * @returns  
 */  
export const useForm = (form = {}) => {  
  const originalModel = ref(form)  
  const state = reactive(form)  

  /**  
   *  重置表单  
   * @param {*} filter  需要忽略的字段  
   */  
  const resetFields = (filter = []) => {  
    const keys = Object.keys(form)  
    keys.forEach((key) => {  
      if (!filter.includes(key)) state[key] = originalModel[key]  
    })  
  }  

  return {  
    initParam:state,  
    resetFields  
  }  
}  

<script setup name="entrust">  
const { initParam, resetFields } = useForm({  
  title: undefined,  
  dateStr: undefined  
})  

<script />  
<template>  
 <input ref="refSearch" placeholder="输入关键字" v-model="initParam.title"  />  
<template/>  
2025-08-21 09:52 负责人:无 分享
已邀请:
DCloud_UNI_JBB

DCloud_UNI_JBB

您好,麻烦发个完整的复现demo

  • 4***@qq.com (作者)

    好的,下面提供了代码。

    2025-08-21 10:35

4***@qq.com

4***@qq.com (作者) - web

Search 组件

<!-- <script setup >  
import debounce from '@/uni_modules/uv-ui-tools/libs/function/debounce.js'  
import { ref, computed, getCurrentInstance } from 'vue'  
import EtDialog from '@/components/dialog'  
const $emit = defineEmits(['input', 'clear', 'change', 'blur', 'focus', 'update:modelValue'])  
const props = defineProps({  
  modelValue: [Number, String],  
  placeholder: String,  
  /* 百分比高度 */  
  height: {  
    type: Number,  
    default: 90  
  }  
})  
const { ctx } = getCurrentInstance()  
const dialog = ref()  

const value = ref(props.modelValue)  

const input = (value) => {  
  $emit('input', value)  
  $emit('update:modelValue', value)  
}  

const clear = () => {  
  value.value = ''  
  $emit('input', '')  
  $emit('update:modelValue', '')  
  $emit('clear')  
}  

const blur = (value) => {  
  $emit('blur', value)  
}  

const focus = (e) => {  
  $emit('focus', e)  
}  

const change = (value) => {  
  $emit('change', value)  
}  

const handleFilter = () => {  
  dialog.value.open('bottom')  
}  

const close = () => {  
  dialog.value.close()  
}  

defineExpose({  
  close  
})  
</script> -->  

<script>  
import EtDialog from '@/components/dialog'  
export default {  
  name: 'Search',  
  components: { EtDialog },  
  props: {  
    modelValue: [Number, String],  
    placeholder: String,  
    /* 百分比高度 */  
    height: {  
      type: Number,  
      default: 90  
    }  
  },  
  data() {  
    return {  
      innerValue: ''  
    }  
  },  
  created() {  
    // #ifdef VUE3  
    this.innerValue = this.modelValue  
    // #endif  
  },  
  watch: {  
    modelValue(newVal) {  
      this.innerValue = newVal  

    }  
  },  
  methods: {  
    onInput(value) {  
      this.innerValue = value  
      this.$nextTick(() => {  
        this.valueChange()  
      })  
    },  
    onClear() {  
      this.innerValue = ""  
      this.$nextTick(() => {  
        this.$emit("clear")  
        this.valueChange()  
      })  
    },  
    onBlur(value) {  
      this.$emit("blur", value)  
    },  
    onFocus() {  
      this.$emit("focus")  
    },  
    onFilter() {  
      this.$refs.dialog.open('bottom')  
    },  
    valueChange() {  
      const value = this.innerValue  
      this.$nextTick(() => {  
        this.$emit("input", value)  
        this.$emit("update:modelValue", value)  
        this.$emit("change", value)  
      })  
    }  
  }  
}  
</script>  

<template>  
  <view class="filter-search">  
    <uni-easyinput prefixIcon="search" v-model="innerValue" :placeholder="placeholder" @input="onInput" @clear="onClear" @blur="onBlur" @focus="onFocus"></uni-easyinput>  
    <view class="uv-icon_box">  
      <uv-icon custom-prefix="et-icon" name="filtration" size="22" @click="onFilter"></uv-icon>  
    </view>  
  </view>  
  <et-dialog ref="dialog" title="筛选">  
    <slot />  
  </et-dialog>  
</template>  
<style scoped lang="scss">  
.filter-search {  
  display: flex;  
  align-items: center;  
  padding-top: 3px;  

  .uv-icon_box {  
    margin-left: 8rpx;  
  }  

  .uni-popup {  
  }  
}  
</style>

使用组件

在Search 组件中不能嵌套 slot, 会导致出现Maximum recursive updates exceeded in component


<script>  
import { defineComponent, ref, reactive } from 'vue'  
import { onReady } from '@dcloudio/uni-app'  
import Search from '@/components/Search'  
export default defineComponent({  
  components: [Search],  
  setup() {  
    const title = ref('Hello')  
    const initParam = reactive({  
      title: undefined,  
      dateStr: undefined  
    })  

    const tableData = []  

    onReady(() => {  
      console.log('onReady')  
    })  

    function handleResetForm() { }  
    function handleQuery() { }  
    function handleInput() { }  
    function handleRow() { }  
    return {  
      title,  
      tableData,  
      initParam,  
      handleResetForm,  
      handleQuery,  
      handleInput,  
      handleRow  
    }  
  }  
})  
</script>  

<template>  
    <view class="page-search">  
      <view class hover-class="none" hover-stop-propagation="false">{{initParam.title}}</view>  

      <Search ref="refSearch" placeholder="输入关键字" v-model="initParam.title" @input="handleInput">  
        <view class="page-table-form">  
          <uni-forms labelPosition="top" :modelValue="initParam" ref="refForm" labelWidth="100%" :rules="{}">  
            <uni-forms-item label="日期" prop="dateStr">  
              <uni-datetime-picker type="date" v-model="initParam.dateStr" placeholder="请选择" :border="false" />  
            </uni-forms-item>  
          </uni-forms>  

          <view class="et-dialog__footer">  
            <uv-button text="重置" @click="handleResetForm"></uv-button>  
            <uv-button type="primary" text="确定" @click="handleFormConfirm"></uv-button>  
          </view>  
        </view>  
      </Search>  
    </view>  

</template>  
4***@qq.com

4***@qq.com (作者) - web

dialog 组件


<script setup name="EtDialog">  
import { ref, computed, getCurrentInstance, nextTick } from 'vue'  
const { ctx } = getCurrentInstance()  
const $emit = defineEmits(['close', 'open'])  
const props = defineProps({  

  /* 标题 */  
  title: String,  

  /* 百分比高度 */  
  height: {  
    type: [Number, String],  
    default: 90  
  },  

  /* 边框圆角:左上 右上 左下 右下 */  
  borderRadius: {  
    type: String,  
    default: '10px 10px 0 0'  
  },  

  showClose: {  
    type: Boolean,  
    default: true  
  },  
  headerStyle: [String, Array, Object],  
})  

const popup = ref()  

const inputHeight = computed(() => {  
  return Math.min(props.height, 100)  
})  

const height = computed(() => {  
  const percent = ctx.$uv.sys().windowHeight / 100  
  let height = ctx.$uv.addUnit((percent * inputHeight.value))  
  return height  
})  

const _close = () => {  
  $emit('close')  
  popup.value.close()  
}  

const open = (type = 'center') => {  
  popup.value.open(type)  
  nextTick(() => { $emit('open') })  
}  

defineExpose({  
  open,  
  close: _close  
})  
</script>  
<template>  
  <uni-popup ref="popup" background-color="#fff" @change="change" :borderRadius="height == 'auto'?'10px 10px 10px 10px':'10px 10px 0 0'">  
    <view class="et-dialog" :style="height == 'auto'?{}:{height}">  
      <view class="et-dialog__header" :style="headerStyle">  
        <view class="et-dialog__title">{{props.title}}</view>  
        <uni-icons v-if="showClose" class="et-dialog__headerbtn" type="closeempty" size="20" @click="_close()"></uni-icons>  
      </view>  
      <view class="et-dialog__body">  
        <slot />  
      </view>  
      <!-- <view class="et-dialog__footer"></view> -->  
    </view>  
  </uni-popup>  
</template>  
<style scoped lang="scss">  
.et-dialog {  
  // [头部]  
  .et-dialog__header {  
    height: 36px;  
    position: relative;  
    padding: 0 20px;  
    border-bottom: 0.5px solid #f2f2f2;  
    box-sizing: border-box;  

    .et-dialog__title {  
      line-height: 36px;  
      text-align: center;  
    }  

    .et-dialog__headerbtn {  
      position: absolute;  
      top: 9px;  
      right: 20px;  
      padding: 0;  
      background: transparent;  
      border: none;  
      outline: none;  
      cursor: pointer;  
    }  
  }  

  // [内容]  
  .et-dialog__body {  
    height: calc(100% - 37px);  
  }  

  // [尾部]  
  .et-dialog__footer {  
  }  
}  
</style>  
4***@qq.com

4***@qq.com (作者) - web

工程文件

要回复问题请先登录注册