This content is authorized by Cao Jun and his team of Yinke Holdings Ronghui R&D Department. The team has more than 10 small program development, deep into the small program field, summed up this excellent long article. At the same time, this content has been incorporated into my open source project, which currently includes JS, network, browbrow-related, performance optimization, security, framework, Git, data structure, algorithm and other content. Whether it is basic or advanced, or source code interpretation, you can get satisfactory answers in this map. Hopefully, this map will help you better prepare for an interview.

Applet – Login

Unionid and openid

Before we learn about applet login, let’s write to understand that applet/official login involves two of the most critical user identifications:

  • OpenIdIs a user for a small program/public identification, developers can identify the user through this identification.
  • UnionIdIs a user for the same subject micro channel small program/public account/APP identification, developers need to bind the same account under the micro channel open platform. Developers can through UnionId, to achieve a number of small programs, public number, and even between the APP data interchange.

Key to the Api

  • Wx. login Official login capability

  • Wx. checkSession Checks whether the current session_key is valid

  • Wx.authorize initiates authorization requests to users in advance

  • Wx. getUserInfo Obtains basic user information

Login process Design

The following is a description of several login processes that the author has contacted:

Use existing login systems

Directly reuse the login system of the existing system, only need to design user name, password/verification code input page in the small program side, can easily achieve login, just need to maintain a good user experience.

Create user system with OpenId

👆 mentioned that OpenId is a small program for the identification of a user, using this we can easily implement a set of small program based user system, it is worth mentioning that this user system is the lowest disturbance to users, can achieve silent login. The specific steps are as follows:

  1. The applet client obtains the code from wX. login

  2. After passing the code to the server, the server gets the code and calls the wechat login certificate verification interface. The wechat server returns openID and session key session_Key. At this time, the developer server can use OpenID to generate user entry, and then return custom login state to the small program client

  3. The small program client cache (through storage) customizes the login state (token), which can be carried as the user identity when invoking the interface

Create user system using Unionid

If you want to achieve a number of small programs, public number, the login system data interchange, you can obtain the user unionID way to establish a user system. Because the unionID in the same open platform of all applications are the same, through the unionID established user system can realize the interoperability of the whole platform data, more convenient access to the original function, then how to get unionID, there are the following two ways:

  1. If the user follows a certain same subject public number, or has been in a certain same subject App, public account for wechat login authorization, through WX. login can directly obtain the unionID

  2. Wechat provides a sample code) can get the unionID to establish user system, and then returned by the server login state, local record can achieve login, attached to the best practices provided by wechat:

    • Call wX.login to get the code, and then exchange from the wechat back end to session_Key, which is used to decrypt the sensitive data returned by getUserInfo.

    • Use wx.getSetting to obtain the authorization of the user

      • If the user is authorized, call API wx.getUserInfo to get the latest user information.
      • If the user is not authorized, a button is displayed in the interface to prompt the user to log in. After the user clicks and authorizes, the latest information of the user will be obtained.
    • Once the user data is obtained, it can be displayed or sent to its own backend.

Matters needing attention

  1. Need to getunionidForm login system, in the past 18 years (before April) is done by following this way, but subsequent WeChat adjustment (because of a small program, enter the active form of authorization popup window of the bounce, are more likely to lead to loss of users), is adjusted for button must be used to guide the user authorization way actively, the adjustment for developers, Developers need to pay attention to abide by the rules of wechat, and timely communication with the business side of the business form, do not exist fluke psychology, in order to prevent small procedures but audit and other situations.
Wx. login(get code) ===> wX. getUserInfo(user authorization) ===> Obtain the UnionIDCopy the code
  1. Because small programs do not have the concept of cookies, the login state must be cached locally, so it is strongly recommended to set the expiration time for the login state

  2. It is worth mentioning that some public parameters, such as platform, channel, and deviceParam, may need to be added to support functions such as risk control, security verification, and multi-platform login. When determining the scheme with the server, as the front-end students should put forward these reasonable suggestions in time and design a reasonable system.

  3. Openid, unionID do not transfer in plain text in the interface, this is a dangerous behavior, and very unprofessional.

Applet – picture export

Students who often develop and use small programs must not be unfamiliar with this function, which is a common way of drainage, generally at the same time in the picture will be attached to a small program TWO-DIMENSIONAL code.

The basic principle of

  1. With the help of canvas element, first draw the style to be exported on canvas (API is basically the same as H5, but there are slight differences, you can pay attention to it when using)

  2. Export the image with canvasToTempFilePath provided by wechat, and save the image locally using saveImageToPhotosAlbum (requires authorization)

How to implement it elegantly

According to the above principle, the implementation is very simple, but is the design draft extraction, drawing can, but as a common function, every time to write a lump of code is not very uncomfortable. So how does the applet design a general method to help us export images? The idea is as follows:

  1. Drawing the desired style is an inescapable step. But we can encapsulate a drawing library, including the drawing of common graphics, such as rectangle, rounded rectangle, circle, sector, triangle, text, pictures to reduce drawing code, only need to extract the style information, you can draw easily, and finally export the picture into the album. I think the following way to draw is more elegant and clear, in fact, you can also use a type parameter to specify the type of drawing, passing in a style array, to draw.

  2. Combined with the implementation of the previous step, if there are multiple export requirements for the same type of cards, you can also use a custom component to encapsulate the same type of cards as a common component, and introduce the component where the function of exporting pictures is needed.

    
  class CanvasKit {
    constructor() {
    }
    drawImg(option = {}) {
      ...
      return this
    }
    drawRect(option = {}) {
      return this
    }
    drawText(option = {}) {
      ...
      return this
    }
    staticexportImg(option = {}) { ... }}let drawer = new CanvasKit('canvasId').drawImg(styleObj1).drawText(styleObj2)
  drawer.exportImg()

Copy the code

Matters needing attention

  1. Network images cannot be drawn to canvas in the applet. You need to download the images to a local temporary file through downLoadFile before drawing
  2. Usually need to draw the QR code to the exported picture, there areA kind of wayExport two-dimensional code, need to carry the parameters must do coding, and there are specific length (32 visible characters) limits, can be generated by the serverShort linkTo solve the problem

Small program – data statistics

Data statistics as a common way to analyze user behavior, small program side is also essential. Small program to take exposure, click data buried point is actually the same as H5 principle. However, burial point is a requirement unrelated to business logic. If we add various burial point codes to every click event and every life cycle, it will interfere with normal business logic and make the code become bloated. The author provides the following ideas to solve the data burial point:

Design a buried point SDK

The code structure of small program is that each Page has a Page method, which accepts a business logic object containing life cycle functions and data to wrap this layer of data. The business logic of the Page is realized by the low-level logic of small program. Through this, we can think of the idea of packaging the Page once, tamper with its life cycle and click events, interfuse buried point code, without interfering with the business logic, as long as do some simple configuration can be buried point, simple code implementation is as follows:

Code for understanding only page =function(params) {
    let keys = params.keys()
    keys.forEach(v= > {
        if (v === 'onLoad') {
          params[v] = function(options) {
            stat()   // Expose the buried point code
            params[v].call(this, options)
          }
        }
        else if (v.includes('click')) {
          params[v] = funciton(event) { 
            let data = event.dataset.config
            stat(data)  // Click the burial point
            param[v].call(this)}}})}Copy the code

This idea is not only applicable to buried points, but also can be used for global exception handling, unified processing of requests and other scenarios.

Analysis of the interface

For some special business, we can take the interface buried point, what is the interface buried point? In many cases, we have call API is not much, only in a particular page calls, through this line of thought, we can analyze, the interface is requested, then this behavior is triggered, it can be buried point through the server log data, the limitation is bigger, but this way and that is the process of analysis result, there may be error, But as a way of thinking about it.

Wechat custom data analysis

Wechat itself provides data analysis capabilities, wechat itself provides two kinds of data analysis methods, routine analysis and custom analysis, which can be configured in the small program background. With the help of small program data assistant this small program can be very convenient to view.

Small program – engineering

What does engineering do

At present, the front-end development process, engineering is an essential part of the small program engineering, what do we need to do? First, look at the current small program development which problems need to be solved:

  1. Less, Sass, and Stylus are all mainstream CSS solutions that do not support a CSS precompiler
  2. NPM package is not supported (this one, heard from wechat open class, wechat is ready to support)
  3. There is no support for subsequent JS features such as ES7, and good async await features are not available
  4. Import of external font files is not supported, only base64 is supported
  5. There are no code checking tools such as ESLint

Scheme selection

For the current common engineering solutions, webpack, rollup, parcel, etc., are often used with single-page applications packaging and processing, while small programs are inherently “multi-page applications” and have some specific configurations. Based on the problem to be solved, which is nothing more than compiling, modifying, and copying files, for these requirements, we think that stream-based Gulp is very well suited to handle and is much easier to configure than WebPack for multi-page applications. Therefore, gulp is recommended for small program engineering

