Rendering Layer
The rendering layer of the framework is written in WXML and WXSS, and components are used to display the data of the logic layer into views, and send the events of the rendering layer to the logic layer.
- WXML (WeiXin Markup language) is used to describe the structure of the page.
- WXS (WeiXin Script) is a scripting language for small programs. Combined with WXML, it can build the structure of the page.
- WXSS (WeiXin Style Sheet) is used to describe the style of the page.
1.WXML
WXML (WeiXin Markup Language) is a tag language designed by the framework. Combined with the Basic components Event system, it can build the structure of the page.
WXML (WeiXin Markup Language) is a tag language designed by the framework. Combined with the Wxml Syntax Reference
Let's take a look at what WXML has with some simple examples:
1.1 Data Binding
<!-- wxml -->
<view> {{message}} </view>// page.js
Page({
data: {
message: 'Hello MINA!'
}
})1.2 List Rendering
<!--wxml-->
<view wx:for="{{array}}"> {{item}} </view>1.3 Conditional Rendering
<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>// page.js
Page({
data: {
view: 'MINA'
}
})1.4 Template
<!--wxml-->
<template name="staffName">
<view>
FirstName: {{firstName}}, LastName: {{lastName}}
</view>
</template>
<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>// page.js
Page({
data: {
staffA: {firstName: 'Hulk', lastName: 'Hu'},
staffB: {firstName: 'Shang', lastName: 'You'},
staffC: {firstName: 'Gideon', lastName: 'Lin'}
}
})2. WXSS
WXSS (WeiXin Style Sheets) is a style language used to describe the component styles of WXML.
WXSS is used to determine how WXML components should be displayed.
In order to adapt to the vast number of front-end developers, WXSS has most of the features of CSS. At the same time, in order to be more suitable for the development of Luffa applets, WXSS has expanded and modified CSS.
Compared with CSS, WXSS has the following extended features:
- Size unit
- Style import
TIP
- For iOS versions of iOS 8 and below, if you use flexbox layout, you need to add the display: -webkit-flex attribute.
2.1 Size unit
rpx (responsive pixel): can be adapted according to the screen width. The screen width is set to 750rpx. For example, on iPhone6, the screen width is 375px, and there are 750 physical pixels in total. Then 750rpx = 375px = 750 physical pixels, 1rpx = 0.5px = 1 physical pixel.
Depending on the screen width of different devices, the conversion relationship between rpx and px will also be different. For example:
TIP
- Suggestion: When developing mini programs, designers can use iPhone13
- Note: There will inevitably be some glitches on smaller screens. Please try to avoid this during development.
2.2 Style import
You can import external style sheets using the import statement. import is followed by the relative path of the external style sheet to be imported, and ; indicates the end of the statement.
Sample code:
/** common.wxss **/
.small-p {
padding:5px;
}/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}2.3 Inline style
The framework component supports the use of style and class attributes to control the style of the component.
- Style: Static styles are uniformly written into the class. Style receives dynamic styles and will be parsed at runtime. Please try to avoid writing static styles into style to avoid affecting the rendering speed.
/** common.wxss **/
.small-p {
padding:5px;
}- Class: Used to specify style rules. Its attribute value is a collection of class selector names (style class names) in the style rules. The style class names do not need to be included. The style class names are separated by spaces.
<view class="normal_view" />2.4 Selectors.
Currently supported selectors are:
2.5 Global styles and local styles
The styles defined in app.wxss are global styles, which apply to every page. The styles defined in the wxss file of page are local styles, which only apply to the corresponding page and will override the same selector in app.wxss.
3. WXS
WXS (WeiXin Script) is a script segment inlined in WXML. Through WXS, a small amount of processing scripts can be inlined in the template to enrich the data preprocessing capabilities of the template. In addition, WXS can also be used to write simpleWXS event response functions
Syntactically, WXS is similar to JavaScript with a few restrictions. For a complete understanding of WXS syntax, please refer toWXS - Introduction
Here are some simple examples using WXS.
Page rendering:
<!--wxml-->
<wxs module="m1">
var msg = "hello world";
module.exports.message = msg;
</wxs>
<view> {{m1.message}} </view>Page output:
hello worldData processing:
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
}
})<!--wxml-->
<!-- The following getMax function takes an array and returns the value of the largest element in the array -->
<wxs module="m1">
var getMax = function(array) {
var max = undefined;
for (var i = 0; i < array.length; ++i) {
max = max === undefined ?
array[i] :
(max >= array[i] ? max : array[i]);
}
return max;
}
module.exports.getMax = getMax;
</wxs>
<!-- Calls the getMax function in WXS with the "array" in page.js as the parameter -->
<view> {{m1.getMax(array)}} </view>Page output:
54. Event system
4.1 What is an event?
- Events are the communication method from the rendering layer to the logic layer.
- Events can feed back user behavior to the logic layer for processing.
- Events can be bound to components. When the trigger event is reached, the corresponding event processing function in the logic layer will be executed.
- Event objects can carry additional information, such as id, dataset, touches.
4.1 How to use events
Bind an event processing function in the component
For example, bindtap, when the user clicks the component, the corresponding event processing function will be found in the Page corresponding to the page.
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>Write the corresponding event processing function in the corresponding Page definition, and the parameter is event.
Page({
tapName: function(event) {
console.log(event)
}
})You can see that the information logged is roughly as follows:
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}4.3 Use WXS function to respond to events
4.3.1 Background
The effect of frequent user interaction is relatively slow in the mini program. For example, there are two elements A and B on the page. The user makes a touchmove gesture on A and requires B to move with it. The movable-view is a typical example. The response process of a touchmove event is as follows:
The touchmove event is thrown from the rendering layer (Webview) to the logic layer (App Service);
The logic layer (App Service) processes the touchmove event and then changes the position of B through setData.
4.3.2 Solution
The basic idea behind this solution is to reduce the number of communications and allow events to be responded to in the rendering layer (Webview). The framework of the mini-program is divided into a rendering layer (Webview) and a logic layer (App Service). The purpose of this layering is to control the code so that it can only run in the logic layer (App Service). This approach requires the code to run in the rendering layer (Webview).
Use WXS functions to respond to mini-program events. Currently, it can only respond to events of built-in components and does not support custom component events.
In addition to pure logical operations, WXS functions can also access and set component classes and styles through encapsulated ComponentDescriptor instances. For interactive animations, setting style and class is enough.
The WXS function accepts two parameters. The first is event. The event.instance object is added to the original event. The second parameter is ownerInstance, which is a ComponentDescriptor object like event.instance. The specific usage is as follows:
- Bind and register WXS functions for event processing in components:
<wxs module="wxs" src="./test.wxs"></wxs>
<view id="tapTest" data-hi="Weixin" bindtap="{{wxs.tapName}}"> Click me! </view>
**Note: Bound WXS functions must be enclosed in {{}}**- The test.wxs file implements the tapName function:
function tapName(event, ownerInstance) {
console.log('tap Weixin', JSON.stringify(event))
}
module.exports = {
tapName: tapName
}ownerInstance contains some methods that can set the style and class of components.
The example of WXS function is as follows:
var wxsFunction = function(event, ownerInstance) {
var instance = ownerInstance.selectComponent('.classSelector') // Returns an instance of the component
instance.setStyle({
"font-size": "14px" // Support for rpx
})
instance.getDataset()
instance.setClass(className)
// ...
return false // does not bubble up, which is equivalent to calling both stopPropagation and preventDefault
}Where the input parameter event is based on the applet event object with event.instance added to represent the ComponentDescriptor instance of the component that triggered the event.
ownerInstance represents the ComponentDescriptor instance of the component where the component that triggered the event is located. If the component that triggered the event is in the page, ownerInstance represents the page instance.
ComponentDescriptor The currently supported APIs are as follows:
4.3.3 Usage method
WXML defines events:
<wxs module="test" src="./test.wxs"></wxs>
<view
change:prop="{{test.propObserver}}"
prop="{{propValue}}"
bindtouchmove="{{test.touchmove}}"
class="movable">
</view>The change:prop (with the change: prefix in front of the property) above triggers the WXS function when the prop property is set. The value must be enclosed in {{}}. Similar to the observer property in the properties defined by Component, it will be triggered after setData({propValue: newValue}) is called.
TIP
WXS functions must be enclosed in {{}}. WXS functions are triggered when the value of a prop is set, not just when the value changes, so the WxsPropObserver function is called once when the page is initialized.
The WXS file test.wxs defines and exports event processing functions and functions triggered by property changes:
module.exports = {
touchmove: function(event, instance) {
console.log('log event', JSON.stringify(event))
},
propObserver: function(newValue, oldValue, ownerInstance, instance) {
console.log('prop observer', newValue, oldValue)
}
}4.4 Detailed explanation of events
4.4.1 Event classification
Events are divided into bubbling events and non-bubbling events:
- Bubbling events: When an event on a component is triggered, the event will be passed to the parent node;
- Non-bubbling events: When an event on a component is triggered, the event will not be passed to the parent node.
Description
Except for the above table, other custom events of components are non-bubbling events unless otherwise stated, such as the submit event of form, the input event of input, and the scroll event of scroll-view. For details, see Component Overview
4.4.2 Ordinary event binding
The writing of event binding is similar to the properties of components, such as:
<view bindtap="handleTap">
Click here!
</view>If the user clicks this view, the handleTap of the page will be called.
The event binding function can be a data binding, such as:
<view bindtap="{{ handlerName }}">
Click here!
</view>At this time, the this.data.handlerName of the page must be a string that specifies the event handling function name; if it is an empty string, the binding will be invalid (this feature can be used to temporarily disable some events).
In most components and custom components, a colon can be followed by bind, and its meaning remains unchanged, such as bind:tap.
4.4.3 Bind and prevent event bubbling
In addition to bind, you can also use catch to bind events. Unlike bind, catch prevents events from bubbling upwards.
For example, in the example below, clicking the inner view will call handleTap3 and handleTap2 in turn (because the tap event will bubble to the middle view, and the middle view prevents the tap event from bubbling and no longer passes it to the parent node), clicking the middle view will trigger handleTap2, and clicking the outer view will trigger handleTap1.
<view id="outer" bindtap="handleTap1">
outer view
<view id="middle" catchtap="handleTap2">
middle view
<view id="inner" bindtap="handleTap3">
inner view
</view>
</view>
</view>4.4.4 Mutually exclusive event binding
Since the basic library version 1.5.44, in addition to bind and catch, you can also use mut-bind to bind events. After a mut-bind is triggered, if the event bubbles to other nodes, the mut-bind binding function on other nodes will not be triggered, but the bind binding function and catch binding function will still be triggered.
In other words, all mut-bind are 'mutually exclusive', only one of the binding functions will be triggered at the same time, and it will not affect the binding effect of bind and catch at all.
For example, in the following example, clicking the inner view will call handleTap3 and handleTap2 in sequence, and clicking the middle view will call handleTap2 and handleTap1.
<view id="outer" mut-bind:tap="handleTap1">
outer view
<view id="middle" bindtap="handleTap2">
middle view
<view id="inner" mut-bind:tap="handleTap3">
inner view
</view>
</view>
</view>4.4.5 Event capture phase
Touch events support the capture phase. The capture phase is before the bubbling phase, and in the capture phase, the order in which events arrive at the node is exactly the opposite of the bubbling phase. When you need to listen to events in the capture phase, you can use the capture-bind and capture-catch keywords, the latter will interrupt the capture phase and cancel the bubbling phase.
In the following code, clicking the inner view will call handleTap2, handleTap4, handleTap3, and handleTap1 in sequence.
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>If the first capture-bind in the above code is changed to capture-catch, only handleTap2 will be triggered.
<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>4.4.6 Event object
Unless otherwise specified, when a component triggers an event, the processing function bound to the event at the logic layer will receive an event object.
BaseEvent basic event object attribute list:
CustomEvent custom event object attribute list (inherited from BaseEvent):
TouchEvent touch event object attribute list (inherited from BaseEvent):
Special event: Touch events in canvas cannot bubble, so there is no currentTarget.
4.4.7 type
Represents the type of event.
4.4.8 timeStamp
The number of milliseconds from the page opening to the triggering event.
4.4.9 target
The source component that triggers the event.
4.4.10 currentTarget
The current component bound to the event.
TIP
target and currentTarget can refer to the above example. When clicking the inner view, the event object target and currentTarget received by handleTap3 are both inner, while the event object target received by handleTap2 is inner, and the currentTarget is middle.
4.4.11 dataset
Some custom data can be attached to the component node, so that these custom node data can be obtained in the event for event logic processing.
In WXML, these custom data start with data- , and multiple words are connected by hyphens - . In this writing style, hyphens are converted to camel case, and uppercase characters are automatically converted to lowercase characters, such as:
- data-element-type , which will eventually be rendered as event.currentTarget.dataset.elementType ;
- data-elementType , which will eventually be rendered as event.currentTarget.dataset.elementtype .
<view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>Page({
bindViewTap:function(event){
event.currentTarget.dataset.alphaBeta === 1 // - will convert to camel case
event.currentTarget.dataset.alphaBeta === 2 // uppercase will be converted to lowercase
}
})4.4.12 touches
touches is an array, each element of which is a Touch object (the touches carried in the canvas touch event is a CanvasTouch array) representing the touch point currently on the screen.
Touch object
CanvasTouch object
4.4.13 changedTouches
changedTouches data format is the same as touches, indicating a touch point that has changed, such as from nothing to something (touchstart), position change (touchmove), from something to nothing (touchend, touchcancel).
4.4.14 detail
Data carried by custom events, such as the submit event of a form component will carry user input, and the error event of a media will carry error information. For details, see the definitions of each event in the definition. Component Overview The x, y with the same pageX, pageY in the detail of the click event represent the distance from the upper left corner of the document.
The x and y in the click event detail are the same as pageX and pageY, representing the distance from the upper left corner of the document.
5. Basic components
The framework provides developers with a series of basic components. Developers can quickly develop by combining these basic components. For detailed introduction, please refer to Component Overview The x, y with the same pageX, pageY in the detail of the click event represent the distance from the upper left corner of the document.
What is a component:
- Components are the basic building blocks of the view layer.
- A component usually includes a start tag and an end tag. Attributes are used to modify the component. The content is within the two tags.
<tagname property="value">
Content goes here ...
</tagname>TIP
All components and attributes are lowercase and connected by a hyphen-
Attribute type:
Public properties
All components have the following properties:
Special properties
Almost all components have their own defined properties, which can modify the function or style of the component. Please refer to the definitions of each Component Overview .
6. Get node information on the interface
6.1 WXML node information
Node information query API Can be used to obtain node properties, styles, position on the interface and other information.
The most common usage is to use this interface to query the current position of a node and the scroll position of the interface.
Sample code:
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function (res) {
res[0].top // The upper boundary coordinate of the #the-id node.
res[1].scrollTop // The vertical scroll position of the display area.
})In the above example, #the-id is a node selector, which is similar to CSS selectors but slightly different. SelectorQuery.select Related instructions in the chapter.
In custom components or pages containing custom components, it is recommended to use this.createSelectorQuery instead wx.createSelectorQuery , which ensures that nodes are selected in the correct range.
6.2 WXML node layout intersection state
Node layout intersection state API can be used to monitor the intersection state of two or more component nodes in the layout position. This set of APIs can often be used to infer whether certain nodes can be seen by users and what proportion can be seen by users.
The main concepts involved in this set of APIs are as follows.
- Reference node: The reference node to be monitored, and its layout area is taken as the reference area. If there are multiple reference nodes, the intersection of their layout areas will be taken as the reference area. The page display area can also be used as one of the reference areas.
- Target node: The target of monitoring, by default, can only be one node (when using the selectAll option, multiple nodes can be monitored at the same time);
- Intersection area: The intersection area of
- Intersection ratio: The ratio of the intersection area to the reference area;
- Threshold: If the intersection ratio reaches the threshold, the callback function of the listener will be triggered. There can be multiple thresholds;
Sample code:
Page({
onLoad: function(){
wx.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => {
res.id // target node id
res.dataset // target node dataset
res.intersectionRatio // the proportion of the intersection area to the layout area of the target node
res.intersectionRect // The intersection region
res.intersectionRect.left // the coordinates of the left boundary of the intersection region
res.intersectionRect.top // the upper boundary of the intersection region
res.intersectionRect.width // width of the intersection region
res.intersectionRect.height // height of the intersection region
})
}
})The following sample code can trigger the callback function every time the target node (specified by the selector .target-class) enters or leaves the page display area.
Sample code:
Page({
onLoad: function(){
wx.createIntersectionObserver(this, {
thresholds: [0.2, 0.5]
}).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => {
res.intersectionRatio // the proportion of the intersection area to the layout area of the target node
res.intersectionRect // The intersection region
res.intersectionRect.left // the coordinates of the left boundary of the intersection region
res.intersectionRect.top // the upper boundary of the intersection region
res.intersectionRect.width // width of the intersection region
res.intersectionRect.height // height of the intersection region
})
}
})Description
The intersection area with the page display area does not accurately represent the area visible to the user, because the area involved in the calculation is the 'layout area', which may be clipped and hidden by other nodes (such as nodes with overflow style as hidden in ancestor nodes) or covered (such as fixed positioned nodes) when drawing.
In custom components or pages containing custom components, it is recommended to use this.createIntersectionObserver instead wx.createIntersectionObserver , which ensures that nodes are selected in the correct range.
7.Responding to display area changes
7.1Display area size
The display area refers to the area in the mini program interface that can be freely laid out and displayed. By default, the size of the mini program display area will not change since the page is initialized, but the following three methods can change this default behavior.
Enable screen rotation support on mobile phones
Mini Programs Support Screen Rotation on Mobile Phones To enable the pages in the Mini Programs to support screen rotation, set 'pageOrientation': 'auto' in the window section of app.json or configure 'pageOrientation': 'auto' in the page json file.
The following is an example of enabling screen rotation in a single page json file.
Code example:
{
"pageOrientation": "auto"
}If the page adds the above declaration, the page will rotate with it when the screen rotates, and the display area size will also change with the screen rotation. pageOrientation can also be set to landscape, indicating that it is fixed to landscape display.
- Whether a page supports screen rotation cannot be configured separately on iPad
Code example:
{
"resizable": true
}If the Mini Program adds the above declaration, the Mini Program will rotate with it when the screen rotates, and the display area size will also change with the screen rotation.
Description
Whether a page supports screen rotation cannot be configured separately on iPad
7.2 Media Query
Sometimes, the layout of the page will be different for display areas of different sizes. In this case, media query can be used to solve most problems.
Code example:
.my-class {
width: 40px;
}
@media (min-width: 480px) {
/* Style rules that only work on screens 480px or wider */
.my-class {
width: 200px;
}
}7.3 Screen rotation event
Sometimes, using media query alone cannot control some fine layout changes. In this case, js can be used as an aid.
To read the display area size of the page in js, you can use selectorQuery.selectViewport
Events where the page size changes can be monitored using the onResize of the page. For custom components, you can use the resize lifecycle to monitor the size information of the display area returned in the callback function.
Code example:
Page({
onResize(res) {
res.size.windowWidth // The width of the new display area.
res.size.windowHeight // the height of the new display area
}
})Component({
pageLifetimes: {
resize(res) {
res.size.windowWidth // The width of the new display area.
res.size.windowHeight // the height of the new display area
}
}
})In addition, you can also use wx.onWindowResize to monitor (but this is not a recommended method).
8. Page animation
8.1 Common methods of interface animation
In mini programs, CSS gradients and CSS animations can usually be used to create simple interface animations.
During the animation process, you can use bindtransitionend bindanimationstart bindanimationiteration bindanimationend to monitor animation events.
Description
These events are not bubbling events and need to be bound to the node where the animation actually occurs to take effect.
At the same time, you can also use the wx.createAnimation interface to dynamically create simple animation effects (the new version of the mini program basic library recommends using the following keyframe animation interface instead).
8.2 Advanced animation methods
In some complex scenarios, the above animation methods may not be applicable.
WXS responds to events The method of using WXS to respond to events can dynamically adjust the style attribute of the node. By constantly changing the value of the style attribute, you can achieve animation effects. At the same time, this method can also dynamically generate animations based on user touch events.
Continuously using setData to change the interface method can also achieve animation effects. This can change the interface arbitrarily, but it usually causes a large delay or freeze, and even causes the mini program to freeze. At this time, you can change the setData of the page to setData in Custom Components n setData to improve performance.