Original address: github.com/berwin/Blog…

If I write it up front, Recently frequent someone piracy my original article, I book piracy, copying my essay, based on my articles and write a new article and say their research results, based on my article for an open source project then my original, according to my book twice rewriting and open source to making thousands of Star, and so on and so forth.

I would like to write the core knowledge to share with you is for your learning and growth, and only for learning and academic exchange.

I do not want my article to be reprinted, rewritten, pirated or copied in any form, or develop a small program framework based on my article to cheat STAR and other acts of infringing my copyright. Once found, I will be held responsible.

If I can’t control the piracy, I will consider shutting down the blog and turning on the whitelist mode, where only the people on the whitelist can read my articles. In order to help more people learn and progress, please refuse piracy!

In September last year, I wrote an article entitled “Basic implementation principle of small program and some thoughts”, which described my process of implementing (exploring) small program and some thoughts, and revealed a fact: small program is based on Web technology.

I went round and round and tried many solutions, but there were some problems with different solutions. I tried to find the perfect solution, but my efforts paid off and I finally found it.

At the end of the previous article, I mentioned that it was always going to go back to dual threading, which is the right direction, and it’s been proven that it’s the right direction.

However, how to implement a small program based on two threads is only mentioned in the previous article, because it was only a preliminary idea, and has not been implemented.

Why dual threading

As I tried and tried many solutions, I began to rethink what applets really were.

After thinking, the conclusion is that from the perspective of product technology, applets have two characteristics:

  1. Free installation
  2. The ability to access the OS through the host APP

There are many definitions and characteristics of small programs, but I think the most fundamental characteristics are the above two points

From a technical point of view, small programs should ensure at least four points: safety, stability, performance, simple.

  • Security, refers to the security of small programs, because small programs provide developers with higher permissions, small program developers have a lot of OS capabilities of the API, if developers use these API to do some malicious things, then it will be a disaster for the user (just open a small program, the result was hacked).
  • Stability refers to the stability of a small program. When a small program crashes or freezes, it should not affect the host APP or other small programs. Most of the gadgets on mobile phones can only open one widget at a time, so readers may not understand what stability is. PC can be open at the same time a number of small procedures, according to the product shape is different, some product in the form of small programs can be run in a web page (multiple small programs running in the same page) at the same time, this time a small program stuck (write an infinite loop, for example), it should not lead to other small program and host environment also follow.
  • Performance, to ensure the execution of small programs, rendering efficiency to be efficient enough.
  • Simple, small programs should be simple enough for developers, preferably without learning directly.

It’s actually very easy to think about how to implement small programs from a product perspective.

This may be implemented differently depending on the host environment

If the host environment is a browser, any web page has the “install free” feature, the browser only has to provide some OS capabilities of the API, this is the applets.

This is not very different from PWA, which is probably why many people compare applets to PWA.

If the host environment is a super APP (iOS or Android), any WebView has the feature of “installation-free”, super APP only needs to provide some OS capable API to the code in the WebView, the routine is the same.

From a technical point of view, the single-threaded approach has to be abandoned entirely for security and stability issues, which is why the “dual-threaded” reference at the end of the last article was the right approach.

In fact, dual threading is not enough. If you allow multiple applets to run at the same time, then dual threading will not solve the problem of stability. The correct way is to use “multi-threading”, where the code of different applets is executed on different threads.

If only one small program is executed at a time, then dual threads (UI thread and logic thread) can meet the “safety” and “stability” requirements. But running multiple applets at the same time requires multiple logical threads to execute the logic of different applets at the same time, and multiple UI threads to render the UI of different applets at the same time.

multithreading

Multithreading not only solves the problem of multiple applets coexisting, each applets also needs to have its own multiple UI threads.

That said, to get rid of SPA (single-page apps) altogether, small apps should be multi-page apps, not single-page apps.