Specific development ideas

Gulp task implementation:

  1. Compile less files to corresponding directories in real time
  2. Introduce runtime files that support async, await
  3. Compile the font file to base64 and generate the corresponding CSS file for easy use
  4. Analyze where the NPM package is referenced, print the NPM package into a file, and copy it to the corresponding directory
  5. Check code specifications

This isn’t too hard to implement, but is it just a pure gulp build script with a fixed directory, copied every time a new little program comes along? Obviously not suitable, then how to really achieve small program engineering? We may need a simple scaffold, the scaffold needs to support the functions:

  1. Support for creating new projects, creating pages, creating Components
  2. Built-in build scripts are supported
  3. Support for the release of applets, and also find a way to access Jenkins and other tools for continuous integration (more on applets continuous integration later)…

Applet architecture

The framework of wechat applet consists of two parts: View layer and App Service logical layer. The View layer is used to render the page structure, and the AppService layer is used for logical processing, data requests, and interface calls.

They run in two threads.

They run in two threads.

They run in two threads.

The view layer communicates with the logical layer through the JSBridage of the system layer. The logical layer notifies the data changes to the view layer and triggers the page update of the view layer. The view layer notifies the triggered events to the logical layer for business processing.

supplement

The view layer uses WebView rendering, the built-in WKWebView is used in iOS, and Tencent’s X5 kernel (based on Blink) is used in Android.

The logical layer runs on iOS using the built-in JSCore and on Android using Tencent’s X5 kernel (based on Blink).

The development tool uses Nw.js to provide both the view layer and the logical layer runtime environment.

Using JS-Beautify to batch format the code of wechat development tool @v1.02.1808080 under Mac:

cd /Applications/wechatwebdevtools.app/Contents/Resources/package.nw
find . -type f -name '*.js' -not -path "./node_modules/*" -not -path -exec js-beautify -r -s 2 -p -f '{}' \;
Copy the code

In the js/extensions/appservice/index. Found in js:

	267: function(a, b, c) {
    const d = c(8),
      e = c(227),
      f = c(226),
      g = c(228),
      h = c(229),
      i = c(230);
    var j = window.__global.navigator.userAgent,
      k = - 1! == j.indexOf('game');
    k || i(), window.__global.getNewWeixinJSBridge = (a) = > {
      const {
        invoke: b
      } = f(a), {
        publish: c
      } = g(a), {
        subscribe: d,
        triggerSubscribeEvent: i
      } = h(a), {
        on: j,
        triggerOnEvent: k
      } = e(a);
      return {
        invoke: b,
        publish: c,
        subscribe: d,
        on: j,
        get __triggerOnEvent() {
          return k
        },
        get __triggerSubscribeEvent() {
          return i
        }
      }
    }, window.WeixinJSBridge = window.__global.WeixinJSBridge = window.__global.getNewWeixinJSBridge('global'), window.__global.WeixinJSBridgeMap = {
      __globalBridge: window.WeixinJSBridge
    }, __devtoolsConfig.online && __devtoolsConfig.autoTest && setInterval((a)= > {
      console.clear()
    }, 1e4);
    try {
      var l = new window.__global.XMLHttpRequest;
      l.responseType = 'text', l.open('GET'.`http://The ${window.location.host}/calibration/The ${Date.now()}`,!0), l.send()
    } catch (a) {}
  }
Copy the code

In the js/extensions/gamenaitveview/index. Found in js:

  299: function(a, b, c) {
    'use strict';
    Object.defineProperty(b, '__esModule', {
      value:!0
    });
    var d = c(242),
      e = c(241),
      f = c(243),
      g = c(244);
    window.WeixinJSBridge = {
      on: d.a,
      invoke: e.a,
      publish: f.a,
      subscribe: g.a
    }
  },
Copy the code

In the js/extensions/pageframe/index. Found in js:

317: function(a, b, c) {
    'use strict';

    function d() {
      window.WeixinJSBridge = {
        on: e.a,
        invoke: f.a,
        publish: g.a,
        subscribe: h.a
      }, k.a.init();
      let a = document.createEvent('UIEvent');
      a.initEvent('WeixinJSBridgeReady',!1,!1), document.dispatchEvent(a), i.a.init()
    }
    Object.defineProperty(b, '__esModule', {
      value:!0
    });
    var e = c(254),
      f = c(253),
      g = c(255),
      h = c(256),
      i = c(86),
      j = c(257),
      k = c.n(j);
    'complete'= = =document.readyState ? d() : window.addEventListener('load'.function() {
      d()
    })
  },
Copy the code

We’ve all seen the definition of WeixinJSBridge. There are several key methods: on, Invoke, Publish, subscribe.

Take the invoke, for example, in the js/extensions/appservice/index. This code found in js:

f (! r) p[b] = s, f.send({command: 'APPSERVICE_INVOKE'.data: {
        api: c,
        args: e,
        callbackID: b
    }
});
Copy the code

In the js/extensions/pageframe/index. Js found in this code:

g[d] = c, e.a.send({
    command: 'WEBVIEW_INVOKE'.data: {
        api: a,
        args: b,
        callbackID: d
    }
})

Copy the code

The command field is used to distinguish behavior, and the Invoke field is used to call Native apis. Use different prefixes in different sources. Data contains the Api name and parameters. In addition, callbackID specifies the handle to the method that accepts the callback. Appservice and Webview use the same communication protocol.

We can’t use BOM and DOM in our code because there aren’t any, and on the other hand we don’t want JS code directly manipulating views.

The following code is found in the development tool remote-helper.js:

const vm = require("vm");

const vmGlobal = {
    require: undefined.eval: undefined.process: undefined, setTimeout(... args) {/ /... Omit code
        return timerCount;
    },
    clearTimeout(id) {
        const timer = timers[id];
        if (timer) {
            clearTimeout(timer);
            deletetimers[id]; } }, setInterval(... args) {/ /... Omit code
        return timerCount;
    },
    clearInterval(id) {
        const timer = timers[id];
        if (timer) {
            clearInterval(timer);
            deletetimers[id]; }},console: (() = > {
        / /... Omit code
        returnconsoleClone; ()}});const jsVm = vm.createContext(vmGlobal);
// Omit a lot of code...
function loadCode(filePath, sourceURL, content) {
    let ret;
    try {
        const script = typeof content === 'string' ? content : fs.readFileSync(filePath, 'utf-8').toString();
        ret = vm.runInContext(script, jsVm, {
            filename: sourceURL,
        });
    }
    catch (e) {
        // something went wrong in user code
        console.error(e);
    }
    return ret;
}
Copy the code

This layering is clearly intentional, with the middle layer having complete control over what the program does to the interface, as well as monitoring the data being passed and the response time. On the one hand the program’s behavior is greatly restricted, on the other hand wechat ensures that they have absolute control over the content and experience of the mini program.

This structure also explains why the animation and drawing apis for small programs are designed to generate a final object rather than executing it step by step. The reason for this is that Json data transfer and parsing are expensive compared to native apis, and if called frequently they can cost performance and affect the user experience.

Download the complete mini program package

App Service – Life Cylce

The interview questions

1. Animation is bound to data, drawing is not. Why do you think it is?

var context = wx.createCanvasContext('firstCanvas')
    
context.setStrokeStyle("#00ff00")
context.setLineWidth(5)
context.rect(0.0.200.200)
context.stroke()
context.setStrokeStyle("#ff0000")
context.setLineWidth(2)
context.moveTo(160.100)
context.arc(100.100.60.0.2 * Math.PI, true)
context.moveTo(140.100)
context.arc(100.100.40.0.Math.PI, false)
context.moveTo(85.80)
context.arc(80.80.5.0.2 * Math.PI, true)
context.moveTo(125.80)
context.arc(120.80.5.0.2 * Math.PI, true)
context.stroke()
context.draw()
Copy the code
Page({
  data: {
    animationData: {}},onShow: function(){
    var animation = wx.createAnimation({
      duration: 1000.timingFunction: 'ease',})this.animation = animation
    
    animation.scale(2.2).rotate(45).step()
    
    this.setData({
      animationData:animation.export()
    })
  }
})
Copy the code

2. Does the applet use the browser Fetch API for Http Rquest requests?

Investigation of knowledge points

  • Know that Request is implemented by Native
  • JSCore is not with Http Request, Websocket, Storage and other functions, that is Webkit with
  • The small programwx.requestIs it implemented in accordance with the FETCH API specification? The answer, obviously, is no. Because there is noPromise

View – WXML

WXML (WeiXin Markup Language)

  • Data binding support
  • Supports logical arithmetic and operation
  • Supports templates and references
  • Support for adding events (bindTap)

