Как предотвратить перезагрузку родительского компонента при изменении параметризованного дочернего компонента в Vue js - PullRequest
12 голосов
/ 14 мая 2019

У меня есть страница, на которой ClientPortfolio (родительский компонент), содержащий список ценных бумаг (дочерний компонент), загружается в список v-data-table.

enter image description here

Проблема, с которой я столкнулся, заключается в том, что ClientPortfolio полностью перезагружается каждый раз, когда я щелкаю по безопасности в списке, в результате чего обновляется весь список, в результате чего прокрутка и выбранный класс сбрасываются, а также излишние потери производительности. Я посмотрел документацию по Vue, и, кажется, ничто не указывает на то, как обновлять дочерний компонент только тогда, когда у него есть параметры, похоже, что родительский компонент обновляется при изменении маршрута каждый раз, когда выбирается защита, несмотря на то, что Vue будет знать , что изменяется только sub (вложенный маршрут), следовательно, необходимо только перезагрузить дочерний компонент

enter image description here

Ближайший ответ, который я получил, был объяснен на https://github.com/vuejs/vue-router/issues/230, который не объясняет в коде, как этого добиться.

routes.js:

routes: [
    {
      path: '/client/:clientno/portfolios/:portfolioNo',
      component: ClientPortfolios,
      children: [
        { path: 'security/:securityNo', component: Security }
      ]     
    }, 
  ]

Ссылка на маршрутизатор в 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>

Вид маршрутизатора (для компонента безопасности) в ClientPortfolios.vue:

<v-flex xs10 ml-2>
      <v-layout>
          <router-view :key="$route.fullPath"></router-view>
      </v-layout>
    </v-flex>

Любой совет о том, как предотвратить перезагрузку родителей, приветствуется.

РЕДАКТИРОВАТЬ: Пытаясь приблизиться к проблеме, я замечаю, что атрибут «Ключ» в ClientPortfolios изменяется (как показано в окне отладки Vue выше) всякий раз, когда я меняю Security, может ли это быть причиной? Есть ли способ назначить ключ компоненту 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>

Security.vue

<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>

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'
        }
      ]
    }
  ]
})

UPDATE

Просто чтобы обновить это, как это было давно, мне наконец-то удалось выяснить, в чем проблема, о которой @matpie указал в другом месте, я обнаружил, что мой App.vue является виновником, где есть ключ: добавьте в самый корень приложения: <router-view :key="$route.fullPath" /> это был шаблон, который я использовал откуда-то, но мне никогда не приходилось смотреть, как он «работал», после удаления ключа все работает как надо, отмечая ответ Matpie принятым.

Ответы [ 4 ]

3 голосов
/ 22 мая 2019

Предотвращение перезагрузки компонента является поведением по умолчанию в Vue.js.Система реактивности Vue автоматически отображает зависимости свойств и выполняет только минимальный объем работы, чтобы гарантировать актуальность DOM.

Используя где-либо атрибут :key, вы сообщаете Vue.js, что этот элемент или компонент должен только совпадают, когда ключи совпадают.Если ключи не совпадают, старый будет уничтожен, а новый будет создан.

Похоже, вы также вводите параметры маршрута для объекта данных (Security.vue).Они не будут обновляться при изменении параметров маршрута, вы должны перетащить их в вычисляемое свойство, чтобы они всегда оставались актуальными.

export default {
  computed: {
    clientNo: (vm) => vm.$route.params.clientno,
  }
}

Это гарантирует, что clientNo всегда соответствует тому, чтонаходится в маршрутизаторе, независимо от того, решит ли Vue повторно использовать этот экземпляр компонента.Если вам нужно выполнить другие побочные эффекты при изменении clientNo, вы можете добавить наблюдателя:

vm.$watch("clientNo", (clientNo) => { /* ... */ })
0 голосов
/ 25 мая 2019

Не могли бы вы проверить еще раз после удаления локальной регистрации компонента безопасности?Поскольку это не нужно, потому что это обрабатывается самим маршрутизатором vue.

components: { // delete this code

      security: Security
    },
0 голосов
/ 22 мая 2019

Однажды у меня была похожая проблема.ИМО это было вызвано разбором пути.Попробуйте установить имя для вашего маршрута.И замените свой параметр router-link to на объект.И удалите router-view :key prop.Это не должно быть там.Он используется для принудительного обновления компонента при изменении маршрута.Обычно это признак плохого кода.Ваш компонент (Security) должен реагировать на обновление параметров маршрута.Не родительский компонент заставляет его.

Итак, попробуйте изменить свой код на:

routes: [
    {
      path: '/client/:clientno/portfolios/:portfolioNo',
      component: ClientPortfolios,
      name: "ClientPortfoliosName", // it can be anything you want. It`s just an alias for internal use.
      children: [
        { 
           path: 'security/:securityNo', 
           name: "PortfolioSecurities", // anyway, consider setting route names as good practice
           component: Security 
        }
      ]     
    }, 
  ]
 <router-link tag="tr" style="cursor:pointer"
              :to="{ name: 'PortfolioSecurities', params: { clientno: $route.params.clientno, portfolioNo: selectedPortfolioSequenceNo, securityNo: props.item.SecurityNo+'-'+props.item.SequenceNo } }"
              :key="props.item.SecurityNo+props.item.SequenceNo">

            </router-link>

И он должен работать.

PS В вашем router-link Вы должны указать маршрут, по которому хотите перейти.В этом случае PortfolioSecurities

0 голосов
/ 21 мая 2019

Вместо использования роутера здесь. Объявите две переменные на корневом уровне для выбранной ценной бумаги и портфеля,

список ценных бумаг на основе выбранного портфеля.

при выборе ценных бумаг из отображаемых ценных бумаг обновите корневую переменную, используя

this.$root.selectedSecurityId = id;

вы можете иметь watch на уровне компонента безопасности.

В корне,

<security selectedid="selectedSecurityId" />

В компоненте security,

....
watch:{
   selectedid:function(){
   //fetch info and show
   }
}
... 

компоненты будут выглядеть следующим образом,

<portfolio>
  //active. list goes here
</portfolio>
........
<security selectedid="selectedSecurityId">
//info goes here
</security>

Вышеуказанный подход поможет избежать роутеров. надеюсь, это поможет.

...