The public beta of WeChat Mini Program has set off a wave of learning about Mini Program development. It is cross-platform, ready to use, comparable to native experience, complete documentation, and efficient development framework. Mini Program has brought many surprises to developers. Through this article, we will analyze the structure of the mini program and share development experience with everyone.
Features of Mini Program:
## Mini Program Architecture The framework of WeChat Mini Program consists of two parts: View view layer and App Service logic 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. The view layer is rendered using WebView, and the logic layer is run using JSCore. The view layer and the logic layer communicate through the JSBridage of the system layer. The logic layer notifies the view layer of data changes and triggers page updates in the view layer. The view layer notifies the triggered events to the logic layer for business processing. When the mini program is started, the complete package of the mini program will be downloaded from the CDN View (page view)The view layer is written by WXML and WXSS, and is displayed by components. Reflect the data of the logical layer into the view, and at the same time send the events of the view layer to the logical layer. 1. View - WXMLWXML (WeiXin Markup Language)Supports data bindingSupports logical arithmetic and operations Support templates and referencesSupport adding events (bindtap)wxml compiler: wcc Convert wxml file to js Execution method: wcc index.wxml 2. View - WXSSWXSS(WeiXin Style Sheets)Supports most CSS featuresAdd size unit rpx, which can be adapted according to the screen widthUse @import Statements can be imported into external style sheetsDoes not support multi-level selectors - to avoid being destroyed by the structure within the component wxss compiler: wcsc Convert wxss files For js execution mode: wcsc index.wxss3, View – WXSS SelectorsWXSS currently supports the following selectors: 4 , View - ComponentThe applet provides a series of components for developing business functions. The comparison between the functions and HTML5 tags is as follows: Mini Program The component is based on the Web Component standardUsing the Polymer framework to implement the Web Component 5, View - Native ComponentThe component currently implemented by Native There is Native component layer above the WebView layer App Service (Logic Layer)The logic layer processes the data and sends it to the view layer, and at the same time accepts event feedback from the view layer1. Entrance to the App() applet ;Page() Page entrance3. Provides rich APIs, such as WeChat user data, scanning, payment and other WeChat-specific capabilities. 4. Each page has an independent scope and provides modularization capabilities. 5. Data binding, event distribution, life cycle management, routing managementRunning environmentIOS - JSCoreAndroid - X5 JS parser DevTool - nwjs Chrome Kernel1. App Service - BindingData binding uses Mustache syntax (double braces) to wrap variables. Dynamic data comes from the data of the corresponding Page. The data can be modified through the setData method.
Event binding is written in the same way as component attributes, in the form of key and value. Key starts with bind or catch, followed by the type of event, such as bindtap, catchtouchstart, and value is a string. , a function with the same name needs to be defined in the corresponding Page.
2. App Service - Life Cylce
3. App Service - API
API communicates with Native through JSBridge
4. App Service - Router
navigateTo(OBJECT)
Keep the current page, jump to a page in the application, and use navigateBack to return to the original page. The page path can only be five layers
redirectTo(OBJECT)
Close the current page and jump to a page within the application.
navigateBack(OBJECT)
Close the current page and return to the previous page or multi-level page. You can get the current page stack through getCurrentPages()) and decide how many levels to return.
5. Mini program development experience
1. Problems with mini programs
Mini programs still use WebView for rendering, not native rendering
Requires independent development , cannot be run in non-WeChat environments.
Developers cannot extend new components.
The header returned by the server interface cannot be executed, such as: Set-Cookie.
JS libraries that rely on the browser environment cannot be used because they are executed by JSCore and do not have window or document objects.
Local (pictures, fonts, etc.) cannot be used in WXSS.
WXSS is converted into js instead of css for compatibility with rpx.
WXSS does not support cascading selectors.
The applet cannot open the page and cannot launch the APP.
The mini program cannot have the same name as the official account, so the name of the mini program became: Optional Stock+, Didi Chuxing DiDi.
2. Advantages that small programs can learn from
Create a new WebView in advance and prepare for new page rendering.
The View layer and the logic layer are separated, driven by data, and do not directly operate the DOM.
Use Virtual DOM for partial updates.
All use https to ensure security during transmission.
Use offline capabilities.
Front-end component development.
Add rpx unit to isolate device size and facilitate development.
3. "Mini Programs" that break away from WeChat: PWA Progressive Applications
The full name of PWA is Progressive Web Apps. When translated into Chinese, it is Progressive Applications. It was launched by Google on June 15, 2015. concept presented.
Progressive Web Apps are an experience that combines the best features of the web and native apps. It is very advantageous for first-time users, who can access it directly in the browser without installing an application. Over time as users develop a connection with the application, it will become more and more powerful. It loads quickly, can push relevant messages even in weak network environments, and can be added to the home screen like a native application, giving a full-screen browsing experience.
PWA has the following characteristics:
Progressive enhancement - Browsers that support new features get a better experience, while browsers that do not support them maintain the original experience.
Offline access - Service workers can work offline or in environments with poor network speeds.
Native-like application - Use the app shell model to achieve a native application-like experience.
Installable - Allows users to keep apps that are useful to them on their home screen without going through the App Store.
Easy to share - Apps can be easily shared via URL.
Continuous updates - Benefiting from the service worker's update process, the application can always stay updated.
Security - Provide services through HTTPS to prevent network snooping and ensure that content is not tampered with.
Searchable - Allows search engines to find web applications thanks to W3C manifests metadata and service worker registration.
Revisit - Make it easy for users to visit again through features such as message push.
Web App Manifest makes the Web more Native Default configuration, full screen settings, etc.).
Service Workers enhance Web capabilities
Resource offline caching and updating through Service Works
App Shell improves display efficiency
App Shell (application shell) is the most basic HTML, CSS and JavaScript required for the user interface of the application. It is immediately loaded after the first time. It is cached and does not need to be downloaded every time it is used. Instead, only the required data is loaded asynchronously to keep the UI localized.
A todos app developed based on the mini program framework
The following is an introduction to the key points of this app development:
1. The directory structure and configuration of this app will not be introduced in detail. These are described in detail in the Document-Framework section. There is no html and css in this platform, replaced by wxml and wxss. There is almost no difference between wxss and css. The disadvantage is that it is not as powerful as css and supports limited selectors. But the advantage is that since there is only one platform, WeChat, there are almost no compatibility issues and you can use standard and updated CSS technology. Only the tags of those components provided by the platform can be used in wxml. HTML tags cannot be used directly. Examples of how to use each component in wxml can be found in the Document - Components section. So in fact, there is no problem in writing wxml and wxss.
2. wxml supports the following features:
Except for templates and references, all others are used in the todo app, but the details of each feature are not used. , only select appropriate functions according to the needs of the app. I saw an article a few days ago saying that the WeChat applet may be implemented based on the vue framework, so I took a look at the vue documentation. For data binding, conditional rendering, list rendering, and events, we have looked at the usage of vue in detail. In comparison, the features provided by wxml are quite similar to the related features of vue, but there are not so many functions, so it is not easy to directly use the features of the vue framework into small programs. The best practice is still based on the instructions provided in the official documents. If the functions are not mentioned in the official documents, it will definitely not work if you use them by guessing. I checked the prototypes of some objects by printing, and I did not find more instance methods than in the official documents, which shows that the framework function of the mini program is indeed limited.
3. Wxss can actually be written in less or sass, as long as the selector meets the requirements of the framework. Due to time constraints, I didn’t try it in this app.
4. There is no two-way binding. In Vue, a Vue instance is a view-model; updates to data in the view layer will be fed back to the model in real time; updates to the model will also be fed back to the view in real time. In the mini program, there is no two-way binding, and the update of the view will not be directly synchronized to the model; you need to get the data directly from the view layer in the relevant event callback, and then update the model through setData. The mini program will use setData inside the mini program. Then re-render the page. For example, for a single todo item, the toggle operation is:
toggleTodo: function( e ) { var id = this.getTodoId( e, 'todo-item-chk-' ); var value = e.detail.value[ 0 ]; var complete = !!value; var todo = this.getTodo( id ); todo.complete = complete; this.updateData( true ); this.updateStorage(); },
In the above code, the value of the checkbox in a single todo item is obtained through e.detail.value[0], and the complete status of the todo is judged through this value. Finally, inside updateData, the content of the model will be refreshed through the setData method. Only in this way will the statistics at the bottom of the app be updated after the toggle operation.
5. When event binding, parameters cannot be passed, only one event can be passed. For example, in the toggle operation above, I actually wanted to pass the current todo's ID to the callback, but I couldn't do it in every possible way. In the end, I could only handle it through the ID method: binding it in wxml. On the component of the event, add an id. This id cannot be repeated in the entire page, so the id must be prefixed, and then add the todo id value at the end of the id; when the event is triggered, it can be obtained through e.currentTarget.id For the component's id, remove the corresponding id prefix to get the todo's id value. This is a method currently used. I think it is not very elegant. I hope to find a better way to implement it later.
#6. The loading effect is taken into consideration in the app and must be achieved by using the loading attribute of the button component. But loading is just a style control, it does not control whether the button can be clicked repeatedly. Therefore, we must also use the disabled attribute of button to prevent repeated clicks.
The remaining implementation details are in the source code of the following two files. You are welcome to point out the problems.
Source code of index.wxml:
<!--list.wxml--> <view class="container"> <view class="app-hd"> <view class="fx1"> <input class="new-todo-input" value="{{newTodoText}}" auto-focus bindinput="newTodoTextInput"/> </view> <button type="primary" size="mini" bindtap="addOne" loading="{{addOneLoading}}" disabled="{{addOneLoading}}"> + Add </button> </view> <view class="todos-list" > <view class="todo-item {{index == 0 ? '' : 'todo-item-not-first'}} {{todo.complete ? 'todo-item-complete' : ''}}" wx:for="{{todos}}" wx:for-item="todo"> <view wx-if="{{!todo.editing}}"> <checkbox-group id="todo-item-chk-{{todo.id}}" bindchange="toggleTodo"> <label class="checkbox"> <checkbox value="1" checked="{{todo.complete}}"/> </label> </checkbox-group> </view> <view id="todo-item-txt-{{todo.id}}" class="todo-text" wx-if="{{!todo.editing}}" bindlongtap="startEdit"> <text>{{todo.text}}</text> </view> <view wx-if="{{!todo.editing}}"> <button id="btn-del-item-{{todo.id}}" bindtap="clearSingle" type="warn" size="mini" loading="{{todo.loading}}" disabled="{{todo.loading}}"> Clear </button> </view> <input id="todo-item-edit-{{todo.id}}" class="todo-text-input" value="{{todo.text}}" auto-focus bindblur="endEditTodo" wx-if="{{todo.editing}}"/> </view> </view> <view class="app-ft" wx:if="{{todos.length > 0}}"> <view class="fx1"> <checkbox-group bindchange="toggleAll"> <label class="checkbox"> <checkbox value="1" checked="{{todosOfUncomplted.length == 0}}"/> </label> </checkbox-group> <text>{{todosOfUncomplted.length}} left.</text> </view> <view wx:if="{{todosOfComplted.length > 0}}"> <button type="warn" size="mini" bindtap="clearAll" loading="{{clearAllLoading}}" disabled="{{clearAllLoading}}"> Clear {{todosOfComplted.length}} of done. </button> </view> </view> <loading hidden="{{loadingHidden}}" bindchange="loadingChange"> {{loadingText}} </loading> <toast hidden="{{toastHidden}}" bindchange="toastChange"> {{toastText}} </toast> </view>
Source code of index.js:
var app = getApp(); Page( { data: { todos: [], todosOfUncomplted: [], todosOfComplted: [], newTodoText: '', addOneLoading: false, loadingHidden: true, loadingText: '', toastHidden: true, toastText: '', clearAllLoading: false }, updateData: function( resetTodos ) { var data = {}; if( resetTodos ) { data.todos = this.data.todos; } data.todosOfUncomplted = this.data.todos.filter( function( t ) { return !t.complete; }); data.todosOfComplted = this.data.todos.filter( function( t ) { return t.complete; }); this.setData( data ); }, updateStorage: function() { var storage = []; this.data.todos.forEach( function( t ) { storage.push( { id: t.id, text: t.text, complete: t.complete }) }); wx.setStorageSync( 'todos', storage ); }, onLoad: function() { this.setData( { todos: wx.getStorageSync( 'todos' ) || [] }); this.updateData( false ); }, getTodo: function( id ) { return this.data.todos.filter( function( t ) { return id == t.id; })[ 0 ]; }, getTodoId: function( e, prefix ) { return e.currentTarget.id.substring( prefix.length ); }, toggleTodo: function( e ) { var id = this.getTodoId( e, 'todo-item-chk-' ); var value = e.detail.value[ 0 ]; var complete = !!value; var todo = this.getTodo( id ); todo.complete = complete; this.updateData( true ); this.updateStorage(); }, toggleAll: function( e ) { var value = e.detail.value[ 0 ]; var complete = !!value; this.data.todos.forEach( function( t ) { t.complete = complete; }); this.updateData( true ); this.updateStorage(); }, clearTodo: function( id ) { var targetIndex; this.data.todos.forEach( function( t, i ) { if( targetIndex !== undefined ) return; if( t.id == id ) { targetIndex = i; } }); this.data.todos.splice( targetIndex, 1 ); }, clearSingle: function( e ) { var id = this.getTodoId( e, 'btn-del-item-' ); var todo = this.getTodo( id ); todo.loading = true; this.updateData( true ); var that = this; setTimeout( function() { that.clearTodo( id ); that.updateData( true ); that.updateStorage(); }, 500 ); }, clearAll: function() { this.setData( { clearAllLoading: true }); var that = this; setTimeout( function() { that.data.todosOfComplted.forEach( function( t ) { that.clearTodo( t.id ); }); that.setData( { clearAllLoading: false }); that.updateData( true ); that.updateStorage(); that.setData( { toastHidden: false, toastText: 'Success' }); }, 500 ); }, startEdit: function( e ) { var id = this.getTodoId( e, 'todo-item-txt-' ); var todo = this.getTodo( id ); todo.editing = true; this.updateData( true ); this.updateStorage(); }, newTodoTextInput: function( e ) { this.setData( { newTodoText: e.detail.value }); }, endEditTodo: function( e ) { var id = this.getTodoId( e, 'todo-item-edit-' ); var todo = this.getTodo( id ); todo.editing = false; todo.text = e.detail.value; this.updateData( true ); this.updateStorage(); }, addOne: function( e ) { if( !this.data.newTodoText ) return; this.setData( { addOneLoading: true }); //open loading this.setData( { loadingHidden: false, loadingText: 'Waiting...' }); var that = this; setTimeout( function() { //close loading and toggle button loading status that.setData( { loadingHidden: true, addOneLoading: false, loadingText: '' }); that.data.todos.push( { id: app.getId(), text: that.data.newTodoText, compelte: false }); that.setData( { newTodoText: '' }); that.updateData( true ); that.updateStorage(); }, 500 ); }, loadingChange: function() { this.setData( { loadingHidden: true, loadingText: '' }); }, toastChange: function() { this.setData( { toastHidden: true, toastText: '' }); } });
最后需要补充的是,这个app在有限的时间内依据微信的官方文档进行开发,所以这里面的实现方式到底是不是合理的,我也不清楚。我也仅仅是通过这个app来了解小程序这个平台的用法。希望微信官方能够推出一些更全面、最好是项目性的demo,在代码层面,给我们这些开发者提供一个最佳实践规范。欢迎有其它的开发思路的朋友,帮我指出我以上实现中的问题。