痛点 & 目标
在微信小程序中进行页面跳转时高度依赖文件路径,但当我们的目录层级过深,或者参数过多时,就会难以维护:
wx.navigateTo(`/pages/mine/address/address?id=${this.data.id}&pageType=EDIT&jumpBack=Y`);
仔细分析,就会发现有以下几个痛点:
- 路由路径和目录结构绑定,任何对目录结构的修改都需要对项目中全部路由路径进行修改
- 参数可读性差,难以维护
而我们的目标是:
- 解藕路由系统和目录结构,尽可能降低目录结构修改对路由产生的影响
- 优化参数的传递方式,尽可能摆脱难以维护的字符串形式
- 参数有迹可循,在不使用 Typescript 等工具时,尽可能让数据流清晰
路由系统实现
从 Vue Router 中汲取灵感,我们可以把所有的页面映射为一个对象,对象的 Key 类似 Vue Router 中的 name,路由跳转时只使用 key 值即可。当目录结构发生改变时只需要修改键值对中的 Value 即可,页面中的代码逻辑无需任何改动。新建 config/routes.js
。
export const Routes = {
Index: '/pages/index/index',
Mine: '/pages/mine/mine',
About: '/pages/static/about/about',
Register: '/pages/register/register',
Address: '/pages/mine/address/address'
...
};
新建 utils/route.js
。
import { Routes } from '../config/routes';
/**
* 构建 url 参数
* @param {string|object|null} params
*/
export const buildParams = (params) => {
let fullParamStr;
if (!params) {
fullParamStr = '';
} else if (typeof params === 'string') {
fullParamStr = params;
} else {
fullParamStr = Object.keys(params)
.map((key, index) => {
const prefix = index === 0 ? `?` : `&`;
return `${prefix}${key}=${params[key]}`;
})
.join('');
}
return fullParamStr;
};
/**
* 高阶函数构建路由跳转模块
* @param {Function} method
*/
const generateRoute = (method) => (routeName, params, events) => {
const routePath = Routes[routeName];
if (!routePath) {
console.error(`The RouteName must be one of: ${Object.keys(Routes).join(', ')}`);
return;
}
return new Promise((resolve, reject) => {
const options = {
url: routePath + buildParams(params),
success: resolve,
fail: reject,
};
if (events) options.events = events;
method(options);
});
};
export const switchTab = generateRoute(wx.switchTab);
export const reLaunch = generateRoute(wx.reLaunch);
export const navigateTo = generateRoute(wx.navigateTo);
export const redirectTo = generateRoute(wx.redirectTo);
export default { switchTab, reLaunch, navigateTo, redirectTo };
使用示例:
import { navigateTo, reLaunch } from '../utils/route';
// 简单跳转
navigateTo('Register');
// 传入字符型作为参数
reLaunch('Index', `?withMessage=${this.data.message}`);
// 传入对象作为参数。同时支持 eventChannel 配置
navigateTo(
'Address',
{ id: this.data.id, pageType: 'EDIT', jumpBack: 'Y' },
// 需要在 Address 中触发 eventChannel.emit('edited')
{ edited: this.refreshAddress.bind(this) },
);
如何接收参数
为了让路由系统中的数据流向保持清晰,因此我们应当约定:每个页面的参数都使用解构语法。
// ok
onLoad(options) {
}
// better
onLoad({ id, pageType = 'CREATE', jumpBack = 'Y' }) {
}