background

Enterprise WeChat in offline scene showed a trend of explosive growth over the past two years, compared with individual WeChat, enterprise there are a lot of personal WeChat WeChat incomparable advantages, such as the strengthening of brand identity, external customer management, rich operational means, etc., to put it bluntly WeChat is geared to the needs of enterprise, the individual WeChat is for personal use, The different positioning also determines that the enterprise wechat is more suitable for use in operation, marketing and promotion. After two large versions of product iterations since 2021.3, at present, Xueerisi one-to-one enterprise wechat has practical experience in the production environment in user portrait, tag system construction, fission activities, channel live code, group assistant, multi-subject support and other aspects. This article will share the pits encountered by Xueersi one-on-one in the process of developing enterprise wechat application and how to solve them, hoping to be of some help to other teams and partners.

1. On page H5 of wechat, page return authorization leads to the problem of circular jump

In the project, there is such a requirement that wechat non-silent authorization needs to be invoked on the home page, and after successful authorization, it will jump to the active page, and then render the data of the active page. Call in the home page to get all the addresses of wechat authorization, similar

https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOP E&agentid=AGENTID&state=STATE#wechat_redirectCopy the code

After obtaining the link successfully, jump to the authorization page through window.location.replace(URL). After the user grants authorization, the wechat processing page jumps to the REDIRECT_URI address in the link, which is the active page. Ideally, there is currently only one active page in window.history, and when the page returns, the page stack is empty and the window closes. But the fact is that when the user returns, it will be returned to the home page again, and the home page continues to execute the jump authorization logic, and the authorization successfully jumps to the active page, so that the user cannot exit the active page and falls into an infinite loop.

After testing, it is found that window.history contains active page and home page with code parameter, as shown in the figure below:

The desired effect in the project is to exit the wechat window directly when you return from the active page. The first solution is to listen to the page return event in the active page, and directly close the wechat window in the return callback event. The popState event is triggered when the user clicks the back button. Note that the active history page must be pushed onto the page stack via window.history.pushState. Code implementation:

created() {
    window.history.pushState({}, null, location.href);
    window.addEventListener("popstate", () => { 
        wx.closeWindow();
    }, false);
},
Copy the code

It seems that the logic is not complicated. After testing on mobile phones, we found that the effect is what we want on iPhone. When the page is returned, the wechat window is directly exited, but it is not implemented on some Android phones. Long press and other actions, and then return to the page, will trigger the listening event. After consulting relevant articles, the conclusion is given

Cause: wechat security policy/wechat browser kernel solution: the user must click the current interface (real person interaction interface)

The problem seems to get stuck here, but here’s the good news:

IPhone and some Android phones are already the effect we want, and the rest of Android phones can only be compatible from other aspects.

Here says the solution idea of part article first.

  • Direct the user to the page. A pop-up box pops up when entering the page to induce the user to click the pop-up box. The disadvantage of this method is that if the user does not click the popbox after entering the page, it will still lead to the page cannot exit, and not all pages are suitable for popbox. So this method is not suitable for our project.
  • Introduce other third-party libraries to listen for phone native fallback events, such as jQuery Mobile, which listens for gestures returned by the phone and then executes callback events. This method increases the size of h5 page, which is not conducive to page loading, and it cannot guarantee that all mobile phones can be monitored. After testing, it is impossible to monitor some mobile phones when they return by physical keys.

If you can’t prevent the page from returning, can you compare the difference between entering the home page twice and closing the window when the page returns to the home page?

After entering the home page for two times, it is found that when the user is authorized successfully, the wechat authorization will jump to the active page, and the code parameter will be brought on the active page. After obtaining the code successfully, we will store it in sessionStorage. When entering the Home page, we will first determine whether there is a code. Just exit the page. Code implementation:

