在Vue js中更改参数化的子组件时如何防止父组件重新加载

May*_*aya 13 vue.js vue-router vue-component vuejs2

我有一个页面,其中将包含证券(子组件)列表的ClientPortfolio(父组件)加载到v数据表列表中。

在此处输入图片说明

我遇到的问题是,每次我单击列表中的安全性时,ClientPortfolio都会完全重新加载,从而导致整个列表被刷新,从而导致滚动和所选类重置,以及不必要的性能开销。我看过Vue的文档,似乎没有什么能指出如何仅在具有参数的情况下刷新子组件的,看起来每次路由选择安全性时都会随着路由的更改而刷新父组件。 Vue 知道只有子(嵌套路由)在变化,因此只需要重新加载子组件

在此处输入图片说明

我得到的最接近的答案在https://github.com/vuejs/vue-router/issues/230上进行了说明,该代码未在代码中说明如何实现此目的。

route.js:

routes: [
    {
      path: '/client/:clientno/portfolios/:portfolioNo',
      component: ClientPortfolios,
      children: [
        { path: 'security/:securityNo', component: Security }
      ]     
    }, 
  ]
Run Code Online (Sandbox Code Playgroud)

ClientPortfolios.vue中的路由器链接:

 <router-link tag="tr" style="cursor:pointer"
              :to="`/client/${$route.params.clientno}/portfolios/${selectedPortfolioSequenceNo}/security/${props.item.SecurityNo}-${props.item.SequenceNo}`"
              :key="props.item.SecurityNo+props.item.SequenceNo">

            </router-link>
Run Code Online (Sandbox Code Playgroud)

ClientPortfolios.vue中的路由器视图(用于安全组件):

<v-flex xs10 ml-2>
      <v-layout>
          <router-view :key="$route.fullPath"></router-view>
      </v-layout>
    </v-flex>
Run Code Online (Sandbox Code Playgroud)

任何有关如何防止父母被重新加载的提示表示赞赏。

编辑:试图更接近该问题,我注意到,每当我更改安全性时,ClientPortfolios中的“关键”属性都会更改(如上面的Vue调试窗口所示),这可能是原因吗?尽管它不是子组件,是否可以将密钥分配给ClientPortfolios组件?还是在导航到其他证券时不更新其密钥的方法?

更新:完整代码

ClientPortfolios.vue

<template>
  <v-layout row fill-height>
    <v-flex xs2>
      <v-layout column class="ma-0 pa-0 elevation-1">
        <v-flex>
          <v-select v-model="selectedPortfolioSequenceNo" :items="clientPortfolios" box label="Portfolio"
            item-text="SequenceNo" item-value="SequenceNo" v-on:change="changePortfolio">
          </v-select>
        </v-flex>
        <v-data-table disable-initial-sort :items="securities" item-key="Id" hide-headers hide-actions
          style="overflow-y: auto;display:block;height: calc(100vh - 135px);">
          <template slot="items" slot-scope="props">
            <router-link tag="tr" style="cursor:pointer"
              :to="{ name: 'Security', params: { securityNo: props.item.SecurityNo+'-'+props.item.SequenceNo } }"
              >
            </router-link>

          </template>
          <template v-slot:no-data>
            <v-flex class="text-xs-center">
              No securities found
            </v-flex>
          </template>
        </v-data-table>
      </v-layout>
    </v-flex>

    <v-flex xs10 ml-2>
      <v-layout>
        <keep-alive>
          <router-view></router-view>
        </keep-alive>
      </v-layout>
    </v-flex>
  </v-layout>

</template>
<script>
  import Security from '@/components/Security'

  export default {
    components: {

      security: Security
    },
    data () {
      return {
        portfoliosLoading: false,
        selectedPortfolioSequenceNo: this.$route.params.portfolioNo,
        selectedPortfolio: null,
        securityNo: this.$route.params.securityNo
      }
    },
    computed: {
      clientPortfolios () {
        return this.$store.state.ClientPortfolios
      },
      securities () {
        if (this.clientPortfolios == null || this.clientPortfolios.length < 1) {
          return []
        }
        let self = this
        this.selectedPortfolio = global.jQuery.grep(this.clientPortfolios, function (portfolio, i) {
          return portfolio.SequenceNo === self.selectedPortfolioSequenceNo
        })[0]

        return this.selectedPortfolio.Securities
      }
    },
    mounted () {
      this.getClientPortfolios()
    },
    activated () {
    },
    methods: {
      changePortfolio () {
        this.$router.push({
          path: '/client/' + this.$route.params.clientno + '/portfolios/' + this.selectedPortfolioSequenceNo
        })
      },
      getClientPortfolios: function () {
        this.portfoliosLoading = true
        let self = this
        this.$store.dispatch('getClientPortfolios', {
          clientNo: this.$route.params.clientno
        }).then(function (serverResponse) {
          self.portfoliosLoading = false
        })
      }
    }
  }