The reason for using multiple UI threads is that it is difficult for a single-page app to simulate the experience of switching pages in a native app. So A single page application, when you go from page A to page B, the canvas is the same canvas, you just erase the content of page A and draw the UI of page B. This principle can be awkward in many scenarios, for example: A user develops A news feed applet that has two pages, page A for news feed and page B for news details. After A long slide, users find an interesting news and click to jump to the details page. After reading the news, they want to go back to the previous position of page A to continue browsing. The single-page application erased page A, so when you go back to PAGE A from B, you will find that page A has been refreshed again, which is A terrible experience.

If the host environment is a browser, you can use multiple IFrames on the page, create a new IFrame to cover the top of the page, when the back, delete the top iframe. If the host environment is a super APP on your phone, change the iframe to a WebView, and the routine is the same.

The key to dual threading is to keep logical threads as “light” as possible and to minimize the frequency of communication between threads. Previous failures on dual threading were mainly because logical threads were very heavy and communication between threads was very frequent, as mentioned in the previous article.

Multithreaded architecture

In order to keep the logical thread as “light” as possible and reduce the frequency of communication between the logical thread and the UI thread, the logical thread’s responsibilities should be designed to do only two things:

  1. Execute user functions (lifecycle, methods, etc.)
  2. Sending and receiving status data

The user’s functions are executed to give the developer the ability to modify the state data, which is sent to the UI thread only after the component is initialized and the developer modifies the state data. All the rest of the logic is done under the UI thread.

The goal is to keep the logical thread as “light” as possible (just executing functions provided by the developer) and reduce the frequency of communication between the logical thread and the UI thread (only during initialization and state data changes).

The architecture is designed this way because otherwise there are two fatal problems: “performance” and “native capability limitations.”

In the previous article, “Basic Implementation principles and Some Thoughts of small programs”, there was a problem with an alternative solution (and the logic thread is heavy and the UI thread is light). Here is a brief review of this failed solution:

The UI thread is only responsible for receiving instructions (basic instructions for creating elements, modifying elements, inserting elements, routing jumps, event bindings, etc.), and the logical thread does everything else (calculating which instructions to issue with the state data provided by the developer). There are two fatal problems with this solution: “performance” and “native capability limitations.”

Because the logical thread after calculation, if want to render a complete UI page, you need to send a lot of drawing instructions (create elements, insert, modify, elements, modify attributes, etc.), instruction in transmission between threads is asynchronous, will lead to a command signal transmission between interval, because the order is very small, lead to the interval time than instruction still have a long time, as shown in the figure below:

The yellow JavaScript execution squares are the execution of instructions, and there is a lot of idle time between the two instructions because the execution signal takes time to pass. So the process behind it is to take a command and draw it, and then wait a little while and then take a new command and draw it again. When the number of instructions is large, the performance problem is very obvious, and drawing an entire UI is very slow.

So it’s important to keep the logical thread as “light” as possible, and to reduce the frequency of communication between the logical thread and the UI thread by changing the “instructions” to “data” that are transferred between threads.

Communication module

Finished talking about the overall architecture design and consideration, then talk about the communication module, as the name implies, the responsibility of the communication module is responsible for the communication function between different threads.

In multi-threaded architecture, communication module is very important, but the implementation of communication module is not complicated, and the bottom layer depends on the message channel provided by the host environment. For example, when the Master layer controls iframe, the underlying layer can use the postMessageAPI.

Note that the communication module sends signals according to the channel id in order to send signals to the specified location accurately. The standard of the channel id should be set to [mid]_[PID]_[CID].

  • mid = MiniApp ID
  • pid = Page ID
  • cid = Component ID

The reason for the signal specification is to execute multiple small programs at the same time, the signal can still find the specified location accurately.

Another important point to note is that the official component logic execution environment and the developer component logic execution environment are not in the same place, and the communication module is responsible for resolving and sending signals to the specified location.

The logic of communication is roughly as follows:

Because Web Worker is a sandbox environment with limited capabilities, the user’s logical code is executed in the sandbox, while the logical part of the official component needs to be executed in the UI layer with full capabilities.