beforeRouteEnter(now, old, next) { next(vm => { let unionId = sessionStorage.getItem("unionId"); if (unionId) { vm.closeWX(); }}); }, closeWX() { document.addEventListener( "WeixinJSBridgeReady", function() { WeixinJSBridge.call("closeWindow"); }, false ); // JSSDK wx.closeWindow(); }Copy the code

Note ⚠️ :

It should be noted that WeixinJSBridge must be ready when calling the method of wechat closing the window. Otherwise, the closeWindow method will not take effect when WeixinJSBridge is not loaded.

We retested it on the phone and found that all the phones looked exactly as we wanted. To review the problem, the logic before the unprocessed page returns:

Listen back and compatible with some android phones after the logic:

2. Wechat H5 front end generates posters

In fission event page, need to request the backend interface to get to the active information, get activity posters data, including the background of the event, the user nickname, avatars, enterprise WeChat qr code, the text and images to regenerate the posters figure, insert page, users may be retained by long-press posters posters or share with friends. In the project, HTML2Canvas was used to convert THE DOM into an image.

Note ⚠️ :

Version 1.0.0-RC.4 is used here, and some of the higher versions can cause ios phones to generate blank images

After determining the implementation scheme, the next step is to draw the DOM, check the HTML2Canvas document, and convert the DOM into a picture.

2.1 Cross-domain Problems

Converting images into images through Canvas.todataURL () will involve cross-domain problems. We expect images to be the same as the current domain name, but we need to obtain users’ wechat profile picture and enterprise wechat live code, so first we need to solve the problem of loading cross-domain images.

  1. Html2canvas is configured across domains. By configuring useCORS: True (cross-domain resource sharing) When requesting images through CORS, the Origin request header with the value of the current domain name must be carried. Because HTML2Canvas needs to construct DOM first, when there is an IMG tag in the DOM, the SRC attribute of the IMG tag will be resolved, and then the Image object will be created. When requesting an image, the image will be read from the browser cache. The image in the cache does not have an access-Control-allow-Origin response header, so if you do not process the image from the image cache, the image will be blank. So you need to add the crossOrigin=”anonymous” attribute to all img tags to re-read the image data in a cross-domain manner. So what we need to do with CORS is:

    • Configure HTML2Canvas to add useCORS:true

      Html2canvas (dom, {useCORS: true, canvas, scale: scaleBy, backgroundColor: "rgba(255,255,255,0)"})Copy the code
    • Add crossorigin= “anonymous” to DOM img

      <img
            crossOrigin="anonymous"
            v-show="posterData.openHeadPortrait"
            :src="posterData.txAvatar"
            class="poster-ava"
            alt=""
            />
      Copy the code
    • Make sure the image CDN server supports CORS Access, which means returning access-Control-Allow-Origin and other response headers

  • The image has already been accessed, that is, it is already in the cache, so you need to add a random string to the image URL
  1. In addition to requesting images using CORS, you can also convert web images to Base64 format. In the project, we use the method of converting the image to Base64 to load the network image.

    let canvas = document.createElement(“CANVAS”), ctx = canvas.getContext(“2d”), img = new Image();

    img.crossOrigin = “Anonymous”; / / the point! Img. onload = function() {canvas.height = img.height; canvas.width = img.width; ctx.drawImage( img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height ); let dataURL = canvas.toDataURL(outputFormat); canvas = null; resolve(dataURL); }; img.onerror = () => { reject(false); }; // img.src = url; Img. SRC = url + “? t=” + new Date().valueOf(); // Prevent caching problems with OSS

CrossOrigin = “Anonymous”, that is, images can be requested across domains. During development, you can also use loadImage to get images in Base64 format.

 loadImage(newUrl, {
          crossOrigin: "Anonymous",
          canvas: true
})
Copy the code

Note ⚠️ :

When the enterprise wechat live code is obtained in two ways, the image cannot be requested in 4G state when the random string is added after the image, which is related to the enterprise wechat processing

2.2 Image definition problem

  • Use img instead of background-image. Background-image causes the image to be blurred.

  • Configure the scale property, obtain the device DPR, and generate the image with the devicePixelRatio supported by the device

    DPR() {// Get device DPR if (window.devicepixelRatio && window.devicepixelRatio > 1) {return window.devicepixelRatio; } return 2; },

  • Obtain the width and height of DOM and assign a value to the generated canvas. Since posters are generated on the mobile end, if the width and height of the generated picture are fixed in proportion, some models will cause the generated canvas to have white edges

    // Set canvas element property width to DOM node width * pixel ratio canvas.width = parseInt(width) * scaleBy; canvas.height = parseInt(height) * scaleBy;

2.3 Ellipsis will not be displayed if a single line of text is exceeded

The wechat nickname of the user needs to be displayed in the generated poster. When the wechat nickname of the user is too long, although the style of text beyond ellipsis is added in the DOM style, it cannot be displayed in the generated canvas.

Therefore, we need to judge the length of the user’s nickname. When the length of the user’s nickname exceeds the maximum length, hide the excess part and display the ellipsis through variable control.

<span class="poster-text-nickname"> {{posterData.txNickName}} </span> <span>... </span>Copy the code

The value of the control variable needs to get the full length of the user’s nickname on the page

<div style="height:0;" > <span class="nickname"> {{ posterData.txNickName }} </span> </div>Copy the code

2.4 Canvas display is incomplete

In the process of canvas generation, if the generated picture is not fully displayed, it is likely that the user rolled the scroll bar of the page during the drawing process, resulting in part of the picture content exceeding, so before drawing the picture, it must be ensured that the scroll bar of the page is placed at the top

      window.pageYOffset = 0;
      document.documentElement.scrollTop = 0;
      document.body.scrollTop = 0;
Copy the code

In addition, when converting canvas into picture, it is best to save the picture in PNG format. If the picture is saved in JPG format, some pictures will show black blocks. In addition, save it in PNG format, and the background color of the picture can be set to transparency. (After testing, image background transparency works well on Android phones, but not on ios phones)

 this.imgUrl = canvas1.toDataURL("image/png");
Copy the code

3. Container height 100vh scrolling problem on iPhone

In the process of testing, we found the following situation in iPhone

This bug is found in iOS8 and later (on iOS5 to 7 you need to manually turn on hardware acceleration using translateZ(0))

Safari uses native scrolling controls for overflow-scrolling. For pages that have -webkit-overflow-scrolling, a UIScrollView is created that provides the sublayer for the rendering module to use. The CSS style is as follows:

  height: 100vh;
  overflow-y: scroll;
  position: relative;
  -webkit-overflow-scrolling: touch;
  background: #ffffff;
Copy the code

When the height is changed to 100%, the problem is solved. What is the difference between a height of 100vh and a height of 100% on mobile?

When the height is set to 100vh, it exceeds the height of the screen. That is to say, 100vh is not the height of the screen on the iPhone, and 100% is the height that fills the screen. Another solution for the iPhone

   min-height: 100vh;
  /* mobile viewport bug fix */
  min-height: -webkit-fill-available;
Copy the code

When the minimum height is not supported by -webkit-fill-available, use min-height: 100vh to ensure that the page height covers the entire screen

4. Record the loading time of the page (window.performance)

In the active page, we need to know the exact time between the user entering the page and the page rendering to evaluate the user experience. This mainly involves window.performance objects.

When a user clicks a link or enters a link to enter a page, the page content is displayed:

  • Unload of previous page (if there is previous page)
  • HTTP redirection
  • Checking the local cache
  • DNS Domain name Resolution
  • Establishing a TCP Connection
  • The client initiates a request
  • The server returns the resource in response
  • Parsing the dom tree
  • Page loading complete

During the whole process, the time required by each stage can be found in the corresponding field in window.performance

  • Redirection time: redirectEnd – redirectStart
  • DNS query time: domainLookupEnd – domainLookupStart
  • TCP connection time: connectEnd – connectStart
  • HTTP request time: responseEnd -responsestart
  • Parsing dom tree takes time: domcomplete-dominteractive
  • ResponseStart – navigationStart
  • DOMready time: domContentLoadedEventEnd – navigationStart
  • Onload time: loadEventEnd – navigationStart, which is the time for the onload callback function to execute.

For corresponding requirements, we need to obtain the time of loadEventEnd and navigationStart. When obtaining the time point of loadEventEnd, we may obtain it in the load callback of the page but still cannot obtain it due to different devices, so we need to obtain it after a certain delay. Here, the timer is used to query the method to obtain whether the value exists

pageLoadTime() { window.addEventListener("load", () => { this.$nextTick(() => { let performance = window.performance || window.msPerformance || window.webkitPerformance;  if (performance) { if (performance && performance.timing) { let timer = setInterval(() => { if ( performance.timing && performance.timing.loadEventEnd ! == 0 ) { clearInterval(timer); let loadTIme = performance.timing.loadEventEnd - performance.timing.navigationStart; }}, 200); }}}); }); },Copy the code

If the time difference between the page created time and the page load callback is used, the recorded time is missing the time when the page is unloaded, redirected, or requested, as compared to window.performance. So if you need to record the performance of the front end, you can use the window.performance solution.

About the author:

Beibei Guo, xueersi one to one front-end development engineer, four years of front-end development experience, love front-end, painting, good at Angular, Vue.

If you want to know more about the technology of education industry, you can scan the qr code below to join the good future official communication group