</script>
Run Code Online (Sandbox Code Playgroud)

安全性

<template>
  <v-flex>
    <v-layout class="screen-header">
      <v-flex class="screen-title">Security Details </v-flex>

    </v-layout>
    <v-divider></v-divider>
    <v-layout align-center justify-space-between row class="contents-placeholder" mb-3 pa-2>
      <v-layout column>
        <v-flex class="form-group" id="security-portfolio-selector">
          <label class="screen-label">Sequence</label>
          <span class="screen-value">{{security.SequenceNo}}</span>
        </v-flex>
        <v-flex class="form-group">
          <label class="screen-label">Security</label>
          <span class="screen-value">{{security.SecurityNo}}-{{security.SequenceNo}}</span>
        </v-flex>

        <v-flex class="form-group">
          <label class="screen-label">Status</label>
          <span class="screen-value-code" v-if="security.Status !== ''">{{security.Status}}</span>
        </v-flex>
      </v-layout>

    </v-layout>

  </v-flex>

</template>
<script>
  export default {
    props: ['securityNo'],
    data () {
      return {
        clientNo: this.$route.params.clientno,
        securityDetailsLoading: false
      }
    },
    computed: {
      security () {
        return this.$store.state.SecurityDetails
      }
    },

    created () {
      if (this.securityNo.length > 1) {
        this.getSecurityDetails()
      }
    },

    methods: {
      getSecurityDetails: function () {
        let self = this
        this.securityDetailsLoading = true

        this.$store.dispatch('getSecurityDetails', {
          securityNo: this.securityNo,
          clientNo: this.clientNo

        }).then(function (serverResponse) {
          self.securityDetailsLoading = false
        })
      }
    }
  }
</script>
Run Code Online (Sandbox Code Playgroud)

router.js

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: Dashboard
    },
    {
      path: '/client/:clientno/details',
      component: Client,
      props: true
    },

    {
      path: '/client/:clientno/portfolios/:portfolioNo',
      component: ClientPortfolios,
      name: 'ClientPortfolios',
      children: [
        { path: 'security/:securityNo',
          component: Security,
          name: 'Security'
        }
      ]
    }
  ]
})
Run Code Online (Sandbox Code Playgroud)

更新

刚刚更新了一段时间,我终于找到了问题所在,这是@matpie在其他地方指出的问题,我发现App.vue是在其中添加:key的罪魁祸首。应用程序的最根本部分:<router-view :key="$route.fullPath" />这是我从某个地方使用的模板,但是从不用看它是“正在工作”,删除密钥后,所有工作都按预期进行,从而标记了matpie答案。

mat*_*pie 5

阻止组件重新加载是Vue.js中的默认行为。Vue的反应性系统会自动映射属性依赖关系,并且仅执行最少的工作来确保DOM是最新的。

通过在:key任何地方使用属性,可以告诉Vue.js该元素或组件在键匹配时才匹配。如果密钥不匹配,则旧密钥将被销毁,而新密钥将被创建。

看起来您也在提取数据对象(Security.vue)上的路由参数。当路由参数更改时,这些参数不会更新,您应该将其添加到计算属性中,以便它们始终保持最新状态。

export default {
  computed: {
    clientNo: (vm) => vm.$route.params.clientno,
  }
}
Run Code Online (Sandbox Code Playgroud)

这将确保clientNo始终匹配路由器中找到的内容,而不管Vue是否决定重用此组件实例。如果clientNo更改时需要执行其他副作用,则可以添加观察者:

vm.$watch("clientNo", (clientNo) => { /* ... */ })
Run Code Online (Sandbox Code Playgroud)

  • 只是为了更新这个已经有一段时间了,我终于找出了问题所在,这就是@matpie 在其他地方指出的,我发现我的 App.vue 是罪魁祸首,其中有一个 :key 添加到应用程序的根: &lt;router-view :key="$route.fullPath" /&gt; 这是我从某处使用的模板,但从来没有看到它“工作”,删除密钥后,一切正常正如它应该的那样,标记 matpie 答案已被接受。谢谢! (3认同)