这个问题是在下在做一个vue项目中遇到的实际场景,这里记录一下我遇到问题之后的思考和最后怎么解决的(老年程序员记性不好 -。-)。
问题是这样的:
页面从后台拿到的数据是由0、1之类的key,而这个key代表的value比如0-女、1-男的对应关系是要从另外一个数据字典接口拿到的;
类似于这样的api:
{
"SEX_TYPE": [
{ "paramValue": 0, "paramDesc": "女" },
{ "paramValue": 1, "paramDesc": "男" }
]
}
那么如果view拿到的是0,就要从字典中找到它的描述女并且显示出来;下面故事开始了
有人说,这不是过滤器 filter 要做的事么,直接Vue.filter不就行了。
然而问题是这个filter是要等待异步的数据字典接口返回之后才能拿到,如果在$mount的时候这个filter没有找到。
那么就会导致错误影响之后的渲染(白屏并报undefined错);
我想到的解决方法有两个:
因为filter属于 asset_types ,关于在Vue实例中asset_types的访问链有以下几个结论;
具体代码实践可以参考: Codepen - filter test
asset_types包括filters、components、directives,以下所有的asset_types都自行替换成前面几项
子组件中的asset_types访问不到父组件中的asset_types,但是可以访问到全局注册的挂载在$root.$options.asset_types.proto上的asset_types,这里对应源码 src/core/util/options.js
全局注册方法Vue.asset_types,比如Vue.filters注册的asset_types会挂载到根实例(其他实例的$root)的$options.asset_types.proto上,并被以后所有创建的Vue实例继承,也就是说,以后所有创建的Vue实例都可以访问到
组件的slot的作用域仅限于它被定义的地方,也就是它被定义的组件中,访问不到父组件的asset_types,但是可以访问到全局定义的asset_types
同理,因为main.js中的new Vue()实例是根实例,它中注册的asset_types会被挂载在$root.$options.asset_types上而不是$root.$options.asset_types.proto上
根据以上几个结论,可以着手coding了~
2.1 使用根组件的filters 因此首先我考虑的是把要注册的filter挂载到根组件上,这样其他组件通过访问$root可以拿到注册的filter。
这里的实现:
<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>
注册filter的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'))
}
这样把根组件上的filters变为响应式化的,并且在渲染的时候因为在rootFilters方法中访问了已经在created中被响应式化的$root.$options.filters。
所以当异步获取的数据被赋给$root.$options.filters的时候,会触发这个组件render watcher的重新渲染,这时候再获取rootFilters方法的时候就能取到filter了;
那这里为什么不用Vue.filter方法直接注册呢。
因为Object.defineProperty不能监听proto上数据的变动。
而全局Vue.filter是将过滤器注册在了根组件$root.$options.asset_types.proto上,因此其变动不能被响应。
这里的代码可以进一步完善,但是这个方法存在一定的问题,首先这里使用了Vue.util上不稳定的方法。
另外在使用中到处可见this.$root.$options这样访问vue实例内部属性的情况,不太文明,读起来也让人困惑。
因此在这个项目做完等待测试的时候我思考了一下,谁说过滤器就一定放在filters里面 -。-,也可以使用mixin来实现嘛
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 { $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的promise保存下来,如果其他地方还用到的话直接返回已经是resolved状态的promise,就不用再次去请求数据了。
另外为了在其他实例中也可以方便的访问,这里挂载在根组件上。
那在我们的根组件中怎么使用呢:
// 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
在react中是单向数据绑定,而在vue和augular中的特色是双向数据绑定。为什么会选择两种不同的机制呢?我猜测是两种不同的机制有不同的适应场景,查了一些资料后,总结一下。
双向数据绑定是非常重要的特性 —— 将JS模型与HTML视图对应,能减少模板编译时间同时提高用户体验。我们将学习在不使用框架的情况下,使用原生JS实现双向绑定 —— 一种为Object.observe
js判断数据类型的多种方法,主要包括:typeof、instanceof、 constructor、 prototype.toString.call()等,下面就逐一介绍它们的异同。
由于js为弱类型语言拥有动态类型,这意味着相同的变量可用作不同的类型。 typeof 运算符返回一个用来表示表达式的数据类型的字符串,目前typeof返回的字符串有以下这些: undefined、boolean、string、number、object、function、“symbol
在js中我们直接这样写typeof obj===‘object’有什么问题呢?发现Array, Object,null都被认为是一个对象了。如何解决这种情况,能保证判断obj是否为一个对象
js要处理十六进制,十进制,字符之间的转换,发现有很多差不多且书写不正确的方法.一个一个实践才真正清楚如何转换,现在来记录一下它们之间转换的方法。
奇数和偶数的判断是数学运算中经常碰到的问题,这篇文章主要讲解通过JavaScript来实现奇偶数的判断。2种判断方法:求余% 、&1
质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。比如100以内共25个,js实现代码如下。
JavaScript自动类型转换真的非常常见,常用的一些便捷的转类型的方式,都是依靠自动转换产生的。比如 转数字 : + x 、 x - 0 , 转字符串 : \\\"\\\" + x 等等。现在总算知道为什么可以这样便捷转换。
XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。 这篇文章主要介绍Js中实现XML和String相互转化
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!