背景
小程序凭借着“用完即走”的优势,获得了广大用户的青睐,国内的小程序主要有微信小程序、支付宝小程序、京东小程序和抖音小程序。这篇文章主要介绍一种小程序页面间传值和互操作的设计思路,我是基于下面2个场景非常希望有这样的一个机制:
在资料列表页中,选择一个资料项跳转到资料编辑页去编辑,我们希望携带资料项到编辑页中
在编辑页中,修改并提交资料项到后台保存成功后,返回到资料列表页中,我们希望资料列表页可以感知到修改项进而展示修改好的数据
对于问题的思考
在开发前端应用中,我们都会遇到上面的问题,我们可能会想到用父子组件来解决,在SPA页面中,我们确实是这样解决的,可是在小程序中它们之间是兄弟关系不是父子关系或者祖孙关系。
通过URL参数传资料项id到编辑页面,编辑页面在显示前去服务端取出资料项来编辑,编辑页面关闭后列表页在显示前去服务端重新加载数据。
把数据放到Redux这样的缓存中,编辑页面根据资料项id取到资料编辑,编辑页提交成功后更新缓存,由于列表页订阅了这些数据,列表页会自动刷新
使用消息事件总线的方式,事件的触发和接收需要进行巧妙的设计,有的小程序框架提供了相关的基础设施,会简化使用
上面几个方案可以部分或者全面地解决提出的问题,上面的几种方案各有优缺点,适合自己项目的方案可以了。我这里补充一种思路,利用在路由表的数据域中加入属性来解决提出的问题,这里把这个功能设计成一个类并提供简洁的方法以供使用。
实现代码
import Taro from '@tarojs/taro'
export type ToParams = {
url: string;
getData?: ()=>any;
callBack?: (par: any)=>void;
};
export default class AdvNavigation{
private static ToExtAttrName:string = "zzz";
static to(params: ToParams){
const {url} = params;
Taro.navigateTo({url,
success: function(){
if(!!params){
const pages:Taro.Page = Taro.getCurrentPages();
const curPage = pages[pages.length-1];
const index = url.indexOf("?");
const url2:string = index==-1? url: url.slice(0, index);
curPage['data'][`${AdvNavigation.ToExtAttrName}-${url2}`] = params;
console.log("curpage:", curPage);
}
}
})
}
static getParams(): ToParams{
const pages:Taro.Page = Taro.getCurrentPages();
const curPage = pages[pages.length-1];
if(pages.length < 2){
return {url: curPage['route']||''};
}
const prePage = pages[pages.length-2];
const attr = `${AdvNavigation.ToExtAttrName}-${curPage['route']}`;
return prePage['data'][attr];
}
static getAndClearParams(): ToParams | undefined{
const pages:Taro.Page = Taro.getCurrentPages();
const curPage = pages[pages.length-2];
if(pages.length < 2){
return {url: curPage['route']||''};
}
const prePage = pages[pages.length-2];
const r = prePage['data'][`${AdvNavigation.ToExtAttrName}-${curPage['route']}`];
delete prePage['data'][`${AdvNavigation.ToExtAttrName}-${curPage['route']}`];
return r;
}
}
使用
用户在个人页中点击登录跳转到登录页面的代码
AdvNavigation.to({
url: "/pages/login/index",
callBack: (e: any) => {
loadUser();
loadAvator();
},
});
在登录页中,为了减少不相关的代码干扰,特只截取了相关的代码
const [params, setParams] = useState<ToParams>({ url: "" });
useEffect(() => {
// 把列表页面的互操作保存起来
setParams(AdvNavigation.getParams()||{url:""});
}, []);
const handleLogin = () => {
console.log(phone, pwd, code);
const reqDto: LoginRequestDto = {
username: phone,
password: pwd,
};
login(reqDto).then((res: ResultDto<LoginResponseDto>) => {
if (res.success) {
Taro.navigateBack().then(() => {
if (params.callBack) {
// 通知列表页
params.callBack(undefined);
}
});
} else {
Taro.showToast({ icon: "error", title: res.errorMessage || "" });
}
});
};
小结
在上面的使用例子中,列表页调用函数to打开编辑页面,编辑页面用getParams获取列表页传过来的信息,在登录成功后通知个人页加载用户和头像信息,可以看出使用上也比较简洁。