Практика динамической фильтрации данных проекта Vue

внешний интерфейс JavaScript Vue.js Promise
Практика динамической фильтрации данных проекта Vue

Эта проблема находится в реальной сцене, с которой я столкнулся в проекте Vue.Здесь я записываю свои мысли после того, как столкнулся с проблемой и как ее решить в конце (старшие программисты имеют плохую память-.-), и некоторые Vue будут вовлечены в процесс.Концепция исходного кода, такая как$mount,render watcherПодождите, если вы не знаете многого, вы можете взглянутьСерия статей по чтению исходного кода Vue ~

Проблема в следующем: данные, которые страница получает из фона,0,1например ключ, а значение, представленное этим ключом, такое как0-女,1-男Соответствующее отношение должно быть получено из другого интерфейса словаря данных; аналогичноТакой Апи:

{
  "SEX_TYPE": [
    { "paramValue": 0, "paramDesc": "女" },
    { "paramValue": 1, "paramDesc": "男" }
  ]
}

Тогда, если вид получает0, необходимо найти его описание в словареИ покажи это, начинается следующая история~

1. Думай

Некоторые говорят, что это не фильтрfilterчто делать, прямоVue.filterНет, но проблема в том, что этот фильтр можно получить только после ожидания возврата интерфейса асинхронного словаря данных.$mountЕсли фильтр не найден, это приведет к ошибкам, которые повлияют на последующую визуализацию (белый экран и сообщение о неопределенной ошибке);

Есть два решения, о которых я могу думать:

  1. изменить интерфейс наСинхронизировать,существуетbeforeCreateилиcreatedИнтерфейс словаря данных получается синхронно в хуке, чтобы гарантировать, что$mountВы можете получить зарегистрированный фильтр для обеспечения времени, но это заблокирует монтирование и продлит время белого экрана, поэтому это не рекомендуется;
  2. Сделать регистрацию фильтра асинхронной и уведомить после получения фильтраrender watcherОбновите себя, чтобы вы могли использовать собственное отзывчивое обновление Vue без блокировки рендеринга, поэтому этот метод изначально принят.

2. Реализация

потому что фильтр принадлежитasset_types, есть следующие выводы о цепочке доступа к assets_types в экземпляре Vue; для конкретной практики кода см.:Codepen - filter test

  1. asset_typesвключатьfilters,components,directives, все следующиеasset_typesзаменить их предыдущими элементами
  2. в дочерних компонентахasset_typesНе удается получить доступ к родительскому компонентуasset_types, но может получить доступ к глобально зарегистрированным монтированиям в$root.$options.asset_types.__proto__Вверхasset_types, здесь соответствует исходному кодуsrc/core/util/options.js
  3. Глобальный метод регистрации Vue.asset_types, такой как assets_types, зарегистрированные Vue.filters, будет подключен к корневому экземпляру (другие экземпляры$root)из$options.asset_types.__proto__, и наследуется всеми экземплярами Vue, созданными в будущем, то есть все экземпляры Vue, созданные в будущем, могут получить к нему доступ
  4. Область действия слота компонента ограничена местом, где он определен, то есть компонентом, в котором он определен, и не может получить доступ к родительскому компоненту.asset_types, но может получить доступ к глобально определенномуasset_types
  5. Точно так же, поскольку в main.jsnew Vue()instance — это корневой экземпляр, зарегистрированный вasset_typesбудет установлен на$root.$options.asset_typesна вместо$root.$options.asset_types.__proto__начальство

Согласно приведенным выше выводам, мы можем начать кодирование~

2.1 Использование фильтров корневого компонента

Поэтому первое, что я считаю, это смонтировать фильтр для регистрации на корневом компоненте, чтобы другие компоненты могли получить доступ к$rootВы можете получить зарегистрированный фильтр, реализация здесь:

<template>
  <div>
    {{ rootFilters( sexVal )}}
  </div>
</template>
 
<script type='text/javascript'>
  import Vue from 'vue'
  import { registerFilters } from 'utils/filters'
 
  export default {
    data() {
      return {
        sexVal: 1  // 性别
      }
    },
    methods: {
      /* 根组件上的过滤器 */
      rootFilters(val, id = 'SEX_TYPE') {
        const mth = this.$root.$options.filters[id]
        return mth && mth(val) || val
      }
    },
    created() {
      // 把根组件中的filters响应式化
      Vue.util.defineReactive(this.$root.$options, 'filters', this.$root.$options.filters)
    },
    mounted() {
      registerFilters.call(this)
        .then(data =>
          // 这里获取到数据字典的data
        )
    }
  }
</script>

js для регистрации фильтра

// utils/filters
 
import * as Api from 'api'
 