Wxml compiler: Wcc converts Wxml files to JS

Execution mode: Wcc index.wxml

Use the Virtual DOM to perform local updates

View – WXSS

WXSS(WeiXin Style Sheets)

WXSS compiler: WCSC converts WXSS files to JS

Execution mode: WCSC index.wxss

Supports most CSS features

Hands-on testing includes but is not limited to the following:

  • Transition
  • Animation
    • Keyframes
  • border-radius
  • calc()
  • Selector, exceptThe official documentationListed, actually supported
    • element>element
    • element+element
    • element element
    • element:first-letter
    • element:first-line
    • element:first-child
    • element:last-child
    • element~element
    • element:first-of-type
    • element:last-of-type
    • element:only-of-type
    • element:only-child
    • element:nth-child(n)
    • element:nth-last-child(n)
    • element:nth-of-type(n)
    • element:nth-last-of-type(n)
    • :root
    • element:empty
    • :not(element)
  • iconfont

It is suggested that Css3 features can be tried.

Size unit RPX

RPX (Responsive Pixel) : Can be adapted according to the width of the screen. Set the screen width to 750rpx. Formula:

const dsWidth = 750

export const screenHeightOfRpx = function () {
  return 750 / env.screenWidth * env.screenHeight
}

export const rpxToPx = function (rpx) {
  return env.screenWidth / 750 * rpx
}

export const pxToRpx = function (px) {
  return 750 / env.screenWidth * px
}

Copy the code
equipment RPX convert px (screen width /750) Px conversion RPX (750/ screen width)
iPhone5 1 the RPX = 0.42 px 1 px = 2.34 the RPX
iPhone6 1 the RPX = 0.5 px 1px = 2rpx
iPhone6 Plus 1 the RPX = 0.552 px 1 px = 1.81 the RPX

Pr2rpx-loader is a library you can look at.

Style import

To import an external style sheet, use the @import statement. @import is followed by the relative path of the external style sheet that you want to import. Indicates the end of the statement.

Inline style

Static styles are written uniformly to classes. Style receives dynamic styles, which are parsed at run time. Avoid writing static styles into style as this may affect the rendering speed.

Global styles versus local styles

The styles defined in app.wxss are global and apply to each page. The styles defined in the page’s WXSS file are local styles that apply only to the corresponding page and override the same selectors in app.wxss.

iconfont

As of 20180810

There are plans to support fonts in applets in the future. Refer to wechat open class.

Small program development is similar to normal Web development. Font ICONS can be used, but SRC: URL () is not available for either local or remote addresses. Base64 values are displayed.

Convert TTF files to Base64. Open the platform transfonter.org/. Click the Add Fonts button to load the file in TTF format. Change base64 encode below to ON. Click the Convert button to Convert, and then click Download to download.

Copy the contents of stylesheet.css from the downloaded compressed file to font. WXSS, And copy everything in icomoon style. CSS except @font-face to font-.wxss and change the I selector to.iconfont. Finally:

<text class="iconfont icon-home" style="font-size:50px; color:red"></text>
Copy the code

View – Component

Applets provide a set of components for developing business functions, compared to HTML5 tags in terms of functionality:

The components of the applet are based on the Web Component standard

Implement the Web Component using the Polymer framework

View – Native Component

The components currently implemented by Native are

  • cavnas

  • video

  • map

  • textarea

The Native component layer is on top of the WebView layer. This currently raises some questions:

  • Components implemented by Native can mask other components
  • The components of the Native implementation need to update their position when the WebView is rendered to scroll, which can cause performance problems, especially on Android machines
  • Small program native componentscover-viewCan cover cavnas video, etc., but there are some disadvantages, such as covering on cavnascover-viewYou’ll find that the coordinate system is not uniform

Current problems or limitations of small programs

As of 20180810

Including but not limited to:

  • The applet still uses WebView rendering, not native rendering. (Partly native)

  • The header returned by the server interface cannot be executed, such as set-cookie.

  • JS libraries that depend on the browser environment cannot be used.

  • You can’t use NPM, but you can build a custom tool or use MPvue. (Official support is planned in the future)

  • Can’t use ES7, you can use Babel + Webpack or use mpvue.

  • No support for using your own font (official plan for future).

  • Iconfont can be used base64.

  • Small program can not send friends circle (can save pictures to local, send pictures to friends before. The TWO-DIMENSIONAL code can use interface B).

  • Restrictions on obtaining the QR code/applet interface.

    • B Interface scene A maximum of 32 visible characters are displayed.
    • The total number of codes generated by an AC interface is limited to 100,000. Exercise caution when invoking the code.
    • Scanning qr code on the real machine can only jump to the online version, so in the test environment, debugging can only be done by compiling qr code through developer tools.
    • The small program page path that is not published to the online version will cause the generation of TWO-DIMENSIONAL code failure. The small program that has added the page needs to be published to the online version first.
  • Applittle push only uses “service notifications” and requires the user to proactively trigger the submission of a formId, which is only valid for 7 days. The current practice is to put the form in each page and hide it to get more formids. The back-end use principle is as follows: The one with the shortest validity period is preferred)

  • The size of small program is limited to 2M, and the total subcontract is not more than 8M

  • Forwarding (sharing) small program can not get successful results, the original can. Link (small game made sin)

  • Get the same unionId must be tied to the same open platform. Open platform binding restrictions:

    • 50 mobile apps
    • 10 sites
    • 50 public accounts with the same main body
    • 5 public accounts of different subjects
    • 50 small programs with the same body
    • 5 different body applets
  • Public number associated small program, link

    • All public accounts can be associated with small programs.
    • An official number can be associated with 10 small procedures with the main body, 3 different main small procedures.
    • A small program can be associated with 500 public number.
    • Public account a month can add 13 associated small program, small program a month can add 500 associated.
  • An official number associated with the main body of 10 small programs and 3 small programs can jump with each other

  • Brand search does not support finance and healthcare

  • Applet authorization requires the user to click

  • Applet does not provide testing access_token

  • In The Android operating system, after a small program is authorized to obtain user information, it is deleted and re-authorized to obtain the old signature. As a result, the authorization fails for the first time

  • In developer tools, if you choose to clear cache all after obtaining user information, you will get a new session_key even if wx.checkSession is used and session_key is valid

Applet HTTP2 support

HTTP2 support: Not supported on the emulator or real machine

In order to verify the small program support for HTTP adaptation, I found two servers to do the test, one is the Internet search to support HTTP2 server, one is my local HTTP2 server. All request methods in the test used WX.request.

  1. Web server that supports HTTP2: HTTPs://www.snel.com:443

  2. View the server on Chrome as HTTP2

  3. The interface is requested on the emulator. The HTTP version of the request header is HTTP1.1. The emulator does not support HTTP2

  4. Since the small program online environment needs to configure the request domain name in the project management, and this domain name is not the requested domain name we need, there is no need to waste a domain name location, so open the options such as do not verify the domain name, TSL and so on to request this interface, through the packet capture tool to perform the same as the simulator

The HTTP2 server needs to do compatibility adaptation for small programs

HTTP2 is not supported on both the real server and the emulator, but both requests are successful, and the HTTP version in the response header is changed to HTTP1.1, indicating that the server is compatible with HTTP1.1.

  1. A new node server is started locally and JSON is returned as the HTTP version of the request

  2. If the server only supports HTTP2, an ALPN protocol error occurred during the simulator request. And remind to use the adaptation HTTP1

  3. When the server’s allowHTTP1 is set to true and the relevant request parameters are processed at the time of the request, the emulator can access the interface and print out the corresponding HTTP request version

Process for obtaining user information

  • The session_key has a validity period. The validity period is not told to the developer, except that the more frequently the user uses the mini program, the longer the session_key validity period will be
  • When wX.login is invoked, the sESSION_key is directly updated, rendering the old SESSION_key invalid
  • In the small program, call wX. checkSession to check the login state and ensure that no expired session_key will not be updated. Then call wX. login to obtain the code. Then the user authorizes the applet to get the user information, the applet gets the encrypted user data, and sends the encrypted data and code to the back-end service. The backend gets the session_key through code and decrypts the data, returning the decrypted user information to the applet

Interview question: What happens when you authorize user information and then login?

  • When a user is authorized, the open platform uses the old session_key to encrypt the user information. When you invoke wx.login to login again, the session_key is refreshed. In this case, the back-end service obtains the new session_key from the open platform, but cannot decrypt the encrypted data of the old session_key, and the user information fails to be obtained
  • How about calling wX.checkSession before authorizing user information? Wx. checkSession Checks the logon state and ensures that wX. login does not flush the Session_key so that the back-end service decrypts the data correctly. However, there is a problem. If the miniprogram does not expire the session_key for a long time, wx.login must regenerate the session_key, which again causes the user information to fail to be decrypted.

