Home >Web Front-end >JS Tutorial >Implementation method of dynamic filtering of Vue project data

Implementation method of dynamic filtering of Vue project data

不言
不言forward
2018-11-16 16:46:163961browse

The content of this article is about the implementation method of dynamic filtering of Vue project data. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

This problem is an actual scenario I encountered while working on a Vue project. Here is a record of my thoughts after encountering the problem and how I finally solved it (old programmers have bad memories

The problem is this:

The data obtained by the page from the background is composed of keys such as 0 and 1, and the corresponding relationship between the value represented by this key, such as 0-female and 1-male, is Obtained from another data dictionary interface;

类似于这样的Api:

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

Then if the view gets 0, it must find its description from the dictionary and display it; the following story begins

1. Thinking

Some people say, isn’t this what the filter is supposed to do? Just use Vue.filter directly.

However, the problem is that this filter has to wait for the asynchronous data dictionary interface It can only be obtained after returning. If this filter is not found when $mounting.

Then it will cause an error and affect subsequent rendering (white screen and undefined error);

What I thought of There are two solutions:

  • Change the interface to synchronization, and obtain the data dictionary interface synchronously in the beforeCreate or created hook to ensure that the registered filter can be obtained when $mounting , to ensure the timing, but this will block the mount and extend the white screen time, so it is not recommended;

  • Change the registration of the filter to asynchronous, and notify the render watcher to update itself after obtaining the filter. In this way, vue's own responsiveness can be used to update the view without blocking rendering, so this method is initially adopted below.

2. Implementation

Because filter belongs to asset_types , there are the following conclusions about the access chain of asset_types in the Vue instance;

For specific code practices, please refer to: Codepen - filter test

  • asset_types includes filters, components, directives, all the following asset_types are replaced by the previous ones

  • The asset_types in the child component cannot access the asset_types in the parent component, but can access the globally registered mount in $ asset_types on root.$options.asset_types.__proto__, which corresponds to the source code src/core/util/options.js

  • Globally registered method Vue.asset_types, such as registered by Vue.filters asset_types will be mounted to the $options.asset_types.__proto__ of the root instance ($root of other instances), and will be inherited by all Vue instances created in the future. That is to say, all Vue instances created in the future can access

  • The scope of a component's slot is limited to the place where it is defined, that is, in the component where it is defined. The asset_types of the parent component cannot be accessed, but the globally defined asset_types can be accessed

  • Similarly, because the new Vue() instance in main.js is the root instance, the asset_types registered in it will be mounted on $root.$options.asset_types instead of $ root.$options.asset_types.__proto__

Based on the above conclusions, you can start coding~

2.1 Use the filters of the root component
So the first thing I consider is to mount the filter to be registered on the root component, so that other components can get the registered filter by accessing $root.

Implementation here:

<template>
  <div>
    {{ rootFilters( sexVal )}}
  </div>
</template>

<script type=&#39;text/javascript&#39;>
  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 for registering filter

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

This makes the filters on the root component responsive, and when rendering, because of the rootFilters The method accesses $root.$options.filters that have been reactive in created.

So when the asynchronously obtained data is assigned to $root.$options.filters, it will trigger the re-rendering of the render watcher of this component. At this time, the filter can be obtained when the rootFilters method is obtained;

Then why not register directly with the Vue.filter method here.

Because Object.defineProperty cannot monitor changes in data on __proto__.

The global Vue.filter registers the filter on the root component $root.$options.asset_types.__proto__, so its changes cannot be responded to.

The code here can be further improved, but there are certain problems with this method. First of all, the unstable method on Vue.util is used here.

In addition, you can see this.$root.$options accessing the internal properties of the vue instance everywhere during use, which is not very civilized and confusing to read.

So when this project was completed and waiting for testing, I thought about it. Who said filters must be placed in filters -. -, you can also use mixin to achieve it

2.2 Using mixin
Be careful when using mixin, because vue treats all variables starting with _ and $ in data as internal Reserved variables.

does not proxy to the current instance, so this._xx cannot be accessed directly and needs to be accessed through 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
    }
  }
}

Save the promise of the Api here. If it is used elsewhere, it will directly return the promise that is already in the resolved state, so there is no need to request data again.

In addition, in order to facilitate access in other instances, it is mounted on the root component.

How to use it in our root component:

// 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=&#39;text/javascript&#39;>
  export default {
    data() {
      return { sexVal: 1 }
    },
    mounted() {
      this.$root._getSysParamsFunc()
        .then(data =>
          // 这里获取到数据字典的data
        )
    }
  }
</script>

这里不仅注册了过滤器,而且也暴露了数据字典,以方便某些地方的列表显示,毕竟这是实际项目中常见的场景。

当然如果使用vuex更好,不过这里的场景个人觉得没必要用vuex,如果还有更好的方法可以讨论一下下啊~

The above is the detailed content of Implementation method of dynamic filtering of Vue project data. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete