微信小程序全局Toast尝试思路

·  阅读 224
原文链接: www.wxapp-union.com

前言

toast 或 message 组件,基本是每个项目都会使用到的。

在Vue、React中它们都是组件,而我们习惯将这类型的组件处理全局Api,以避免每个页面都要写 template 以及 data,更加方便使用

而在小程序中,自定义组件 Toast 也有同样的问题[wx.showToast() 这个 api 只有 success/loading 两种方式,无法满足我们的需求]

原版是这样…


                                                        
<td-toast is-show="{{$toast.show}}" icon="{{$toast.icon}}" text="{{$toast.text}}"></td-toast>
{
 //data
 data:{
   $toast: {
     show: false,
     text: '',
     icon: ''
   }
 },
 //methods
 toast(text, icon = '', times = 2000, cb) {
   //...省略其他逻辑
   this.setData({
     $toast: {
       show: true,
       text: text,
       icon: icon
     }
   });
   //...省略其他逻辑
 },
 clearToast() {
   //...省略其他逻辑
   this.setData({
     $toast: {
       show: false,
       text: '',
       icon: ''
     }
   });
   //...省略其他逻辑
 }
}


<div class="md-section-divider"></div>

 复制代码


问题

我们的业务基本上每个页面都会有 toast。这意味着每个 page 都得去定义wxml、data、toast()、clearToast() ?
我们需要个 wx.showToast() 一样的方式去调用

方案

我们先处理wxml的问题,发现这个问题并不好解决,我们没有办法动态的创建标签。
那么只好先采用莽夫方案了: 创建一个 plugin.wxml(用来存放所有的全局公用模板)
每个page 都会 include 这个 plugin.wxml

//plugin.wxml


                                                        
<!-- 目前只有toast -->
<td-toast is-show="{{$toast.show}}" icon="{{$toast.icon}}" text="{{$toast.text}}"></td-toast>


<div class="md-section-divider"></div>

 复制代码


接下来处理js 的问题,来个 plugin.js 来做plugin.wxml对应的数据逻辑

//plugin.js


                                                        
export default {
 $data: {
   $toast: {
     show: false,
     text: '',
     icon: ''
}
},
//methods
 toast(text, icon = '', times = 2000, cb) {
//...省略其他逻辑
this.setData({
     $toast: {
       show: true,
       text: text,
       icon: icon
}
});
//...省略其他逻辑
},
 clearToast() {
//...省略其他逻辑
this.setData({
     $toast: {
       show: false,
       text: '',
       icon: ''
}
});
//...省略其他逻辑
}
};


<div class="md-section-divider"></div>

 复制代码


能看出来其实plugin.js就是page的内容,那下一步需要做的就是吧plugin.js的内容注入到每个page中。

这时候尝试着定义了一个inject.js


                                                        
import plugin from './plugin';
function inject(page) {
for (let key in plugin.$data) {
if (Object.prototype.hasOwnProperty.call(plugin.$data, key)) { //过滤
     page.data[key] = plugin.$data[key];
}
}
const obj = Object.assign({}, plugin, page);
return obj;
}

export default (page) => {
return inject(page)
}


<div class="md-section-divider"></div>

 复制代码


这时候有了个 inject方法能把 plugin.js合并到page了。
只需要在page里加上inject方法就好了


                                                        
// pages/demo/index.js
import inject from './../plugin/inject';
Page(
// 注入 plugin
 inject({
   data: {},
   onLoad: function(options) {},
   onReady: function() {},
   onShareAppMessage: function() {
// plugin.js中定义的 toast()
this.toast('分享')
}
})
);


<div class="md-section-divider"></div>

 复制代码


到这里就差不多基本完成了,当然还有一些问题,比如说json配置中的 usingComponents 字段
这些问题目前也没有找到好的解决方式,
目前正在写一个构建工具来自动处理 json配置,当然主要是用来单文件开发,处理不能使用 npm 的问题,附加支持postcss

这是题外话了,上面的inject 我们还能用来做一些其他的事情,比如对page的hook

例如:增加onLogin回调


                                                        
//plugin.js
export default {
 $data: {
   $toast: {
     show: false,
     text: '',
     icon: ''
}
},
/**
  * 生命周期函数--监听onLoad
  */
 loadHooker: function(onLoad, onLogin) {
return function(option) {
// 不管三七二十一 先调了onLoad再说
     onLoad.call(this, option);
const app = getApp();
if (app.globalData.userInfo) { // 已经登录 
       setTimeout(()=>{
this.globalData = app.globalData;
if (onLogin) { 
           onLogin.call(this, option, app.globalData.userInfo);
}
},0)
} else { //没有登录 异步=》onLogin
       app.userInfoReadyCallback = json => {
this.globalData = app.globalData;
if (onLogin) {
           onLogin.call(this, option, app.globalData.userInfo);
}
};
}
};
},
 toast(text, icon = '', times = 2000, cb) {
//...省略其他逻辑
},
 clearToast() {
//...省略其他逻辑
}
};


//inject.js
import plugin from './plugin';

function inject(page) {
for (let key in plugin.$data) {
if (Object.prototype.hasOwnProperty.call(plugin.$data, key)) { //过滤
     page.data[key] = plugin.$data[key];
}
}
//新回调
const onLoadHooker = plugin.loadHooker(page.onLoad, page.onLogin);
const obj = Object.assign({}, plugin, page);
 obj.onLoad = onLoadHooker;// hookonLoad
return obj;
}

export default (page) => {
return inject(page)
}


<div class="md-section-divider"></div>

 复制代码


如果fetch接口依赖于用户信息


                                                        
Page(
 inject({
   data: {},
   onLoad: function() {},
   onLogin: function(options, userInfo) {
this.toast('拿到用户信息')
this.fetch(userInfo.openid);
}
})
)

 复制代码


好吧 第一次写这种文章 挺生疏,有什么错误或者有更好的思路希望能指出

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改