/**
* 获取并注册过滤器
* 注册在$root.$options.filters上不是$root.$options.filters.__proto__上
* 注意这里的this是vue实例,需要用call或apply调用
* @returns {Promise}
*/
export function registerFilters() {
  return Api.sysParams()            // 获取数据字典的Api,返回的是promise
    .then(({ data }) => {
      Object.keys(data).forEach(T =>
        this.$set(this.$root.$options.filters, T, val => {
          const tar = data[T].find(item => item['paramValue'] === val)
          return tar['paramDesc'] || ''
        })
      )
      return data
    })
    .catch(err => console.error(err, ' in utils/filters.js'))
}

Это делает фильтры на корневом компоненте отзывчивыми, а при рендерингеrootFiltersметод имеет доступ к созданному методу, который был сделан реактивным$root.$options.filters, поэтому, когда асинхронно извлекаемые данные назначаются$root.$options.filters, он вызовет повторный рендеринг наблюдателя рендеринга компонента, а затем снова получит его.rootFiltersФильтр можно получить при использовании метода;

Так почему бы не зарегистрироваться напрямую с помощью метода Vue.filter, потому чтоObject.definePropertyне могу контролировать__proto__Изменения в данных и глобальный Vue.filter для регистрации фильтра в корневом компоненте$root.$options.asset_types.__proto__, поэтому на его изменения нельзя реагировать.

Код здесь можно еще улучшить, но есть определенные проблемы с этим методом, во-первых, он используется здесьVue.utilметод, который не является стабильным, и его можно увидеть повсюду в использованииthis.$root.$optionsТакой доступ к внутренним свойствам экземпляра vue является нецивилизованным и запутанным для чтения.

Поэтому, когда проект был завершен и ждал теста, я задумался.Кто бы сказал, что фильтр надо ставить в фильтры -. -, вы также можете использовать миксин для достижения этого

2.2 Использование примесей

Одна вещь, на которую следует обратить внимание при использовании миксинов, потому что vue помещает все данные в_,$Переменные в начале рассматриваются как внутренне зарезервированные переменные,не проксирует текущий экземпляр, так прямоthis._xxнедоступен и требуетthis.$data._xxпосетить.

// mixins/sysParamsMixin.js

import * as Api from 'api'

export default {
  data() {
    return {
      _filterFunc: null,       // 过滤器函数
      _sysParams: null,        // 获取数据字典
      _sysParamsPromise: null  // 获取sysParams之后返回的Promise
    }
  },
  methods: {
    /* 注册过滤器到_filterFunc中 */
    _getSysParamsFunc() {
      const { $data } = this
      return $data._sysParamsPromise || ($data._sysParamsPromise = Api.sysParams()
        .then(({ data }) => {
          this.$data._sysParams = data
          this.$data._filterFunc = {}
          Object.keys(data).forEach(paramKey =>
            this.$data._filterFunc[paramKey] = val => {
              const tar = data[paramKey].find(item => item['paramValue'] === val)
              return tar && tar['paramDesc'] || ''
            })
          return data
        })
        .catch(err => console.error(err, ' in src/mixins/sysParamsMixin.js')))
    },

    /* 按照键值获取单个过滤器 */
    _rootFilters(val, id = 'SEX_TYPE') {
      const func = this.$data._filterFunc
      const mth = func && func[id]
      return mth && mth(val) || val
    },

    /* 获取数据字典 */
    _getSysParams() {
      return this.$data._sysParams
    }
  }
}

положи сюдаApiОбещание сохраняется, и если оно используется в другом месте, оно уже возвращается напрямуюresolvedОбещание государства, вам не нужно снова запросить данные. Кроме того, для удобного доступа в других случаях он устанавливается на корневом компоненте здесь.

что в нашемкорневой компонентКак использовать в:

// src/main.js

import sysParamsMixin from 'mixins/sysParamsMixin'

new Vue({
  el: '#app',
  mixins: [sysParamsMixin],
  render: h => h(App),
})

В компоненте, который должен использовать фильтр:

<template>
  <div>
    {{ $root._rootFilters( sexVal )}}
  </div>
</template>
 
<script type='text/javascript'>
  export default {
    data() {
      return { sexVal: 1 }
    },
    mounted() {
      this.$root._getSysParamsFunc()
        .then(data =>
          // 这里获取到数据字典的data
        )
    }
  }
</script>

Здесь прописаны не только фильтры, но и выставлен словарь данных для облегчения отображения списков в определенных местах, ведь это частый сценарий в реальных проектах.

Конечно, лучше использовать vuex, но я лично не думаю, что vuex нужен здесь для сцены, если есть лучший способ, могу обсудить~


Большинство сообщений в Интернете имеют разную глубину и даже некоторые несоответствия. Следующие статьи являются кратким изложением процесса обучения. Если вы найдете какие-либо ошибки, пожалуйста, оставьте сообщение, чтобы указать ~

Ссылаться на:

  1. Исходный код Vue.js 2.5.17
  2. Серия чтения исходного кода Vue
  3. Vue 2.5.17 filter test

PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~

Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~