简介
上一篇介绍的查询模块数据流发现了一些问题:https://www.cnblogs.com/cjc-0313/p/16810460.html
- 当用户没有操作视图,而是通过路由前后跳转时,并不会触发视图层数据的更新,因此需要调整部分数据流向
- 路由参数和 API 参数的生成过程存在重复,需要合并;这是由一开始对生成过程的权责分配不清而导致的逻辑混乱
优化版
先上图:
与旧版相比,本此优化主要调整了“视图层 —— 路由 —— 本地数据 —— API 参数”四者之间的数据流向,不影响 API 层与服务器之间的交互。虚线部分仍是不需要处理路由参数变化时的数据流向
这是旧版的数据流向:
上图与示例代码其实有些区别,代码中是从视图层获取数据后保存到本地的同时重新生成路由参数,并不是从本地数据获取更新并生成路由参数。
而新版中改为:用户操作使得参数发生变化时,在事件处理其中只将更新值传给路由对象,而本地数据变量是从路由对象获取更新后的值,以便更新视图上的数据;同时,仍是通过监听路由的变化,自动触发生成新的 API 参数并调用相关接口。API 参数这里可以根据需要,更换成自动从路由对象获取新的值,或者像这里介绍的一样,在路由对象的侦听器中更新。这里采用在侦听器中更新的主要的目的是通过同步模式确保 API 参数能在发送请求前完成,因为 vue 的计算属性生成新的 API 参数并不能保证在侦听器之前执行完毕(注意是完毕,也就是生成了完整的新参数,而不是开始执行生成过程)。
示例代码
由于调整了数据流向,因此涉及到上述四部分的代码都需要调整。同时,将公共方法提取到外部,通过 mixins 导入方便复用。
searchParamsMixins.js(核心)
import isObjEmpty from '@/utils/utils/isObjEmpty';
const searchParamsMixins = {
methods: {
//#region 工具 --
// params 或 query 对象可能有属性,但值为空,所以还需要额外判断
isEmpty(obj) {
return isObjEmpty(obj);
},
//#endregion --
//#region 路由参数处理 --
// 用户操作视图后,调用事件处理器方法,获取更新
// 将更新的内容以特定格式传入处理方法,转化为新的路由参数对象,并 push 到路由器对象
// watch 侦听路由对象,一旦改变,立即获取路由中的参数,自动生成 API 参数并请求服务器
/* 参数示例:
* 全保留 { queryChanges: 'allReserve', paramsChanges: 'allReserve' }
* 只保留 query 或 只保留 params,保留的给一个 'allReserve',清空的给 undefined 或不给
* 全清空 {}
* 对 query 或 params 单独增、删、改,保留的参数给一个 'allReserve',要增加的参数的属性直接给值,删除的给 undefined,修改的也是直接给值
*/
/**
*
* @param {从路由对象获取旧的参数} oldParams
* @param {根据用户输入,生成参数的更新值(不是完整的新参数)} paramsChanges
* @param {参数的默认值,可选} defaultParams
* @returns 返回更新后的完整 query 或 params 对象,用于路由显示参数
*/
handleUpdateParams(oldParams, paramsChanges, defaultParams = {}) {
let newParams = {}; // paramsChanges === undefined
if (paramsChanges === 'allReserve') {
newParams = oldParams;
} else if (!this.isEmpty(paramsChanges)) {
for (const key in paramsChanges) {
// 将同名参数重置为初始值
if (Object.hasOwnProperty.call(paramsChanges, key)) {
if (
Object.hasOwnProperty.call(oldParams, key) &&
paramsChanges[key] === undefined
)
newParams[key] = defaultParams?.[key]
? defaultParams[key]
: undefined;
else {
newParams[key] = paramsChanges[key];
}
}
}
}
return newParams;
},
/**
*
* @param {查询组件在 router 中定义的 name} SearchRouterCompoName
* @param {$route 对象中 query 和 params 对象的属性的变化} changes
*/
handleUpdateRouteParams(SearchRouterCompoName, { queryChanges, paramsChanges }) {
if (SearchRouterCompoName) {
let location = { name: SearchRouterCompoName };
let newQuery = this.handleUpdateParams(this.$route.query, queryChanges),
newParams = this.handleUpdateParams(this.$route.params, paramsChanges);
if (!this.isEmpty(newQuery)) location.query = newQuery;
if (!this.isEmpty(newParams)) location.params = newParams;
this.$router.push(location);
}
},
//#endregion --
//#region 生成请求参数 --
// 为了确保在请求之前获取到最新的路由参数,此处通过方法在请求函数前同步执行,而不是通过计算属性获取
setupSearchParams(route) {
return Object.assign({}, route.params, route.query);
},
//#endregion --
}
}
export default searchParamsMixins;
isObjEmpty.js
import { isEmpty } from 'lodash'
function isObjEmpty(obj) {
if (isEmpty(obj)) return true;
// lodash.isEmpty 无法排除对象有属性,但属性值为空的情况
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
if (obj[key]) return false;
}
}
return true;
}
export default isObjEmpty;
业务组件中使用
将定义好的方法作为业务组件的 mixins,然后在本地重新包装一个传入查询组件 name 值的方法。
对于用户操作后从视图层返回的数据,在本地的事件处理方法中,仅将更新作为参数传递给 handleSearchParamsChaned 方法。
import searchParamsMixins from '@/utils/mixins/searchParams';
...
mixins: [searchParamsMixins],
...
methods: {
// 从外部混入,本地只包装一个传入路由组件名称的方法
handleUpdateRoute(changes) {
this.handleUpdateRouteParams('Search', changes);
},
// 增加或修改 query 参数;params 同理
handleUpdateCurrentName(name) {
this.handleUpdateRoute({
queryChanges: { name: name},
paramsChanges: 'allReserve', // 由于只有 query 的变化,params 的属性全部保留
});
},
// 更新数组类型的参数
handleUpdateCurrentTags(tagId, tagValue) {
const oldTags = this.$route.query.tags;
let newTags = oldTags instanceof Array ? [].concat(oldTags ) : [];
const findIdx = this.oldTags.findIndex(oldTagVal => oldTagVal === tagValue);
if (findIdx > -1) {
newTags [findIdx ] = tagValue;
} else {
newTags .push(tagValue);
}
this.handleUpdateRoute({
queryChanges: { newTags },
paramsChanges: 'allReserve',
});
},
// 显示全部结果(无参查询)
handleGetAllResult() {
this.handleUpdateRoute({});
},
},
...
考虑到参数的变化规则不可穷举,因此将变化的处理过程放在通用方法之外,由业务组件负责。通常会放在事件的 handler 中,或者多处 handler 存在相同变化的,可以抽象为单独的函数,但仍然是在业务组件之中。
总结
本次升级主要是解决了开头提到的两个问题,让数据流向的规则更加简化,通过降低模块的重复与耦合来提升模块协作能力,提高程序的复用性和稳定性,更加方便开发和调试,略微加深了对于软件分层、逻辑复用、权责分配等方面的理解。
标签:key,paramsChanges,查询,参数,模块,params,数据流,query,路由 From: https://www.cnblogs.com/cjc-0313/p/16837624.html