So the communication module needs to figure out whether the signal should be sent to the sandbox or to the UI layer.

Rendering process

The rendering process of applets is essentially no different from that of popular front-end frameworks (vue.js and React) in the market. Applets will let developers write a template for rendering, and then write a JS for controlling the logic of applets and updating data. This is the same with vue.js and React.

UI = render(state)
Copy the code

To be more specific:

UI = template(data)
Copy the code

In applets, templates are readily available, the template framework written by the developer is directly available, and data is continuously transferred from the logical layer to the UI layer at the beginning of the lifecycle.

So developer-written templates are loaded directly into the UI layer, while developer-written JS is loaded into the sandbox for execution. When the project life cycle is initialized, the data is transferred to the UI layer, which takes the data and renders it with the template. This is the first rendering.

Each time the developer updates the state via JS, the state is transferred to the UI layer for rendering.

Of course, some optimizations will be made so that each data update only changes the parts of the UI that are associated with the data update to improve performance. You can do this with VirtualDOM, or with the finest-grained Watcher like Vue. Js 1.0, you can monitor the data bound to each DOM tag for updates.

routing

When I talked about multithreading earlier, I mentioned that applets should completely abandon SPA (single-page application) and make multi-page application, because multi-page application can retain the state of the previous page.

So the internal routing is based on the architecture of the multi-page application, based on this principle routing is not complicated.

First, the behavior that triggers routing can come from the UI layer or from the sandbox. The signal at the UI level might be a user clicking the back button, or some kind of gesture that backs back to the previous page, signaled by the host environment. The signal sent in the sandbox is the signal sent by the developer through the API provided by the official.

Either a signal from the UI layer “user” or a signal from the sandbox “developer” should be sent to the Master layer, which controls the routing.

Routing signals have two behaviors: forward and backward.

Forward signal: If the hosting environment is a browser and the UI page is hosted by an IFrame, the forward signal corresponds to creating a new IFrame on top of the previous page and initializing the life cycle of the new page. The principles of other hosting environments and containers that host UI pages are consistent with the pure Web solution.

Back signal: The behavior of the back signal is to delete a page from the top of the page stack (carrier of the UI interface, e.g. Iframe, WebView, etc.), the official API provided to developers can back up several layers through parameter Settings, corresponding behavior is to delete several pages, delete stack. length-1 at most (back to the home page).

The figure above shows the internal flow chart of the route when a developer calls the API to open a new page. After receiving the signal, the Master will create an iframe and push it onto the page stack. When the page is created, it will bring the basic JS library into the page. After the page is created, the basic JS library will perform the initialization operation immediately. The Master then sends a signal into the sandbox. The process is designed this way because it serves two purposes:

  1. The developer needs to be notified that the page has been created successfully.
  2. Create the “root component” of the new page in the sandbox and officially start the life cycle and rendering process of the new page.

The life cycle

The life cycle can peek into the internal running time sequence. In multi-threaded architecture, because the rendering layer and logic layer are executed separately, the life cycle relies on the communication module to transmit signals to control the different stages of the small program.

The entry point of the applet is the Master. The Master is not in the UI layer, nor is it in the sandbox. It is the one God perspective that overrides everything. In the case of a browser, Master is index.js, which is the entry point to the entire program.

In index.js, in addition to doing some initialization work, the most important thing is that it needs to start the sandbox environment (such as Web Worker). Once the sandbox environment is started, it also does some initialization work for the environment. Initialization of the Master and sandbox environment is the first step after starting the applet from the index.js entry.

When the sandbox environment is initialized, it needs to send a signal to the Master that the sandbox environment is ready. At this point, the Master will create the first page of the applet, the home page of the applet, according to the configuration set by the developer.

The configuration information contains the routing information of the applet, as well as the home page of the applet and the corresponding components.