Performance optimization

We know that the View part runs on top of the WebView, so most of the optimizations in the front end are useful.

We know that the View part runs on top of the WebView, so most of the optimizations in the front end are useful.

We know that the View part runs on top of the WebView, so most of the optimizations in the front end are useful.

Load optimization

The size of the code package is the most direct factor that affects the loading and startup speed of small programs. Not only does a larger code package take longer to download, it also takes longer to inject business code. So the best optimization is to reduce the size of the code package.

Representation of the three stages of applet loading.

Optimize the way

  • Code compression.
  • Clean up useless code and resource files in a timely manner.
  • Reduce the size and number of resource files such as images in your code package.
  • Subcontract loading.

Suggestions for optimizing the first-screen loading experience

  • Advance requests: Asynchronous data requests do not require waiting for the page to be rendered.
  • Use cache: Use the Storage API to cache the asynchronous request data. When the second startup, use the cache data to render the page and update in the background.
  • Avoid white screens: Show skeleton pages and basic content first.
  • Prompt feedback: give immediate feedback on the interactive operations that require the user to wait, so that the user does not think the applet is unresponsive.

Use subcontract load optimization

In the subcontract project, build a small program to build outputs one or more of the functions of the subcontract, a small program that each subcontractor must contain a main package, the so-called master package, which placed the default start page/TabBar page, as well as some public resources/JS script used by all the subcontract, the subcontract is according to the configuration of the developer.

When the small program is started, the main package will be downloaded and the page in the main package will be started by default. If the user needs to open a page in the subpackage, the client will download the corresponding subpackage and display it after the download is complete.

Advantages:

  • For developers, small programs can have a larger code volume, carrying more functions and services
  • For users, you can open small programs faster and use more features without affecting startup speed

Limitations:

  • The subcontract size of the whole small program shall not exceed 8M
  • The size of a single subcontract or main package cannot exceed 2M

The configuration for native subcontracting loading assumes the following applets directory structure to support subcontracting:

├ ─ ─ app. Js ├ ─ ─ app. Json ├ ─ ─ app. WXSS ├ ─ ─ packageA │ └ ─ ─ pages │ ├ ─ ─ the cat │ └ ─ ─ dog ├ ─ ─ packageB │ └ ─ ─ pages │ ├ ─ ─ apple │ └ ─ ─ banana ├ ─ ─ pages │ ├ ─ ─ index │ └ ─ ─ logs └ ─ ─ utilsCopy the code

Developers declare the subpackage structure of a project by using the app.json subPackages field:

{
  "pages": ["pages/index"."pages/logs"]."subPackages": [{"root": "packageA"."pages": [
        "pages/cat"."pages/dog"] {},"root": "packageB"."pages": [
        "pages/apple"."pages/banana"]]}}Copy the code

Principle of the subcontract

  • After subPackages are declared, they are packaged according to the subPackages configuration path, and directories outside the subPackages configuration path are packaged into the app (main package)
  • App (the main package) can also have its own Pages (the outermost pages field)
  • The root directory of a subPackage cannot be a subdirectory within another subPackage
  • The home TAB page must be in the app (main package)

Refer to the principle of

  • PackageA cannot require packageB JS files, but can require app, their own package JS files
  • PackageA can not import packageB template, but can require app, its own package template
  • PackageA cannot use the resources of packageB, but it can use the resources of app and its own package

Official subcontract preloading is coming soon

Independent of the subcontract

Render performance optimization

  • Each call to setData is an interprocess communication process, and the communication overhead is positively correlated with the amount of setData.

  • SetData causes an update of the view-level page content, a time-consuming operation that blocks user interaction for some time.

  • SetData is the most frequently used in small program development and is the most likely to cause performance problems.

Avoid improper use of setData

  • Using data to share data between methods may increase the amount of data transferred by setData. Data should include only data related to page rendering.
  • The use of setData to transfer a large amount of data, ** communication time is positively correlated with the data, page update delay may cause the page update cost increase. ** Transfer only the changed data in the page, using setData’s special key for local updates.
  • Frequent calls to setData in a short period of time, operation lag, interaction delay, blocked communication, page rendering delay. ** Avoid unnecessary setData and merge successive setData calls.
  • In the background page setData, ** preempt the rendering resources of the front page. ** setData call after the page enters the background, deferred until the page is displayed again.

Avoid improper use of onPageScroll

  • Listen for pageScroll events only when necessary. If it is not monitored, it will not be distributed.
  • Avoid executing complex logic in onPageScroll
  • Avoid frequent calls to setData in onPageScroll
  • Avoid frequently querying node information (SelectQuery) when sliding to determine whether to display. In some scenarios, it is recommended to use inersectionObserver instead of node layout

Use custom components

In a scenario that requires frequent updates, custom components are updated only within the component and are not affected by the complexity of other parts of the page.

Official mini program technical capability planning

Custom component 2.0

Small program between several pages, there are some of the same or similar area, then you can think of these regional logic encapsulated into a custom components, the code can be reused, or for more independent logic, can also to encapsulate it into a custom components, namely WeChat custom components released last year, it make the code reuse, reduce the amount of code, more convenient modular, Optimized code architecture organization also enables clearer modules for later maintenance, thus ensuring better performance.

But custom Component 2.0, which wechat intends to launch on the basis of the original, will have more advanced performance:

  • The usingComponents plan supports both global and wildcard definitions: this means that instead of having to iterate on every page, you can batch import all custom components in a directory
  • Features like Computed and Watch are planned to make the code logic clearer
  • Plan to support the Component constructor plug-in when instantiating a custom Component, allowing you to add some logic to the constructor at this stage, allowing you to easily extend it, even extending it to Vue syntax

NPM support

The current pain point of small program development is that open source components have to be manually copied to the project, and later components have to be manually updated. In the near future, small programs will support NPM package management. With this, it will be easy to introduce open source projects by declaring them in the project, then installing them with a simple command.

Official custom component

Wechat mini program team said they are considering launching some official custom components, why not built into the base library? Because the built-in component is provided to the developer, the component must be a capability that is difficult or impossible for the developer to implement. So they prefer to encapsulate custom components. They want to encapsulate common components with complex interactive logic based on these built-in components, making it easier for people to develop. With such components, developers do not have to pay attention to how the barrage is floating, which can save developers development costs.

At the same time, they want to provide some specifications and templates for developers to design custom components that are easy to use.

Add experience score

When small programs load too slowly, they can lead to user loss, and the developers of small programs may be faced with the dilemma of not knowing how to locate the problem or how to solve it.

To this end, the small program is about to launch an experience scoring function, which is to help developers to check out what the small program experience is bad, but also will give an optimization guidelines and suggestions.

Native components are rendered on the same layer

Small program in the original technology selection, introduced the concept of primary components, because the native components can make small application ability more rich, such as maps, the ability of audio and video, but the native components are by the client native rendering, led to a native component level is the highest, developers can easily open the debugging problems, The video component was found blocked on the vConsole.

To solve this problem, wechat made a transitional plan: Cover-view. Cover-views can be overlaid on top of native components, and this suite of solutions solves most of the requirement scenarios. For example, a lot of buttons, titles and even animations on video components are implemented using a cover-view, but it still doesn’t fully address the experience of native components because of the limitations of the Cover-view:

  • Cannot be rendered with other components
  • There is no complete touch event
  • Cover-views differ in how they represent styles
  • Cover-view doesn’t have enough support for styles

Therefore, wechat decided to replace Cover-View with same-layer rendering, which can be used like ordinary components. The level of native components is no longer the highest, but is rendered at the same level with other non-native components, which can be completely controlled by Z-Index and can fully support touch events.

Wechat said that the same layer rendering on the iOS platform small program has begun a private test, will soon open to developers, Android platform has made a breakthrough, is currently doing a round of encapsulation work, open soon.

wepy vs mpvue

Data flow management

Compared with the traditional small program framework, this has been our expectation as a senior developer to solve, in the Web development, with Flux, Redux, Vuex and other data flow tools appear, we also expect to use in business complex small program.

  • By default, WePY supports Redux, which can be built in when scaffolding projects

  • Mpvue is a port of Vue and supports Vuex, which is also built in for scaffolding projects

componentization

If, like us, you have experienced small program business development from scratch, it is recommended to read the chapter “Componentization development of small Programs” for the development of component libraries of official syntax (starting from base library 1.6.3, which officially provides componentization solutions).

  • WePY is similar to Vue to implement a single file component, the biggest difference is the file suffix. Wpy, but the writing method will be different, you can see [mainstream framework use Case 1: WePY] chapter, learning a certain cost, but also quickly adapt to:
export default class Index extends wepy.page {}
Copy the code
  • Mpvue as a port version of Vue, support single-file components, template, script and style are in a.vue file, and Vue writing similar, so familiar with Vue development students will be more adapted to.

