background
In a program, there are more or less non-mainline functions embedded. For example, the active page of e-commerce software (hit golden eggs, grab red envelopes, etc.) is less related to the main line of the software, and needs to have rapid iteration and flexible characteristics. When considering the development of this part of the function, most people will consider to separate such projects, such as iframe. While in the small program side, The Webview component provided by Tencent is very friendly, so far the API provided for the development of the webview display class can basically meet our needs. However, Tencent has always been heavy of course not in the small program to webview communication to give us good support, let us have a look at the document:
Sure, the WebView component provides a bindMessage method for a web page to send a message to an applet, but it will trigger and receive a message at a specific time (applet back/component destruction/share). This asynchronous message arrival mechanism is obviously not what we need. So, since the path of web => applets is no longer feasible, let’s consider another path: applets => web is possible. And the message from the app to the web must also rely on Tencent’s API, through reading the documentation, we found
Web – view a web page can use JSSDK 1.3.2 provided interfaces return small program page support interface: wx. MiniProgram. NavigateTo
In the usual small program development, the communication between pages is through wx.navigateTo. If we do the same, we might find a breakthrough.
Function implementation
1. Pay
The method of setting up wechat payment in the front end is very simple. You only need to send order parameters to the server to get six parameters including appId, nonceStr, Package, paySign, signType and timeStamp. Wx. requestPayment can then be invoked in the applets environment. Based on the above conditions, we came up with a solution that when the user clicks the payment button, In the webview by wx. MiniProgram. NavigateTo will be paid from the server to obtain six parameters transfer a small program to another page, and return in the callback in the current webview page:
handlePay(data){
let_this = this; new Promise((resolve, reject) => { wx.requestPayment({ ... data, success(res) { resolve("Success!")
},
fail(err) {
resolve("Fail!")
}
})
}).then(outcome => {
wx.navigateBack({
delta: 1,
success() {
wx.showToast({
title: 'payment' + outcome,
icon: "none"})})})}Copy the code
If we call the handlePay method in the onLoad hook of the applet, we can let the user just pay on this page and jump back to the WebView page as soon as the payment completes (successfully or failed). In a traditional VUE project, the jump to the route triggers component lifecycle hooks, where we put data requests to trigger page data updates. However, the above jump back is essentially a routing change in the applet. The onShow hook on the WebView page is called, and then the embedded H5 is simply stored on the memory stack. Getting the H5 page to refresh the results (or trigger any event) as soon as the payment is complete is a tricky problem. After discussion, we came up with two solutions:
(1)
this.$wx.miniProgram.navigateTo({
url: "/pages/training/counter/counter? task=pay¶m="+param }); /* In H5, jump to the miniprogram payment interface through the SDK of WX and pass the payment parameters */setTimeout(() => {
this.$refs.dlg.showDialog(); }, 1000); /* After 1s, a dialog pops up that can only be closed by clicking, allowing the user to trigger a page refresh by clicking */Copy the code
(2)
this.$wx.miniProgram.navigateTo({
url: "/pages/training/counter/counter? task=pay¶m="+param }); / * by wx SDK to jump in the h5 pay small program interface, will pay parameters * / document. AddEventListener ("visibilitychange".function() {/* add page refresh method */});Copy the code
The (2) method does not work in small program development tools, and works in real machine debugging (no testing of various models); Considering that this method involves manipulating document objects, compatibility is a concern and the possibility of misoperation may arise during user rollback. Finally, we adopted the popover method in (1).
2. The authentication
Actually this problem should have been solved before payment. Since it is a functional WebView application, it is necessary for the user’s behavior to be recorded (which is also a prerequisite for payment). The back end of the store trainer project is essentially independent from the small program merchant side, and the logics of user login are carried out on the server of the small program merchant side. Therefore, this time the backend uses the forward (packet layer 1) method, all requests will first arrive on the merchant side of the applet server, after the authentication is completed, the request will be forwarded to the store trainer server. The most important thing on the front end is to pass the userKey representing user information to H5 (in fact, this is the only example of a small program communicating to H5 in this project) :
this.setData({
url: `${this.data.url}? storeId=${storeId}&userKey=${userKey}'// webView component SRC})Copy the code
It is worth noting that this way of communication achieves the requirements of communication from the small program to H5, but once the SRC of the WebView component changes, the existing page stack will be destroyed, so we can only pass the userKey representing the user information when H5 initialization. Before the H5 page is destroyed, Any manipulation of the WebView component SRC can cause the above problems.
3. If I want to use another applets API…
This seems like a bogus proposition: why write H5 when you want to use the applets API so badly? But first, an example:
onLoad: function (options) {
let _this = this;
let { url, type} = options; Wx. navigateBack({// Jump back in with a speed that no one would noticesuccess() {/* Because the operation of saving pictures and videos needs to be authorized by the user in the small program, so this problem should be solved first. * _this.cansave (). Then (res =>   {if (res == "ok") {
_this.wxDownload(url, type);
wx.showToast({
title: 'Start downloading',
icon: "none"
})
}
}).catch(err => {
console.log(err)
})
},
fail(err) {
throw (err)
}
})
},
canSave() {
return new Promise((reslove,reject) => {
wx.getSetting({
success: (res) => {
if(! res.authSetting['scope.writePhotosAlbum'] {wx.authorize({// authorize popover scope:'scope.writePhotosAlbum',
success: () => {
reslove("ok"}, fail: () => {// After the first authorization is rejected, due to Tencent's strange setting, the second time will directly enter the fail callback, we hijack the fail function wx.showModal({title:'tip',
content: 'Please go to Settings to complete the album authorization operation',
success(res) {
if(res.confirm) {wx.openSetting({// Open the applet setting permission interface success(res) {if (res.authSetting['scope.writePhotosAlbum'] {// The user agrees to reslove("ok")
}
},
fail(err) {
reject(err)
}
})
} else if (res.cancel) {
reject('User hit Cancel'}}})}})}else {
reslove("ok")
}
}
})
})
},
wxDownload(url, typeDownloadFile ({url: url, // download the url of the resource network success:function (res) {
if(res.statusCode === 200) {wx.playVoice({filePath: res.tempFilepath})} // Save video to local switch (type) {
case "img":
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success:
function (data) {
wx.showToast({
title: 'Image downloaded successfully',})}});break;
case "video":
wx.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success:
function (data) {
wx.showToast({
title: 'Video download successful'})}}); } }, fail:function (err) {
console.log(err)
}
})
}
Copy the code
Review
1. Graceful route parameter transmission
Review: in a small program to develop functional webview page, the key lies in the cross-domain communication (I feel more like a ‘cross-browser communication), and the realization of communication is used in the h5 WeChat SDKWX. MiniProgram. NavigateTo. In the old days when we were doing small program projects, we would write it this way
wx.navigateTo({ url: `/pages/b? id=996` });Copy the code
So, let’s do the same thing in the jump pay operation
wx.navigateTo({ url: `/pages/pay? appId=${appId}&nonceStr=${nonceStr}&package=${package}&paySign=${paySign}&timeStamp=${timeStamp}`});Copy the code
However, after doing this, it was discovered that the parameters passed to the applet page were always wrong, and finally found that the package argument contained “=”, which was escaped in the route. Because we need to make it elegant:
letparam = encodeURIComponent(JSON.stringify(res)); // Encrypt this for the res payment parameter returned by the server.$wx.miniProgram.navigateTo({
url: "/pages/training/counter/counter? task=pay¶m="+ param }); // I think we can wrap a small program's routing API so that it can pass parameters like vue RouterCopy the code
Then decrypt it in the applet page
/ * * / onLoad hooklet {task,param} = options;
letdata = JSON.parse(decodeURIComponent(param)); .Copy the code
2. The modular
If we want to use the friendly apis provided by applets in H5, there is only one way to do this: jump to a applets page and back again. It would not be nice to create a small application page for every feature we use. Can we create a applet page (arguably a packaged component) that does just one thing: output all the applet apis that might be used in an H5 application? So, we should write this in the component
onLoad: function(options) {// Pass the API to be called with the task argumentlet {task,param} = options;
let data = JSON.parse(decodeURIComponent(param));
switch(task){
case "pay":
this.handlePay(data);
break;
case "download":
this.handleDownload(data);
break;
case "copy":
this.handleCopy(data);
break;
case "preview":
this.handlePreview(data);
break; . }},Copy the code
3. Business thinking
In fact, business is the most essential problem. What we have mentioned above is all about how to do something, but why to do it has not been mentioned. Considering the current business of the company, for example, some applications unrelated to the main line business will be added to our client side small program from time to time: service manager registration, store online registration… Increasing functionality will continue to increase the size of small programs, and for a program with a large number of users, the release caused by frequent updates of new feature development is also very troublesome. In many current scenarios, this is achieved by adding another applets and adding an entry to the original main applets, so we keep registering applets… However, the increasing number of “mini-programs” that meet the requirements of granularity are confined to the mini-program ecosystem. If we want to add a service manager registration entrance on the company’s official website, or the public account, we can only achieve this by pasting small program TWO-DIMENSIONAL code. When it comes to cross-scenario issues, H5 applications have great advantages. However, when porting to other environments, we need to adapt the implementation method for different environments where we need to take advantage of the apPLETS API (so object-oriented writing and granularity of the code are very important). Although this article covers how to develop a functional applets WebView page, for now I think it is best not to embed an h5 application with too much functionality in the WebView interface. If so, please check this article out.