属性 或方法“_”未在实例上定义但在渲染期间被引用

Property or method "_" is not defined on the instance but referenced during render

我是一个有能力的 React 开发人员,但是从另一个开发人员那里继承了一个 vue.js 项目并且已经维护了几年,不幸的是我没有像我应该的那样通过个人努力来学习 vue。

我在使用 lodash 时抛出了一个奇怪的错误,我相信它不喜欢我的 _.debounce 调用

组件:
<script>
import _ from 'lodash'
import CostCodeField from '@/components/workdays/CostCodeField'

// ...
</script>

<template lang='html'>
  <!-- relevant code snippet -->
  <!-- ... -->
  <b-table class="charges-table is-fullwidth" :data="workday.charges" :striped="true" :mobile-cards="false" :row-class="chargeClass">
    <b-table-column label="Cost Code" width="260">
        <b-field expanded="expanded">
          <cost-code-field
            :value="props.row.cost_code.number" :disabled="timecard.is_submitted || locked || isLoading(props)"
            :job="props.row.job"
            @input="set($event, props.index, 'cost_code')"
            @change="_.debounce(submit(props.row, props.index), 100)"
          ></cost-code-field>
          <p class="control">
            <a
              class="button"
              @click="triggerCostCode(props.row, props.index)" :disabled="props.row.job.jd_job_number_id === undefined || timecard.is_submitted || isLoading(props)"
            >
              <b-icon icon="magnify"></b-icon>
            </a>
          </p>
        </b-field>
      </b-table-column>
      <!-- ... -->
  </b-table>
</template>
console.error
[Vue warn]: Property or method "_" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

found in

---> <ChargesTable> at src/components/workdays/ChargesTable.vue
       <BTabItem>
         <BTabs>
           <WorkdayListItem> at src/components/workdays/WorkdayListItem.vue
             <Timecard> at src/components/timecards/Timecard.vue
               <App> at src/App.vue
                 <Root> vue.esm.js:628
    VueJS 3
    change ChargesTable.vue:127
    VueJS 4
    setCode CostCodeField.vue:108
    VueJS 12
    mutations timecards.js:322
    mutations timecards.js:319
    wrappedMutationHandler vuex.esm.js:844
    commitIterator vuex.esm.js:466
    commit vuex.esm.js:465
    _withCommit vuex.esm.js:624
    commit vuex.esm.js:464
    boundCommit vuex.esm.js:409
    submit CostCodeLookup.vue:134
    submit CostCodeLookup.vue:16
    VueJS 33
[Vue warn]: Error in v-on handler: "TypeError: _vm._ is undefined"

found in

---> <CostCodeField> at src/components/workdays/CostCodeField.vue
       <BField>
         <BTableColumn>
           <BTable>
             <ChargesTable> at src/components/workdays/ChargesTable.vue
               <BTabItem>
                 <BTabs>
                   <WorkdayListItem> at src/components/workdays/WorkdayListItem.vue
                     <Timecard> at src/components/timecards/Timecard.vue
                       <App> at src/App.vue
                         <Root> vue.esm.js:628
TypeError: _vm._ is undefined
    change ChargesTable.vue:127
    VueJS 4
    setCode CostCodeField.vue:108
    VueJS 12
    mutations timecards.js:322
    mutations timecards.js:319
    wrappedMutationHandler vuex.esm.js:844
    commitIterator vuex.esm.js:466
    commit vuex.esm.js:465
    _withCommit vuex.esm.js:624
    commit vuex.esm.js:464
    boundCommit vuex.esm.js:409
    submit CostCodeLookup.vue:134
    submit CostCodeLookup.vue:16
    VueJS 33

模板中引用的任何 var 在后台都带有 this 前缀。这意味着您不能在模板中引用全局变量,除非它们在其上下文中明确声明。

简单来说,模板上下文就是一个对象。如果你没有定义一个特定的键,也没有给它分配一个特定的值,它将是 undefined.

将全局变量暴露给模板的最简洁的方法可能是使用 computed:

// ...
  computed: {
    _: () => _
  }
// ...

在 Composition API 中,公开全局变量的最简单方法是将它们添加到 setup 返回的对象中:

export default {
  // ... 
  setup() {
    //...
    return {
      _,
      //...
    }
  }
}

在模板中访问 _

假设您正在使用选项 API,导入 _ 不会自动使其对模板可用(正如@tao 在 ). The template can only access fields exposed via the component options (e.g., data, props, methods, computed, etc.) in addition to a few allow-listed globals (Vue 2 allowed globals, Vue 3 allowed globals 中指出的那样)。

使用_.debounce

_.debounce 的第一个参数是 函数引用:

_.debounce(submit(props.row, props.index), 100) // ❌ calls `submit()`, and passes the result to _.debounce()

要创建去抖动的 submit,请将 submit 引用作为参数传递给 _.debounce,如下所示:

_.debounce(submit, 100)

从技术上讲,您可以立即调用去抖函数:

_.debounce(submit, 100)(props.row, props.index)

...但是不要在模板中这样做(原因见下文)。

v-on

使用去抖事件处理程序

v-on 指令(@ for shorthand)的值是一个表达式(如您的情况)时,模板编译器会自动将表达式包装在一个函数中,所以这个:

@change="_.debounce(submit, 100)"

...本质上变成:

@change="($event) => _.debounce(submit, 100)"

...这不会有任何效果,因为 debounce 不会调用包装函数本身。

您可能想在以下位置立即调用该函数:

@change="_.debounce(submit, 100)(props.row, props.index)"

...但这会在每个事件上创建一个 new 去抖动函数,这会破坏去抖动。

解决方案

在 SFC 的 <script> 部分创建一个去抖函数,然后可以将其用作 v-on 值:

选项API:

<script>
import { debounce } from 'lodash'

export default {
  created() {
    this.debouncedSubmit = debounce(this.submit, 100)
  },
  methods: {
    submit(row, index) {/*...*/}
  }
}
</script>

setup() 选项中的组合 API:

<script>
import { debounce } from 'lodash'

export default {
  setup() {
    const submit = (row, index) => {/*...*/}

    return {
      debouncedSubmit: debounce(submit, 100),
    }
  }
}
</script>

<script setup>中的组合API:

<script setup>
import { debounce } from 'lodash'

const submit = (row, index) => {/*...*/}
const debouncedSubmit = debounce(submit, 100)
</script>

模板:

<cost-code-field @change="debouncedSubmit(props.row, props.index)" />

demo