engineering

All mini program development relies on official developer tools. Developer tools simple and intuitive, is of great help to debug the small program, now also support tencent cloud (currently we haven’t used, but some developers or helpful for new), can apply for test report view applet in the real operation performance and the effect on mobile devices, but it itself has no similar concepts and tools of front-end engineering.

  • Wepy builds in, initializes the project with the wepy init command. The process is as follows:
  • Wepy-cli determines whether the template is in the remote repository or local, immediately jumps to step 3 if it is local, and continues otherwise.
  • Templates are downloaded from the remote repository and saved locally.
  • Ask the developer a question such as Project name, and create the Project based on the developer’s answer.
  • Mpvue uses vue’s preferred Webpack as a build tool, but provides some of its own plugins and modifications to the configuration file, such as:
  • The HTml-webpack-plugin is no longer needed
  • Change webpack-dev-middleware to webpack-dev-middleware-hard-disk based on webpack-dev-middleware
  • The biggest change is from webpack-loader to mpvue-loader
  • However, the configuration is similar, with environment configuration files, which eventually compile into directory structures and file suffixes supported by small programs.

Comprehensive comparison

Contrast frame Wechat small program mpvue wepy
Grammar specification Small program development specification vue.js Class vue. Js
Label set Small program HTM L + applet Small program
Style specification wxss sass,less,postcss sass,less,styus
componentization No componentization mechanism Vue specification Custom component specifications
Many period of the reuse Do not reuse Support the h5 Support the h5
An automated build No automatic build webpack Framework built-in
It costs A new study Vue learning Vue and wepy
Data management Does not support vuex redux

Personal views on selection

Conclusion: Choose MPVUE.

Wepy vs mpvue.

Reason:

Engineering native development, such as NPM packages (to be introduced in the future), ES7, image compression, PostCss, PUG, ESLint, etc., will not work because there is no engineering. If you want to build your own engineering, you can use wepy or MPvue directly. Mpvue and wepy can be mixed with small program native development. Refer to MPvue-Echart, refer to Wepy. The problem is that wepy doesn’t introduce Webpack ([email protected] still doesn’t), and all of these things have to be built on wheels. Not introducing Webpack was a major hit. Community-maintained mature Webpack is obviously more stable and has more wheels.

Maintaining WEPY is also maintained by the community. Is it official? In fact, wepy’s main developer is only the author, with a link to contrubutors attached. In addition, the official recruitment is later things, and Tencent to have the energy to help maintain wePY, why not spend energy on small program native development? Then there’s the MPvUE, which is maintained by a front end team at Meituan.

Learning cost Vue has a flat learning curve. Mpvue is a subset of Vue. Therefore, the learning cost of MPVUE is lower than that of WEPY. Especially before the technology stack has learned to use Vue.

Mpvue already supports the Web and applets. Because MPVUE is based on AST, it can support Alipay applet and fast application in the future. They have a plan.

Please find it yourself under the requirements pool

Both have their own pits. But I find some of the wePY pits intolerable. For example, the list of repeat components with computed is all the same set of data and 1.x is unresolvable. After developing complete small programs for both Wepy and MPvue, I find wepy has more problems, and some problems cannot be solved due to its architecture design.

mpvue

Vue. Js applet version, fork from vuejs/[email protected], retains the Vue Runtime capability and adds support for the applet platform. Mpvue is a front-end framework for developing small programs using vue.js. The framework is based on the Vue.js core. Mpvue modifs the Runtime and compiler implementations of vue. js so that it can run in a small program environment, thus introducing a complete vue. js development experience for small program development.

Principle of the framework

Two general directions

  • throughmpvueProvides mp Runtime adaptor applet
  • throughmpvue-loaderOutput micro channel small program required file structure and module content.

Seven specific issues

To understand the MPVUE principle you have to understand the Vue principle, that’s the big premise. But it’s going to take a lot of space to explain how Vue works, so let’s do learnVue.

Now assume that you have a general understanding of the Vue principle.

Because Vue uses the Virtual DOM, the Virtual DOM can operate on any platform that supports JavaScript. For example, Vue currently supports browser platforms or WEEx, as well as MP (small programs). Finally, how does the Virtual DOM map to real DOM nodes? Vue provides an adaptation layer for the platform. For browsers, see Runtime/nod-ops.js, for Weex, see Runtime/Nod-ops.js, and for small programs, see Runtime/Nod-ops. Different platforms provide the same interface through the adaptation layer. When the Virtual DOM operates the Real DOM node, it only needs to call the interfaces of the adaptation layer. The internal implementation does not need to be concerned about, as it changes according to the platform.

So the idea is definitely to add an MP platform runtime. The problem is that small programs can not operate DOM, so the implementation of node-ops.js under MP is direct return obj.

You need to patch between the new Virtual DOM and the old Virtual DOM to find the diff. How to update the view of diff after patch is finished, that is, how to add attr, class, style and other DOM attributes to the DOM? Vue has the concept of nextTick for updating views. What should mpvue do with setData for small programs?

Another problem is how to generate the Virtual DOM for small programs. How to compile a template into a render function. It also involves runtime – compilers -vs- only runtime, obviously if you want to improve performance, reduce package size, output WXML, mpVUE also need to provide the ability to precompile. Dynamic components, custom render, and

There are some other questions, but just to sum up

  • 1. How to precompile and generaterender function
  • 2. How to precompile WXML, WXSS, WXS
  • 3. How to patch diff
  • 4. How do I update a view
  • 5. How to establish a small program event agent mechanism to trigger the corresponding vUE component event response in the event agent function
  • 6. How to establish vUE instance and applet Page instance association
  • 7. How to establish the mapping relationship between the applet and vUE life cycle to trigger the VUE life cycle in the applet life cycle

Platform /mp directory structure

.Exercises ── compiler // ├── runtime // To figure out the problem. ├─ util // To figure out the problem.js // The entry of the mpvue-template-compiler. The package.json command automatically generates the mpvue-template-compiler package. ├── entry- Runtime.js ├─ class.jsCopy the code

The following content step by step to answer these questions, also understand the principle

mpvue-loader

Mpvue-loader is an extended version of Vue-Loader, which is similar to superset. In addition to the capabilities of Vue-Loader itself, it also uses mpvue-template-compiler to generate render function.

  • entry

It analyzes the dependency modules, starting with the entries in the WebPack configuration, and packages them separately. App in the entry attribute and its contents will be packaged as WeChat small procedures required by the app. The js/app. The json/app. WXSS, the rest of the page will generate the corresponding page. Js/page. The json/page WXML/page WXSS, An example entry will generate the following files, the contents of which will be explained below:

// webpack.config.js
{
    // ...
    entry: {
        app: resolve('./src/main.js'),               // The app field is identified as the app type
        index: resolve('./src/pages/index/main.js'),   // The remaining fields are recognized as page types
        'news/home': resolve('./src/pages/news/home/index.js')}}// The structure of the output file. ├ ─ ─ app. Js ├ ─ ─ app. Json ├ ─ ─ · app. WXSS ├ ─ ─ components │ ├ ─ ─ card $74Bfae61. WXML │ ├ ─ ─ index $023Eef02. WXML │ └ ─ ─ news $0699930B.w XML ├ ─ ─ news │ ├ ─ ─ home. Js │ ├ ─ ─ home. WXML │ └ ─ ─ home. WXSS ├ ─ ─ pages │ └ ─ ─ index │ ├ ─ ─ index. The js │ ├ ─ ─ index. WXML │ └ ─ ─ index. WXSS └ ─ ─static├ ─ ─ CSS │ ├ ─ ─ app. WXSS │ ├ ─ ─ but WXSS │ └ ─ ─ news │ └ ─ ─ home. WXSS └ ─ ─ js ├ ─ ─ app. Js ├ ─ ─ index. The js ├ ─ ─ the manifest. Js ├ ─ ─ News │ ├ ── home.js ├ ─ vendor.jsCopy the code
  • WXML each.vueWe use the IMPORT syntax of the WXML specification for reuse. We also use the props data if the component is involved. Here’s an example:
<template>
    <div class="my-component" @click="test">
        <h1>{{msg}}</h1>
        <other-component :msg="msg"></other-component>
    </div>
</template>
<script>
import otherComponent from './otherComponent.vue'

export default {
  components: { otherComponent },
  data () {
    return { msg: 'Hello Vue.js! '}},methods: {
    test() {}
  }
}
</script>
Copy the code

The template portion of such a Vue component generates the corresponding WXML

<import src="components/other-component$hash.wxml" />
<template name="component$hash">
    <view class="my-component" bindtap="handleProxy">
        <view class="_h1">{{msg}}</view>
        <template is="other-component$hash" wx:if="{{ $c[0] }}" data="{{ ...$c[0] }}"></template>
    </view>
