当前位置: 首页 > Web前端 > HTML

Vue项目数据动态过滤实践

时间:2023-04-02 16:00:05 HTML

这个问题是下一个Vue项目中遇到的实际场景,这里记录下我遇到问题后的思考以及最后是怎么解决的(老程序员记性不好-.-)。问题是这样的:页面从后台获取的数据是一个key比如0、1,这个key代表的值比如0-female,1-male是从另外一个数据字典接口获取的;Api类似这样:{"SEX_TYPE":[{"paramValue":0,"paramDesc":"female"},{"paramValue":1,"paramDesc":"male"}]}然后如果视图需要当它到达0,需要从字典中找到它的描述并显示出来;下面的故事开始了1.思考有人说,这不是filter应该做的吗,别随便用Vue.filter。但是问题是这个filter只有在异步数据字典接口返回后才能获取到,如果在$mount的时候没有找到filter。那么就会报错影响后续渲染(白屏和undefinederror);我想到了两个方案:让接口同步,在beforeCreate或者created钩子中同步获取数据字典接口,并保证在$mount中可以拿到注册的filter,保证时序,但是这样会阻塞mount和延长白屏时间,不推荐;将filter的注册改为异步,获取到filter后通知renderwatcher自行更新,这样就可以使用Vue自带的响应式更新视图不??会阻塞渲染,所以下面初步采用这种方式。2、实现因为filter属于asset_types,所以关于asset_types在vue实例中的访问链有如下结论;具体代码实践可以参考:Codepen-filtertestasset_types包括filters、components、directives,后面的asset_types全部替换为之前的item子组件中的asset_types不能访问父组件中的asset_types,但是可以访问全局注册的asset_types挂载在$root.$options.asset_types.__proto__上,对应源码src/core/util/options。js全局注册方法Vue.asset_types,比如Vue.filters注册的asset_types,会挂载到根实例(其他实例的$root)的$options.asset_types.__proto__上,并被所有Vue实例继承createdlater,也就是以后创建的所有Vue实例都可以访问组件的slot。作用域仅限于定义的地方,即在定义的组件中,不能访问父组件的asset_types,但可以访问全局定义的。asset_types是一样的,因为main.js中new的Vue()实例是root实例,所以里面注册的asset_types会挂载到$root.$options.asset_types上,而不是$root.$options.asset_types.__proto__上以上结论,就可以开始编码了~2.1使用根组件的过滤器,所以我首先考虑的是将要注册的过滤器挂载到根组件上,这样其他组件就可以通过访问$root获取注册的过滤器.此处实现:registerfilter'sjs//utils/filtersimport*asApifrom'api'/***获取并注册过滤器*在$root.$options.filters上注册,而不是在$root.$options.filters.__proto__上注册*这里注意这是一个vue实例,需要用call或者apply来调用*@returns{Promise}*/导出函数registerFilters(){重新turnApi.sysParams()//获取数据字典的Api,返回一个promise.then(({data})=>{Object.keys(data).forEach(T=>this.$set(this.$root.$options.filters,T,val=>{consttar=data[T].find(item=>item['paramValue']===val)returntar['paramDesc']||''}))返回数据}).catch(err=>console.error(err,'inutils/filters.js'))}这使得根组件上的过滤器具有响应性,并且在渲染时因为在rootFilters方法中$root.$options.filters在created中已经响应,在其中访问,所以当异步获取的数据赋值给$root.$options.filters时,会触发该组件的renderwatcher的重新渲染。这时候获取rootFilters方法就可以获取到过滤器了;那这里为什么不直接用Vue.filter方法注册呢。因为Object.defineProperty无法监听__proto__上的数据变化。全局的Vue.filter在根组件$root.$options.asset_types.__proto__上注册了过滤器,所以它的变化无法被响应。这里的代码还可以进一步完善,但是这种方法存在一定的问题。首先,这里使用了Vue.util上的unstable方法。另外随处可??见this.$root.$options用于访问Vue实例的内部属性,读起来不是很文明和混乱。所以当这个项目做完等待测试的时候,我想了想,谁说filter一定要放在filters-。-,也可以使用mixin来实现2.2使用mixin使用mixin要注意一点,因为在vue中,data中所有以_和$开头的变量都是作为内部保留变量使用的。没有代理到当前实例,所以this._xx不能直接访问,需要通过this.$data._xx访问。//mixins/sysParamsMixin.jsimport*asApifrom'api'exportdefault{data(){return{_filterFunc:null,//过滤函数_sysParams:null,//获取数据字典_sysParamsPromise:null//获取之后返回的sysParamsPromise}},methods:{/*将过滤器注册到_filterFunc*/_getSysParamsFunc(){const{$data}=thisreturn$data._sysParamsPromise||($data._sysParamsPromise=Api.sysParams().then(({data})=>{this.$data._sysParams=datathis.$data._filterFunc={}Object.keys(data).forEach(paramKey=>this.$data._filterFunc[paramKey]=val=>{consttar=data[paramKey].find(item=>item['paramValue']===val)returntar&&tar['paramDesc']||''})返回数据}).catch(err=>console.error(err,'insrc/mixins/sysParamsMixin.js')))},/*通过键值获取单个过滤器*/_rootFilters(val,id='SEX_TYPE'){constfunc=this.$data._filterFuncconstmth=func&&func[id]returnmth&&mth(val)||val},/*getdatadictionary*/_getSysParams(){returnthis.$data._sysParams}}}到这里,Api的promise就保存下来了。如果在别处使用,会直接返回已经resolve的promise,不需要再次请求数据。另外为了方便其他实例访问,这里挂载在组件的root上。如何在我们的根组件中使用它://src/main.jsimportsysParamsMixinfrom'mixins/sysParamsMixin'newVue({el:'#app',mixins:[sysParamsMixin],render:h=>h(App),})在需要过滤器的组件中:这里不仅注册了过滤器添加了,同时也暴露了数据字典,方便一些地方的列表展示。毕竟这在实际项目中是很常见的场景。当然用vuex更好,但是我个人觉得这里的场景没必要用vuex。如果有更好的方法可以一起讨论~