When the first page is pushed to the page stack, the page starts to initialize. At this time, the UI layer can get the information of the component tree of the page and the corresponding template of each component, and the page will be initialized from the root component. During the initialization process, the UI layer components will send signals to the Master. Notifying the Master component that initialization is complete, the Master needs to send a signal to the sandbox after receiving the signal, the sandbox needs to create a corresponding component in the sandbox environment to execute the developer’s JS logic. That is to say, the same component is being split into two parts, the initialization in the UI layer, respectively, and then in the sandbox initialization, component is responsible for rendering and processing in the UI layer UI events, such as the component of the sandbox is mainly responsible for developers to define function called, and to provide some developer API changes the state of the components, the entire process as shown in the figure below:

When a logical layer component is initialized, two lifecycle functions are triggered: “beforeCreate” and “created”.

When logic layer after the component initialization, will send a signal to the render layer components, notify the rendering component logic layer component side is initialized, state information and components will be sent to the render layer components, rendering layer components after receiving the signal, can take the data for the first time to do rendering operation, as shown in the figure below:

When rendering is done for the first time, the render layer component sends a signal to the logical layer component, which triggers a lifecycle “onReady” to notify the developer that it has rendered for the first time.

Later, whenever the developer calls setData to modify the data, the logical layer component will send the latest data to the corresponding component of the rendering layer, which will go through the rendering process again with the latest state.

If the user clicks on one of the components during the binding element of the event, the UI component will send signal to the logical layer in the corresponding components, and will send some event information in the past, after receiving the signal invocation logic layer component developers binding function and event information through the parameter passed to the developers, the end of the entire process.

Application scenarios of underlying technologies

The previous introduction of a lot of underlying technology principle, although this technology is based on the “small program in the Web environment” invented, but the small program is only one of the many application scenarios of this technology, this technology can support a lot of application scenarios.

WebIDE plug-in system

WebIDE plug-in system and applets have the same characteristics, IDE plug-ins also need to meet at the same time: security, stability, performance, simplicity.

  • Security: Ensure that plugins provided by third-party developers are harmless to users.
  • Stable: Because IDE plug-ins require N plug-ins to execute simultaneously, the failure or poor performance of one plug-in should not affect the health of other plug-ins or the overall IDE.
  • Performance: The performance of the plug-in needs to be smooth.
  • Simple: Make sure third-party developers don’t have to spend too much money developing plug-ins.

The difference is that the applet scenario is that only one applet can be executed in an “environment”, whereas the IDE requires N plug-ins to be executed simultaneously in the same “environment”.

When I say “environment”, I mean: in a Web environment, usually the entry file of the page is index.html, so an environment will only run one applet.

An environment running multiple plug-ins can be interpreted as running a large number of plug-ins at the same time in index. HTML, that is, running a large number of plug-ins on the same page at the same time, and these plug-ins run in isolation from each other on the same page. In this scenario, “stability” is particularly important. The entire page should not be affected. It is also important that plug-in A should not have access to or modify the UI of plug-in B because it is executed on the same page.

In summary, the technical principles described in this article also apply to the Plug-in architecture of WebIDE.

Figma plug-in system

Figma plug-in system and the technical system will be different, the technical principle introduced in this paper is also suitable for Figma plug-in system such requirements.

App Store

If you treat a web page as an entry point to the Internet, or as an operating system, and treat the small program running in the web page as an application. So using this technology, you can make this web page run a large number of different types of applications at the same time, and realize the ability to turn the web page into an operating system.

Other scenarios

The application of this technology extends far beyond the above examples, and can be used in any scenario that requires third-party developers to participate and ultimately run applications on their own platforms.

conclusion

This paper discusses all aspects of the implementation of small program based on Web technology in detail, and introduces how to make small program based on Web technology have four basic characteristics, such as security, stability, performance, simplicity.

At the same time, it is also mentioned in the end of this paper that although the technology was born in the scene of small program, the underlying technology is not limited to small program, but also has a wide range of applications in other scenarios.

An advertisement is interrupted. The team only entered P7 before, but now several P6+ HCS are open. The opportunity is rare, the students who have the idea to join me in tmall Double eleven can send a resume :[email protected]