</template>
Copy the code

You may have noticed that the other-Component (: MSG =” MSG “) is converted to. At runtime, mpvue merges all component instance data into a tree, starting with the root component, and then goes to appData via setData. $C stands for $children. As for the 0, it is a mark that our compiler handles, marking each child component with a specific unique mark. The tree data structure is as follows:

// Here the data structure is an array and index is dynamic
{
  $child: {
    '0'{
      // ... root data
      $child: {
        '0': {
          // ... data
          msg: 'Hello Vue.js! '.$child: {
            / /... data
          }
        }
      }
    }
  }
}
Copy the code
  • wxss

The only difference is that.css is configured to be.wxss. Some of the processing for CSS is described in detail in the postcss-mpvue-wxss and px2Rpx-Loader documents.

App.json/page. Json above 1.1.1

It is recommended that you put app.json/page. Json at the entrance of the page and use the copy-webpack-plugin to copy to the corresponding generation location.

1.1.1 the following

This comes from the app and page entry files, usually main.js, and you need to export default {config: {}}, this can be recognized by our loader as a configuration, which needs to be written as JSON file.

import Vue from 'vue';
import App from './app';

const vueApp = new Vue(App);
vueApp.$mount();

// This is the additional configuration we agreed upon
export default {
    // The data in this field will be populated in app.json/page
    config: {
        pages: ['static/calendar/calendar'.'^pages/list/list'].// Will be filled in webpack
        window: {
            backgroundTextStyle: 'light'.navigationBarBackgroundColor: '#455A73'.navigationBarTitleText: 'Meituan Bus Ticket'.navigationBarTextStyle: '#fff'}}};Copy the code

At this time, we will automatically fill the Pages field in app.json according to the entry page data. The pages field is also customizable, with the convention that pages beginning with a ^ sign are placed at the top of the array.

Style scoped to style in the vue – loader scoped approach is for each style and a attr to mark the module – id, and then also to add after each rule in the CSS/module – id, The result is a CSS “scope space.”

[module-id] = [module-id]; [module-id] = [module-id];

<! -- .vue -->
<template>
    <div class="container">
        // ...
    </div>
</template>
<style scoped>
    .container {
        color: red;
    }
</style>

<! -- vue-loader -->
<template>
    <div class="container" data-v-23e58823>
        // ...
    </div>
</template>
<style scoped>
    .container[data-v-23e58823] {
        color: red;
    }
</style>

<! -- mpvue-loader -->
<template>
    <div class="container data-v-23e58823">
        // ...
    </div>
</template>
<style scoped>
    .container.data-v-23e58823 {
        color: red;
    }
</style>
Copy the code
  • compiler

The content produced is:

(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
// The mpvue-template-compiler uses the AST to precompile a render function to generate the Virtual DOM.
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
  / / _c creates virtual node, reference https://github.com/Meituan-Dianping/mpvue/blob/master/packages/mpvue/index.js#L3606
  / / and https://github.com/Meituan-Dianping/mpvue/blob/master/packages/mpvue/index.js#L3680
  return _c('div', {
    staticClass: "my-component"
  }, [_c('h1', [_vm._v(_vm._s(_vm.msg))]), _vm._v(""), _c('other-component', {
    attrs: {
      "msg": _vm.msg,
      "mpcomid": '0'}})].1)}// staticRenderFns is used for static rendering and will not patch during update. StaticRenderFns is an empty array.
var staticRenderFns = []
render._withStripped = true
var esExports = { render: render, staticRenderFns: staticRenderFns }
/* harmony default export */ __webpack_exports__["a"] = (esExports);
if (false) {
  module.hot.accept()
  if (module.hot.data) {
     require("vue-hot-reload-api").rerender("data-v-54ad9125", esExports)
  }
}

/ * * * / })
Copy the code

compiler

The compiler is related to the precompilation of the template. See “Talk about Vue’s Template compilation” for more information. It’s the same principle.

Mpvue himself implements export {compile, compileToFunctions, compileToWxml}(link) where compileToWxml is used to generate WXML, the code is here.

In addition, mpVUE does not need to provide a run-time compiler, although in theory it could. Because small programs cannot manipulate the DOM, even if the run-time compiler is provided, there is no interface.

Elaborate on the compile process:

1. Parse the Vue file into a template object

// mpvue-loader/lib/loader.js
var parts = parse(content, fileName, this.sourceMap)
Copy the code

If the vue file source is as follows:

<template> <view class="container-bg"> <view class="home-container"> <home-quotation-view v-for="(item, index) in lists" :key="index" :reason="item.reason" :stockList="item.list" @itemViewClicked="itemViewClicked" /> </view>  </view> </template> <script lang="js"> import homeQuotationView from '@/components/homeQuotationView' import topListApi  from '@/api/topListApi' export default { data () { return { lists: [] } }, components: { homeQuotationView }, methods: { async loadRankList () { let {data} = await topListApi.rankList() if (data) { this.dateTime = data.dt this.lists = data.rankList.filter((item) => { return !! item }) } }, itemViewClicked (quotationItem) { wx.navigateTo({ url: `/pages/topListDetail/main? item=${JSON.stringify(quotationItem)}` }) } }, onShow () { this.loadRankList() } } </script> <style lang="stylus" scoped> .container-bg width 100% height 100% background-color #F2F4FA .home-container width 100% height 100% overflow-x hidden </style>Copy the code

Calling parse(content, fileName, this.sourcemap) gives you something like this:

{
  template: {
    type: 'template'.content: '\n
      
       \n 
       
        \n 
        \n 
       \n
      \n'.start: 10.attrs: {},
    end: 251
  },
  script: {
    type: 'script'.content: '\n\n\n\n\n\n\n\n\nimport homeQuotationView from \'@/components/homeQuotationView\'\nimport topListApi from \'@/api/topListApi\'\n\nexport default {\n data () {\n return {\n lists: []\n }\n },\n components: {\n homeQuotationView\n },\n methods: {\n async loadRankList () {\n let {data} = await topListApi.rankList()\n if (data) {\n this.dateTime = data.dt\n this.lists = data.rankList.filter((item) => {\n return !! item\n })\n }\n },\n itemViewClicked (quotationItem) {\n wx.navigateTo({\n url: `/pages/topListDetail/main? item=${JSON.stringify(quotationItem)}`\n })\n }\n },\n onShow () {\n this.loadRankList()\n }\n}\n'.start: 282.attrs: {
      lang: 'js'
    },
    lang: 'js'.end: 946. },styles: [{
    type: 'style'.content: '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.container-bg\n width 100%\n height 100%\n background-color #F2F4FA\n\n.home-container\n width 100%\n height 100%\n overflow-x hidden\n\n'.start: 985.attrs: [Object].lang: 'stylus'.scoped: true.end: 1135. }].customBlocks: []}Copy the code

2. Call the mpue-loader /lib/template-compiler/index.js interface and pass in the HTML template:

var templateCompilerPath = normalize.lib('template-compiler/index')... var defaultLoaders = {html: templateCompilerPath + templateCompilerOptions,
  css: options.extractCSS
    ? getCSSExtractLoader()
    : styleLoaderPath + '! ' + 'css-loader' + cssLoaderOptions,
  js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? babelLoaderOptions : ' '
}

// check if there are custom loaders specified via
// webpack config, otherwise use defaults
var loaders = Object.assign({}, defaultLoaders, options.loaders)
Copy the code
  1. Mpvue /packages/mpvue-template-compiler/build.js:
// mpvue-loader/lib/template-compiler/index.js
var compiled = compile(html, compilerOptions)
Copy the code

The compile method produces the following AST (Abstract Syntax Tree) template, render function, and staticRenderFns

