Home  >  Article  >  Web Front-end  >  The implementation process of dynamic filtering of Vue project data

The implementation process of dynamic filtering of Vue project data

不言
不言Original
2018-09-08 17:39:351289browse

The content of this article is about the implementation process 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 I record my thoughts after encountering the problem and how I finally solved it (old programmers have bad memory-.-), and the process It will involve some Vue source code concepts such as $mount, render watcher, etc. If you don’t know much about it, you can take a look at the Vue source code reading series~

Questions It is like this: the data the page gets from the background is composed of keys such as 0, 1, and the value represented by this key is such as 0-女, The corresponding relationship of 1-Male is to be obtained from another data dictionary interface; similar to this API:

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

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

1. Thinking

Some people say that this is not a filterfilter What to do, just use Vue.filter directly. However, the problem is that this filter has to wait for the asynchronous data dictionary interface to return before it can be obtained. If it is $mount, If this filter is not found, it will cause errors that will affect subsequent rendering (white screen and undefined error);

I have two solutions in mind:

  1. 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 at $mount , to ensure the timing, but this will block the mount and extend the white screen time, so it is not recommended;

  2. Change the registration of the filter to asynchronous, and notify the render watcher after getting the filter Update yourself so that you can use vue's own responsiveness 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 Vue instances; for specific code practices, please refer to: Codepen - filter test

  1. asset_types includes filters, components, directives, all of the following asset_types are replaced by the previous items

  2. asset_types in the sub-component cannot access asset_types in the parent component. However, you can access the globally registered asset_types mounted on $root.$options.asset_types.__proto__, which corresponds to the source code src/core/util/options.js

  3. Global registration method Vue.asset_types, for example, the asset_types registered by Vue.filters will be mounted to the $options.asset_types of the root instance ($root of other instances) .__proto__, and is inherited by all Vue instances created in the future. That is to say, all Vue instances created in the future can access the slot of the

  4. component. The scope of the slot is only Limited to the place where it is defined, that is, in the component in which it is defined, the asset_types of the parent component cannot be accessed, but the globally defined asset_types

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

## Based on the above conclusions, you can start coding ~

2.1 Using 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 access it by accessing

$root You can get the registered filter. The implementation here:

<template>
  <p>
    {{ rootFilters( sexVal )}}
  </p>
</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
    .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 The

rootFilters method accesses $root.$options.filters that has been responsively made in created, so when the asynchronously acquired data is assigned to $root.$ options.filters will trigger the re-rendering of this component render watcher. At this time, you can get the filter when you get the rootFilters method;

Why not use Vue here? The .filter method is registered directly, because

Object.defineProperty cannot monitor data changes on __proto__, and the global Vue.filter registers the filter in 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, an unstable method on

Vue.util is used here. In addition, it can be seen everywhere in usethis .$root.$optionsThis way of accessing the internal properties of the vue instance 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 使用mixin

使用mixin要注意一点,因为vue中把data里所有以_$开头的变量都作为内部保留的变量,并不代理到当前实例上,因此直接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 thisPromise = this.$data._sysParamsPromise
      return thisPromise || Api.sysParams()            // 获取数据字典的Api
        .then(({ data }) => {
          this.$data._filterFunc = {}
          Object.keys(data).forEach(paramKey =>
            this.$data._filterFunc[paramKey] = val => {        // 过滤器注册到_filterFunc中
              const tar = data[paramKey].find(item => item['paramValue'] === val)
              return 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
    }
  },
  mounted() {
    this.$data._filterFunc ||
    (this.$data._sysParamsPromise = this._getSysParamsFunc())
  }
}

这里把Api的promise保存下来,如果其他地方还用到的话直接返回已经是resolved状态的promise,就不用再次去请求数据了。

那在我们的组件中怎么使用呢:

<template>
  <p>
    {{ _rootFilters( sexVal )}}
  </p>
</template>
 
<script type=&#39;text/javascript&#39;>
  import * as Api from 'api'
  import sysParamsMixin from 'mixins/sysParamsMixin'
 
  export default {
    mixins: [sysParamsMixin],
    data() {
      return { sexVal: 1 }
    },
    mounted() {
      this._getSysParamsFunc()
        .then(data =>
          // 这里获取到数据字典的data
        )
    }
  }
</script>

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

相关推荐:

如何在项目中使用Vue+animate过渡动画

Vue过滤器filters使用详解

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

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn