wxapp-boilerplate
wxapp-boilerplate copied to clipboard
希望能提供对支付宝小程序js api转换的处理
类似这个项目https://github.com/douzi8/wxToAlipay/tree/8047bb809f32a2c882f24a5584b264018e91b1ab/lib/js
能自定义mapping,封装微信和小程序的差异
好
//src/shim.js
function noop() {}
function paramsMap(options, maps = {}) {
let params = {};
for (let key in options) {
let myKey = maps.hasOwnProperty(key) ? maps[key] : key;
params[myKey] = options[key];
}
return params;
}
// 修改 api params 格式
function setStorageSync(key, data) {
return {
key,
data,
};
}
function removeStorageSync(key) {
return {
key,
};
}
function getStorageSync(key) {
return my.getStorageSync({
key: key,
}).data;
}
function previewImage(options) {
let params = paramsMap(options);
let current = params.current;
if (current) {
current = options.urls.indexOf(current);
}
if (current === -1 || !current) {
current = 0;
}
params.current = current;
return params;
}
function makePhoneCall(options) {
return paramsMap(options, {
phoneNumber: 'number',
});
}
function request(options) {
let params = paramsMap(options, {
header: 'headers',
});
let success = params.success || noop;
params.success = function (res) {
let result = paramsMap(res, {
headers: 'header',
status: 'statusCode',
});
success(result);
};
return params;
}
/**
* wx success里面的system字段为'Android 6.0.1'
*/
function getSystemInfo(options) {
let params = paramsMap(options);
let success = params.success || noop;
params.success = function (res) {
success(getSystemInfoSync(res));
};
return params;
}
function getSystemInfoSync(res) {
res.system = res.platform + ' ' + res.system;
// 支付宝小程序windowHeight可能拿到0
if (!res.windowHeight) {
res.windowHeight = parseInt(res.screenHeight * res.windowWidth / res.screenWidth, 10) - 40;
}
return res;
}
/**
* wx模态弹窗不同的参数对应到支付宝confirm和alert API
*/
function showModal(options) {
let params = paramsMap(options);
let showCancel = params.showCancel;
if (typeof showCancel === 'undefined') {
showCancel = true;
}
// 确认框
if (showCancel) {
params.confirmButtonText = params.confirmText;
params.cancelButtonText = params.cancelText;
}
else {
// 提醒框
params.buttonText = params.confirmText;
}
my[showCancel ? 'confirm' : 'alert'](params);
}
/**
* 参数{icon: 'loading'} 无法成功映射,建议不要使用
*/
function showToast(options) {
let params = paramsMap(options, {
title: 'content',
icon: 'type',
});
return params;
}
/**
* sucess回调没有取消操作
* 点击取消或蒙层时,回调fail, errMsg 为 "showActionSheet:fail cancel"
*/
function showActionSheet(options) {
let params = paramsMap(options, {
itemList: 'items',
});
let success = params.success || noop;
let fail = params.fail || noop;
params.success = function ({
index: tapIndex,
}) {
if (tapIndex === -1) {
fail({
errMsg: 'showActionSheet:fail cancel',
});
}
else {
success({
tapIndex,
});
}
};
return params;
}
// 此函数可以根据业务自行定制, 参数无法统一映射
function login(options) {
let params = paramsMap(options);
let success = params.success || noop;
params.scopes = 'auth_user';
params.success = function (res) {
success({
code: res.authCode,
});
};
return params;
}
// 此函数可以根据业务自行定制, 参数无法统一映射
function requestPayment(options) {
let params = paramsMap(options, {
alipay_trade_body: 'orderStr',
});
let success = params.success || noop;
let fail = params.fail || noop;
params.success = function (res) {
if (res.resultCode === 9000) {
success();
}
else {
fail();
}
};
return params;
}
function showLoading(options) {
let params = paramsMap(options, {
title: 'content',
});
return params;
}
const FUNCTION_MAP = {
login: 'getAuthCode',
request: 'httpRequest',
setNavigationBarTitle: 'setNavigationBar',
setNavigationBarColor: 'setNavigationBar',
requestPayment: 'tradePay',
};
// // 处理
const shim = {};
const myShim = () => {
for (let key in my) {
if (my.hasOwnProperty(key) && typeof my[key] === 'function') {
let result = FUNCTION_MAP[key];
let apiName = result || key;
let paramsFunc = null;
let func = null;
switch (key) {
case 'login':
paramsFunc = login;
break;
case 'showActionSheet':
paramsFunc = showActionSheet;
break;
case 'showToast':
paramsFunc = showToast;
break;
case 'showLoading':
paramsFunc = showLoading;
break;
case 'request':
paramsFunc = request;
break;
case 'makePhoneCall':
paramsFunc = makePhoneCall;
break;
case 'previewImage':
paramsFunc = previewImage;
break;
case 'setStorageSync':
paramsFunc = setStorageSync;
break;
case 'removeStorageSync':
paramsFunc = removeStorageSync;
break;
case 'getSystemInfo':
paramsFunc = getSystemInfo;
break;
case 'requestPayment':
paramsFunc = requestPayment;
break;
case 'showModal':
paramsFunc = showModal;
break;
case 'getStorageSync':
func = getStorageSync;
break;
case 'getSystemInfoSync':
func = getSystemInfoSync;
break;
// return getSystemInfoSync(my.getSystemInfoSync());
}
shim[key] = (options, ...params) => {
if (func) {
console.log(`调用 api:${apiName}, params:${JSON.stringify(params)}`);
return func(options, ...params);
}
else if (paramsFunc) {
console.log(`调用 void api:${apiName}, params:${JSON.stringify(paramsFunc(options, ...params))}`);
return my[apiName](paramsFunc(options, ...params));
}
else {
console.log(`调用 void api:${apiName}, params:${JSON.stringify(options, ...params)}`);
return my[apiName](options, ...params);
}
};
}
}
};
myShim();
export {
shim as default,
};
然后在具体页面引用 import shim from 'shim.js';
,将 wx.
调用改为 shim.
是可以在编译为支付宝小程序时正确执行,不需要改动调用参数
但我想在webpack打包成支付宝小程序时引用 shim.js
,并且将 wx
改为 shim
//webpack.config.babel.js
plugins: [
new EnvironmentPlugin({
NODE_ENV: 'development',
}),
new DefinePlugin({
__DEV__: isDev,
__WECHAT__: isWechat,
__ALIPAY__: isAlipay,
wx: isWechat ? 'wx' : 'shim',
my: isWechat ? 'wx' : 'shim',
}),
new WXAppWebpackPlugin({
clear: !isDev,
}),
new ProvidePlugin({
'shim': 'shim',
}),
new optimize.ModuleConcatenationPlugin(),
new IgnorePlugin(/vertx/),
shouldLint && new StylelintPlugin(),
min && new MinifyPlugin(),
new CopyPlugin(copyPatterns, { context: srcDir }),
].filter(Boolean),
///
alias: {
utils: require('path').resolve(__dirname, 'src/utils'),
shim: require('path').resolve(__dirname, 'src/shim.js'),
},
这样就报错了
Uncaught ReferenceError: shim is not defined
Uncaught ReferenceError: shim is not defined
看起来是 runtime error,能否提供更多的调用上下文和 error stack?
不过从你的配置可以看得出一点问题:
//webpack.config.babel.js
// ...
new DefinePlugin({
__DEV__: isDev,
__WECHAT__: isWechat,
__ALIPAY__: isAlipay,
wx: isWechat ? 'wx' : 'shim',
my: isWechat ? 'wx' : 'shim',
})
当打包 target
是支付宝小程序时,my
会变成 shim
,而在 src/shim.js
里,for (let key in my) {
这里的 my
将会被转换为 shim
,此时 shim
为空对象。因此个人建议 webpack.config.js
的 DefinePlugin
应该是 my: isWechat ? 'wx' : 'my'
,不然编译后的代码将无法引用 my
对象
另外,src/shim.js
里面可以通过 if (__WECHAT__ ) {}
或 if (__ALIPAY__) {}
来判断环境差异
我现在写了一个 miniapp.js
来封装一般的 api,在 page.js
里将 wx
或 my
替换为 miniapp
业务相关的 api,例如登录授权和支付要看具体流程另外封装
// src/utils/miniapp.js
// 整合各小程序的api,命名和调用参数以微信为标准
import promisify from 'utils/promisify.js';
function noop() {}
function paramsMap(options, maps = {}) {
let params = {};
for (let key in options) {
let myKey = maps.hasOwnProperty(key) ? maps[key] : key;
params[myKey] = options[key];
}
return params;
}
function mySystemInfo2wx(systemInfo) {
systemInfo.system = `${systemInfo.platform} ${systemInfo.system}`;
// 支付宝小程序windowHeight可能拿到0
if (!systemInfo.windowHeight) {
systemInfo.windowHeight = parseInt(systemInfo.screenHeight * systemInfo.windowWidth / systemInfo.screenWidth, 10) - 40;
}
return systemInfo;
}
const miniapp = {
request: (options) => {
if (__WECHAT__) {
return wx.request(options);
}
if (__ALIPAY__) {
let params = paramsMap(options, {
header: 'headers',
});
let success = params.success || noop;
params.success = function (res) {
let result = paramsMap(res, {
headers: 'header',
status: 'statusCode',
});
success(result);
};
return my.httpRequest(params);
}
},
getLocation: (options) => {
if (__WECHAT__) {
// wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标
return wx.getLocation(options);
}
if (__ALIPAY__) {
console.warn('alipay api: getLocation 只支持 gcj02 坐标系');
options.type = 0; // 高德地图用的是 gcj02
return my.getLocation(options);
}
},
openLocation: (options) => {
if (__WECHAT__) {
return wx.openLocation(options);
}
if (__ALIPAY__) {
options.scale = options.scale || 18;
return my.getLocation(options);
}
},
getSystemInfo: (options) => {
if (__WECHAT__) {
return wx.getSystemInfo(options);
}
if (__ALIPAY__) {
let success = options.success || noop;
options.success = function (res) {
success(mySystemInfo2wx(res));
};
return my.getSystemInfo(options);
}
},
getSystemInfoSync: () => {
if (__WECHAT__) {
return wx.getSystemInfoSync();
}
if (__ALIPAY__) {
return mySystemInfo2wx(my.getSystemInfoSync());
}
},
getStorage: (options) => {
if (__WECHAT__) {
return wx.getStorage(options);
}
if (__ALIPAY__) {
let fail = options.fail || noop;
options.success = function (res) {
if (!res.data) {
fail(res);
}
else {
console.log(res);
}
};
return my.getStorage(options);
}
},
getStorageSync: (key) => {
if (__WECHAT__) {
return wx.getStorageSync(key);
}
if (__ALIPAY__) {
return my.getStorageSync({
key: key,
}).data;
}
},
setStorage: (options) => {
if (__WECHAT__) {
return wx.setStorage(options);
}
if (__ALIPAY__) {
return my.setStorage(options);
}
},
setStorageSync: (key, data) => {
if (__WECHAT__) {
return wx.setStorageSync(key, data);
}
if (__ALIPAY__) {
return my.setStorageSync({
key: key,
data: data,
});
}
},
removeStorage: (options) => {
if (__WECHAT__) {
return wx.removeStorage(options);
}
if (__ALIPAY__) {
return my.removeStorage(options);
}
},
removeStorageSync: (key) => {
if (__WECHAT__) {
return wx.removeStorageSync(key);
}
if (__ALIPAY__) {
return my.removeStorageSync({
key: key,
});
}
},
clearStorage: (options) => {
if (__WECHAT__) {
return wx.clearStorage(options);
}
if (__ALIPAY__) {
return my.clearStorage(options);
}
},
clearStorageSync: () => {
if (__WECHAT__) {
return wx.clearStorageSync();
}
if (__ALIPAY__) {
return my.clearStorageSync();
}
},
redirectTo: (options) => {
if (__WECHAT__) {
return wx.redirectTo(options);
}
if (__ALIPAY__) {
return my.redirectTo(options);
}
},
navigateTo: (options) => {
if (__WECHAT__) {
return wx.navigateTo(options);
}
if (__ALIPAY__) {
return my.navigateTo(options);
}
},
navigateBack: (options) => {
if (__WECHAT__) {
return wx.navigateBack(options);
}
if (__ALIPAY__) {
return my.navigateBack(options);
}
},
showModal: (options) => {
if (__WECHAT__) {
return wx.showModal(options);
}
if (__ALIPAY__) {
// 支付宝不支持自定义按钮颜色
if (options.showCancel) {
let params = paramsMap(options, {
confirmButtonText: 'confirmText',
cancelButtonText: 'cancelText',
});
return my.confirm(params);
}
else {
let params = paramsMap(options, {
buttonText: 'confirmText',
});
return my.alert(params);
}
}
},
showToast: (options) => {
if (__WECHAT__) {
return wx.showToast(options);
}
if (__ALIPAY__) {
let params = paramsMap(options, {
content: 'title',
type: 'icon',
});
params.duration = params.duration || 1500;
if (params.type === 'loading') {
params.type = 'none';
console.warn('alipay api: showToast 不支持 type=loading, 暂时使用 none 代替');
}
return my.showToast(options);
}
},
hideToast: () => {
if (__WECHAT__) {
return wx.hideToast();
}
if (__ALIPAY__) {
return my.hideToast();
}
},
showShareMenu: (options) => {
if (__WECHAT__) {
return wx.showShareMenu(options);
}
if (__ALIPAY__) {
return null;
}
},
makePhoneCall: (options) => {
if (__WECHAT__) {
return wx.makePhoneCall(options);
}
if (__ALIPAY__) {
let params = paramsMap(options, {
number: 'phoneNumber',
});
return my.makePhoneCall(params);
}
},
setNavigationBarTitle: (options) => {
if (__WECHAT__) {
return wx.setNavigationBarTitle(options);
}
if (__ALIPAY__) {
return my.setNavigationBar(options);
}
},
setNavigationBarColor: (options) => {
if (__WECHAT__) {
return wx.setNavigationBarColor(options);
}
if (__ALIPAY__) {
console.warn('alipay api: setNavigationBar 不支持 frontColor, animation');
return my.setNavigationBar(options);
}
},
};
miniapp.pro = {};
for (let key in miniapp) {
if (miniapp.hasOwnProperty(key) && typeof miniapp[key] === 'function') {
miniapp.pro[key] = promisify(miniapp[key]);
}
}
export {
miniapp as default,
};
webpack 添加了对 json 和 wxml 的转换
//webpack.config.babel.js
module: {
rules: [isWechat ? {} : {
test: /\.json$/,
loader: 'string-replace-loader',
options: {
multiple: [{
search: 'navigationBarTitleText',
replace: 'defaultTitle',
flags: 'g',
},
{
search: 'navigationBarBackgroundColor',
replace: 'titleBarColor',
flags: 'g',
},
{
search: 'enablePullDownRefresh',
replace: 'pullRefresh',
flags: 'g',
},
{
search: 'usingComponents',
replace: 'usingComponents',
flags: 'g',
}
],
},
},
// *.wxml
isWechat ? {} : {
test: /\.wxml$/,
include: /src/,
loader: 'string-replace-loader',
options: {
multiple: [{
search: 'wx:if',
replace: 'a:if',
flags: 'g',
},
{
search: 'wx:elif',
replace: 'a:elif',
flags: 'g',
},
{
search: 'wx:else',
replace: 'a:else',
flags: 'g',
},
{
search: 'wx:for',
replace: 'a:for',
flags: 'g',
},
{
search: 'wx:for-index',
replace: 'a:for-index',
flags: 'g',
},
{
search: 'wx:for-item',
replace: 'a:for-item',
flags: 'g',
},
{
search: 'bindtap',
replace: 'onTap',
flags: 'g',
},
{
search: 'catchtap',
replace: 'catchTap',
flags: 'g',
},
{
search: 'bindinput',
replace: 'onInput',
flags: 'g',
},
{
search: 'bindchange',
replace: 'onChange',
flags: 'g',
},
{
search: 'bindfocus',
replace: 'onFocus',
flags: 'g',
},
{
search: 'bindsubmit',
replace: 'onSubmit',
flags: 'g',
},
{
search: 'bindscrolltolower',
replace: 'onScrollToLower',
flags: 'g',
},
],
},
},
]
}
// 全局引入 miniapp,供 page.js 调用
new ProvidePlugin({
'miniapp': [require('path').resolve(__dirname, 'src/utils/miniapp.js'), 'default'],
}),
我在开发支付宝小程序过程中遇到的一些兼容问题,然后在这个项目的基础上添加了一些功能,保证代码能以微信的标准做开发 写了一个example,如果这边考虑整合,我可以提交一个pr https://github.com/chaucerling/wxapp-boilerplate-alipay-example