{
  ast: {
    type: 1.tag: 'view'.attrsList: [].attrsMap: {
      class: 'container-bg'
    },
    parent: undefined.children: [{
      type: 1.tag: 'view'.attrsList: [].attrsMap: {
        class: 'home-container'
      },
      parent: {
        type: 1.tag: 'view'.attrsList: [].attrsMap: {
          class: 'container-bg'
        },
        parent: undefined.children: [
          [Circular]
        ],
        plain: false.staticClass: '"container-bg"'.static: false.staticRoot: false
      },
      children: [{
        type: 1.tag: 'home-quotation-view'.attrsList: [{
          name: ':reason'.value: 'item.reason'
        }, {
          name: ':stockList'.value: 'item.list'
        }, {
          name: '@itemViewClicked'.value: 'itemViewClicked'}].attrsMap: {
          'v-for': '(item, index) in lists'.':key': 'index'.':reason': 'item.reason'.':stockList': 'item.list'.'@itemViewClicked': 'itemViewClicked'.'data-eventid': '{{\'0-\'+index}}'.'data-comkey': '{{$k}}'
        },
        parent: [Circular],
        children: [].for: 'lists'.alias: 'item'.iterator1: 'index'.key: 'index'.plain: false.hasBindings: true.attrs: [{
          name: 'reason'.value: 'item.reason'
        }, {
          name: 'stockList'.value: 'item.list'
        }, {
          name: 'eventid'.value: '\'0-\'+index'
        }, {
          name: 'mpcomid'.value: '\'0-\'+index'}].events: {
          itemViewClicked: {
            value: 'itemViewClicked'.modifiers: undefined}},eventid: '\'0-\'+index'.mpcomid: '\'0-\'+index'.static: false.staticRoot: false.forProcessed: true}].plain: false.staticClass: '"home-container"'.static: false.staticRoot: false}].plain: false.staticClass: '"container-bg"'.static: false.staticRoot: false
  },
  render: 'with(this){return _c(\'view\',{staticClass:"container-bg"},[_c(\'view\',{staticClass:"home-container"},_l((lists),function(item,index){ret urn _c(\'home-quotation-view\',{key:index,attrs:{"reason":item.reason,"stockList":item.list,"eventid":\'0-\'+index,"mpcomid" :\'0-\'+index},on:{"itemViewClicked":itemViewClicked}})}))])}'.staticRenderFns: [].errors: [].tips: []}Copy the code

The result of the render function is to return a VNode object.

(function() {
  with(this) {return _c('div', {// Create a div element
      attrs:{"id":"app"}  //div add attribute ID
      },[
        _m(0),  // Static node header, corresponding to the render function staticRenderFns array index 0
        _v(""), // Empty text node
        (message) // A ternary expression to determine whether message exists
         // If it exists, create a p element with text inside and value toString(message).? _c('p',[_v("\n "+_s(message)+"\n ")])
        // If it does not exist, create a p element with text in it and the value is No message.
        :_c('p',[_v("\n No message.\n ")])])}})Copy the code

Where _c is the createElement method of the vue object, _m is renderStatic, _v is createTextVNode, and _s is toString.

// src/core/instance/render.js
export function initRender (vm: Component) {...// bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  vm._c = (a, b, c, d) = > createElement(vm, a, b, c, d, false)
  // normalization is always applied for the public version, used in
  // user-written render functions.
  vm.$createElement = (a, b, c, d) = > createElement(vm, a, b, c, d, true)... }... Vue.prototype._s = toString ... Vue.prototype._m = renderStatic ... Vue.prototype._v = createTextVNode ...Copy the code
  1. Call compileWxml to produce the WXML template, This method eventually calls the compileToWxml method of mpvue/packages/mpvue-template-compiler/build.js to convert the first compile template into the WXML template of the applet
// mpvue-loader/lib/template-compiler/index.js
compileToWxml.call(this, compiled, html)
Copy the code

That answers questions 1 and 2

runtime

The directory structure

.exercises ── events.js // Answer the question.Exercises ── index.js // The entry provides the Vue object, as well$mount6, 7 ├─ Nodes-ops.js // For the implementation of the real DOM, because small programs can not operate the DOM, ├─ patch.js │ ├─ render.js │ ├─ patch.js │ ├─ patch.jsCopy the code

patch.js

This is the same as vue’s createPatchFunction. The old tree and the new tree are still used for patch output diff, but there is an extra line this.$updateDataToMP() for update.

render.js

The two core methods are initDataToMP and updateDataToMP.

InitDataToMP collects data on the VM and then calls the setData rendering of the applet Page example.

The updateDataToMP is updated every time the patch, which is dependent on the collection, finds that the data has changed (see the patch.js code). This part also uses the nextTick and queue. ThrottleSetData was eventually used. The 50 milliseconds is used to control the frequency to solve the performance problems caused by frequent changes to Data that cause large amounts of Data to be transferred.

CollectVmData eventually uses formatVmData. Of particular note is one comment:

GetVmData Obtains all data in the current component, including props and computed data

We also know that the communication between service and view is between two threads. If Data contains a large amount of Data, it will increase the amount of Data to be transmitted, increase the transmission cost, and cause performance degradation.

events.js

As stated on the website, the eventTypeMap is used to allude to each event

import { getComKey, eventTypeMap } from '.. /util/index'
Copy the code
// The event type for the applet to the Web event
export const eventTypeMap = {
  tap: ['tap'.'click'].touchstart: ['touchstart'].touchmove: ['touchmove'].touchcancel: ['touchcancel'].touchend: ['touchend'].longtap: ['longtap'].input: ['input'].blur: ['change'.'blur'].submit: ['submit'].focus: ['focus'].scrolltoupper: ['scrolltoupper'].scrolltolower: ['scrolltolower'].scroll: ['scroll']}Copy the code

The handleProxyWithVue method is used to proxy the applets event to the VUE event.

Also look at the author’s own thoughts on this part

Event broker mechanism: Data updates triggered by user interaction are completed through event broker mechanism. In vue.js code, the event response function corresponds to the method of the component, and vue.js automatically maintains the context. However, there is no similar mechanism in small programs, and a real-time virtual DOM is maintained in the execution environment of wue. js, which is completely corresponding to the view layer of small programs. We think that after events are triggered on small program component nodes, as long as the corresponding nodes on the virtual DOM are found, the corresponding events are triggered. On the other hand, if the vue. js event response triggers a data update, its lifecycle function update is automatically triggered, and the applet data is updated synchronously on this function, and data synchronization is achieved.

GetHandle: Find the corresponding node, then find the handle.

lifecycle.js

In the initMP method, create your own applet App, Page. Implement life cycle related methods, using callHook agent compatible small program App, Page life cycle.

The official document lifecycle says:

Unlike VUE, we trigger vue’s Mounted life cycle after onReady

In this section, onReady is executed before next, and this next callback is ultimately vue’s mountComponent. You can see this in index.js. This part of the code solves the problem of triggering the VUE lifecycle in the applet lifecycle.

export function initMP (mpType, next) {
  // ...
    global.Page({
      // Life cycle function -- listens for the first rendering of the page
      onReady () {
        mp.status = 'ready'

        callHook(rootVueVM, 'onReady')
        next()
      },
    })
  // ...
}
Copy the code

In the onShow applet, use $nextTick to render the data for the first time, referring to render.

export function initMP (mpType, next) {
  // ...
  global.Page({
    // Life cycle function -- listen for page display
    onShow () {
      mp.page = this
      mp.status = 'show'
      callHook(rootVueVM, 'onShow')

      // Only the page needs setData
      rootVueVM.$nextTick((a)= > {
        rootVueVM._initDataToMP()
      })
    },
  })
  // ...
}
Copy the code

When the template is generated by the MPue-Loader, for example, the @click event is changed to bindTap =”handleProxy”, and the event bindings will all use the handleProxy method.

You can look at the mpvue-Loader review above.

Finally, handleProxy calls handleProxyWithVue in event.js.

export function initMP (mpType, next) {
  // ...
    global.Page({
      handleProxy (e) {
        return rootVueVM.$handleProxyWithVue(e)
      },
    })
  // ...
}
Copy the code

index.js

Finally, index.js takes care of the various initializations and mounts.

Why do Class and Style not support components yet

Cause: The current component is implemented using the template tag of the applet. The class and style specified to the component are mounted on the template tag. The template tag does not support the class and style attributes.

Solution: Bind class or style to an props property on the custom component.

/ / component component a vue<template>
  <div class="container" :class="pClass">.</div>
</template>
Copy the code
<script>
    export default {
    props: {
      pClass: {
        type: String,
        default: ''
      }
    }
  }
</script>
Copy the code
<! --PageB.vue-->
<template>
    <component-a :pClass="cusComponentAClass"  />
</template>
Copy the code
<script>
data () {
    return {
      cusComponentAClass: 'a-class b-class'
    }
  }
</script>
Copy the code
<style lang="stylus" scoped>
  .a-class
    border red solid 2rpx
  .b-class
    margin-right 20rpx
</style>
Copy the code

The problem with this is that after style and scoped, the code generated by compiling the template looks like this:

 .a-class.data-v-8f1d914e {
   border: #f00 solid 2rpx;
 }
 .b-class.data-v-8f1d914e {
   margin-right 20rpx
 }
Copy the code

If you want these classes to work, you can’t use scoped style. Instead, you’d better prefix your own a-class and B-class to prevent other files from referencing these styles:

 <style lang="stylus">
  .a-class
    border red solid 2rpx
  .b-class
    margin-right 20rpx
</style>

<style lang="stylus" scoped>
  .other-class
    border red solid 2rpx
    
   ...
</style>
Copy the code
  • Bind the style property to an props property on the component:
 <! . - P component component a vue -- -- >
 <template>
  <div class="container" :style="pStyle">.</div>
