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
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:
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;
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.
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
asset_types
includes filters
, components
, directives
, all of the following asset_types
are replaced by the previous items
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
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
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
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__
$root You can get the registered filter. The implementation here:
<template> <p> {{ rootFilters( sexVal )}} </p> </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>
// 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')) }
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;
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.
Vue.util is used here. In addition, it can be seen everywhere in use
this .$root.$optionsThis way of accessing the internal properties of the vue instance is not very civilized and confusing to read.
使用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='text/javascript'> 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>
这里不仅注册了过滤器,而且也暴露了数据字典,以方便某些地方的列表显示,毕竟这是实际项目中常见的场景。
相关推荐:
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!