简介
在项目中使用 i18n 统一处理国际化。当用户点击菜单(二级菜单被选中)时,将二级模块为当前菜单项的对应变量设置到状态库;之后新页面加载时,会从状态库获取该变量,并作为根路径(模块名)寻找对应的变量,i18n.$$() 方法参数格式为:subModuleName.特定的i18n变量路径
,示例:permissionManagement.fieldKeys.id
,其中 fieldKeys
表示表格列或表单字段。
遇到的问题与分析
问题 1:导航完成,视图结构已更新,但变量未改变
使用中发现,每次导航结束,新页面上的 subModuleName
还是上一次设置的值,并没有更新,直到再次点击才会更新。这是因为 DOM 事件属于宏任务,执行较慢,而 VUe-Router 的导航可能是同步任务或微任务(具体是哪种需要看源码才能分析,但优先级肯定大于宏任务),执行比起 DOM 事件要更快,因此新页面无法保证一定能在状态库更新后才加载。
一开始希望使用“延迟导航”的模式,但搜索了 element 的文档,发现并没有提供延迟导航的功能,因此,考虑不使用 el-menu 的自动导航,而是在状态库更新方法调用之后,执行 router.push() 编程式导航。
问题 2:使用编程式导航后,问题依旧存在
经过试验,发现二级菜单还好,但一级菜单切换时,由于这里还存在一层异步的数据更新,因此更新后的视图还是无法确保数据更新;同时,为了配合这里的异步数据更新,路由会变慢,可能带来不好的用户体验。
因此,经过思考后,决定放弃这种模式,改为将一、二级菜单置于同个业务组件下(原先是分开的,顶部一级菜单,左边二级菜单,而且两者属于同级的兄弟组件,所以一开始需要使用异步的数据更新到状态库,使得两者间可以通信),由该组件负责统一管理两级关联菜单的“同步”更新。
同时,其他的业务组件改为使用本地的 subModuleName
变量,不再维护全局状态,反正只要确保当前组件环境下只有一个全局常量即可;而一些通用组件则通过 props 由业务组件传入 subModuleName
,使得通用组件也能脱离全局的公共状态。
总结
一开始对于两级菜单的认知错误,没有将其作为一个整体进行维护,导致出现不必要的设计,增加了实现和维护的困难度。
对于这类关联紧密的兄弟组件,应当作为一个整体,由父组件统一维护其状态,避免出现需要异步通信的情况。而对于关系不大的组件,使用异步的全局状态库通信则更为方便,比如此处的两级菜单与其他页面。
还有一点是关于 i18n
:在视图上使用 i18n
的 $t()
方法时,若参数为响应式对象,则页面切换导致 moduelName
、 subModuleName
之类的全局状态变化时,可能会导致页面短暂出现原参数字符串。
比如使用本文一开始的异步通信模式开发时,由于路由切换比状态更新更快,因此 subModuleName
还是上一次的状态,而页面已经切换了,导致 subModule
不是当前 moduleName
下的属性,因此无法找到对应的 i18n
文本,则此时 $t()
方法会显示参数拼接而成的字符串,带来不好的用户体验。
因此,需要注意在视图上使用响应式对象作为 $t()
参数的时机,若该对象是由当前组件内部的全局常量,倒是不会出现原参数字符串;而对于上述的两级联动菜单,由于 moduleName
参数会随着一级菜单更新,则最好是在给 表示二级菜单列表的响应式对象 赋值前,先使用 $t()
过滤,避免视图触发响应式更新。