</template>
Copy the code
<script>
  export default {
    props: {
      pStyle: {
        type: String,
        default: ''
      }
    }
  }
</script>
Copy the code
<! --PageB.vue-->
<template>
    <component-a :pStyle="cusComponentAStyle"  />
</template>
Copy the code
<script> const cusComponentAStyle = 'border:red solid 2rpx; margin-right:20rpx; ' data () { return { cusComponentAStyle } } </script>Copy the code
<style lang="stylus" scoped>
  ...
</style>
Copy the code

You can also define a styleObject and convert it to a styleString using a utility function, as shown below:

const bstyle = {
  border: 'red solid 2rpx'.'margin-right': '20rpx'
}
let arr = []
for (let [key, value] of Object.entries(bstyle)) {
  arr.push(`${key}: ${value}`)}const cusComponentAStyle = arr.join('; ')
Copy the code
  • Of course, custom components will only change certain CSS styles, passing in individual style values via Pros and then binding via :style is certainly fine:
<! -- component component. Vue -- -- >
 <template>
  <div class="container" :style="{'background-color': backgroundColor}">.</div>
</template>
Copy the code
<script>
    export default {
    props: {
      backgroundColor: {
        type: String,
        default: 'yellow'
      }
    }
  }
</script>
Copy the code
<! -- PageB.vue -->
<template>
    <component-a backgroundColor="red"  />
</template>
Copy the code

The subcontract to load

Package. The json

  • Update: “mpvue – loader”, “^ 1.1.2 – rc. 4” “webpack – mpvue – asset – the plugin” : “^ while”
  • Added: “relative”: “^3.0.2”

Matters needing attention

  • 1.1.2- Rc. 5 Fixed bug with incorrect slot file path generation
  • The 1.1.x version is not very stable yet, and the 1.1.x version is recommended for the time being for projects with high stability requirements

Move the config content in SRC /main.js to the same directory as main.json(new)

export default {
  // config: {... } need to move
}

Copy the code

to

{
 "pages": [
   "pages/index/main"."pages/logs/main"]."subPackages": [{"root": "pages/packageA"."pages": [
       "counter/main"]}],"window": {...}
}
Copy the code

Webpack configuration and upgrade guide

  • This upgrade is intended to adjust the directory structure of generated files, and change the relative path of dependent files from the original write dead absolute path
  • [email protected] relies on [email protected] for dependent resource references
  • You need to create a main.json file in the same directory as main.js, and use webapck-copy-plugin to copy the config information in the build directory
  • The json configuration file is copied by the webapck-copy-plugin, which will not handle dependencies. You can put the image in the root static directory. Use the webapck-copy-plugin to copy the past

build/webpack.base.conf.js

+var CopyWebpackPlugin = require('copy-webpack-plugin')
+var relative = require('relative')

 function resolve (dir) {
   return path.join(__dirname, '.. ', dir)
 }

-function getEntry (rootSrc, pattern) {-var files = glob.sync(path.resolve(rootSrc, pattern))
-  return files.reduce((res, file) = >{-var info = path.parse(file)
-    var key = info.dir.slice(rootSrc.length + 1) + '/' + info.name
-    res[key] = path.resolve(file)
-    return res
-  }, {})
+function getEntry (rootSrc) {+var map = {};
+  glob.sync(rootSrc + '/pages/**/main.js')
+  .forEach(file= >{+var key = relative(rootSrc, file).replace('.js'.' '); + map[key] = file; + +})return map;
 }

   plugins: [
-    new MpvuePlugin()
+    new MpvuePlugin(),
+    new CopyWebpackPlugin([{
+      from: '**/*.json',
+      to: 'app.json'
+    }], {
+      context: 'src/'+ +})new CopyWebpackPlugin([ // Handle images referenced in main.json. Do not include images referenced in code+ {+from: path.resolve(__dirname, '.. /static'),
+        to: path.resolve(__dirname, '.. /dist/static'),
+        ignore: ['*'] +} +])]}Copy the code

build/webpack.dev.conf.js

module.exports = merge(baseWebpackConfig, {
   devtool: '#source-map'.output: {
     path: config.build.assetsRoot,
-    filename: utils.assetsPath('js/[name].js'),
-    chunkFilename: utils.assetsPath('js/[id].js')
+    filename: utils.assetsPath('[name].js'),
+    chunkFilename: utils.assetsPath('[id].js')},plugins: [
     new webpack.DefinePlugin({
    module.exports = merge(baseWebpackConfig, {
     // copy from ./webpack.prod.conf.js
     // extract css into its own file
     new ExtractTextPlugin({
-      filename: utils.assetsPath('css/[name].wxss')
+      filename: utils.assetsPath('[name].wxss')}),module.exports = merge(baseWebpackConfig, {
       }
     }),
     new webpack.optimize.CommonsChunkPlugin({
-      name: 'vendor',
+      name: 'common/vendor'.minChunks: function (module, count) {
         // any required modules inside node_modules are extracted to vendor
         return (
        module.exports = merge(baseWebpackConfig, {
       }
     }),
     new webpack.optimize.CommonsChunkPlugin({
-      name: 'manifest',
-      chunks: ['vendor']
+      name: 'common/manifest',
+      chunks: ['common/vendor']}), -// copy custom static assets
-    new CopyWebpackPlugin([
-      {
-        from: path.resolve(__dirname, '.. /static'),
-        to: config.build.assetsSubDirectory,
-        ignore: ['*'] -} -]),Copy the code

build/webpack.prod.conf.js


    var webpackConfig = merge(baseWebpackConfig, {
   devtool: config.build.productionSourceMap ? '#source-map' : false.output: {
     path: config.build.assetsRoot,
-    filename: utils.assetsPath('js/[name].js'),
-    chunkFilename: utils.assetsPath('js/[id].js')
+    filename: utils.assetsPath('[name].js'),
+    chunkFilename: utils.assetsPath('[id].js')},plugins: [
    var webpackConfig = merge(baseWebpackConfig, {
     }),
     // extract css into its own file
     new ExtractTextPlugin({
-      // filename: utils.assetsPath('css/[name].[contenthash].css')
-      filename: utils.assetsPath('css/[name].wxss')
+      // filename: utils.assetsPath('[name].[contenthash].css')
+      filename: utils.assetsPath('[name].wxss')}),// Compress extracted CSS. We are using this plugin so that possible
     // duplicated CSS from different components can be deduped.
    var webpackConfig = merge(baseWebpackConfig, {
     new webpack.HashedModuleIdsPlugin(),
     // split vendor js into its own file
     new webpack.optimize.CommonsChunkPlugin({
-      name: 'vendor',
+      name: 'common/vendor'.minChunks: function (module, count) {
         // any required modules inside node_modules are extracted to vendor
         return (
     var webpackConfig = merge(baseWebpackConfig, {
     // extract webpack runtime and module manifest to its own file in order to
     // prevent vendor hash from being updated whenever app bundle is updated
     new webpack.optimize.CommonsChunkPlugin({
-      name: 'manifest',
-      chunks: ['vendor']
-    }),
+      name: 'common/manifest',
+      chunks: ['common/vendor'] +})// copy custom static assets
-    new CopyWebpackPlugin([
-      {
-        from: path.resolve(__dirname, '.. /static'),
-        to: config.build.assetsSubDirectory,
-        ignore: ['*'] -} -])]})Copy the code

config/index.js


module.exports = {
     env: require('./prod.env'),
     index: path.resolve(__dirname, '.. /dist/index.html'),
     assetsRoot: path.resolve(__dirname, '.. /dist'),
-    assetsSubDirectory: 'static'.// Do not put the resource aggregation in static directory
+    assetsSubDirectory: ' '.assetsPublicPath: '/'.productionSourceMap: false.// Gzip off by default as many popular static hosts such as@ @26 -.7 +26.7@ @module.exports = {
     port: 8080.// There is no need to open the browser automatically in the applet developer tool
     autoOpenBrowser: false,
-    assetsSubDirectory: 'static'.// Do not put the resource aggregation in static directory
+    assetsSubDirectory: ' '.assetsPublicPath: '/'.proxyTable: {},
     // CSS Sourcemaps off by default because relative paths are "buggy"

Copy the code

Refer to the link

Part of the above content is from:

  • Analysis of wechat Small Program Architecture (I)
  • Micro channel small program architecture analysis
  • 2018 wechat Open class season 7 Shanghai station · Small program special
  • Use iconFont in applet
  • The next step of wechat small program: support NPM, small program cloud, visual programming, support subcontracting
  • mpvue-docs
  • Best practices for developing wechat applet using Mpvue
  • Use vue. js to develop wechat small program: Open source framework MPvUE analysis
  • learnVue

If you want to learn more front-end knowledge, interview skills or some of my personal insights, you can follow my public number to learn together