ホームページ > 記事 > WeChat アプレット > WeChat ミニ プログラム アーキテクチャの分析と例
WeChat ミニ プログラムのパブリック ベータ版は、ミニ プログラム開発に関する学習の波を引き起こしました。これは、クロスプラットフォームで、すぐに使用でき、ネイティブ エクスペリエンスに匹敵し、完全なドキュメントであり、効率的な開発フレームワークにより、多くの驚きをもたらしました。開発者。この記事を通じて、ミニプログラムの構造を分析し、開発経験を皆さんと共有します。
ミニ プログラムの特徴:
ミニ プログラム アーキテクチャ
WeChat ミニ プログラムのフレームワークは、ビュー レイヤーと App Service ロジック レイヤーの 2 つの部分で構成されます。ビュー レイヤーは、ページ構造をレンダリングするために使用され、AppService は、AppService をレンダリングします。レイヤーは論理処理とデータ要求、インターフェイス呼び出しに使用され、2 つのスレッドで実行されます。
ビュー層はWebViewを使用してレンダリングされ、ロジック層はJSCoreを使用して実行されます。
ビュー層とロジック層はシステム層の JSBridage を介して通信し、ロジック層はデータ変更をビュー層に通知し、ビュー層でトリガーされたイベントをビジネスのロジック層に通知します。処理。
アプレットが開始されると、アプレットの完全なパッケージが CDN からダウンロードされます
View (ページビュー)
View レイヤーは WXML と WXSS によって記述され、コンポーネントによって表示されます。
論理層のデータをビューに反映すると同時に、ビュー層のイベントを論理層に送信します。
1. 表示 - WXML
WXML (WeiXin Markup Language)
データバインディングをサポート
論理演算と演算をサポート
テンプレートと参照をサポート
イベントの追加をサポート(bindtap)
wxmlコンパイラ: wcc wxmlファイルを変換to js 実行メソッド: wccindex.wxml
2. ビュー - WXSS
WXSS (WeiXin Style Sheets)
ほとんどの CSS 機能をサポート
画面の幅に応じて調整できるサイズ単位 rpx を追加します
import ステートメントは外部スタイル シートをインポートできます
マルチレベル セレクターはサポートしていません - コンポーネントの内部構造による損傷を回避します
wxss コンパイラ: wcsc wxss ファイルを js に変換します 実行メソッド: wcsc Index.wxss
3、ビュー – WXSS セレクター
WXSS は現在、次のセレクターをサポートしています:
4. ビュー – コンポーネント
アプレットは、ビジネス関数を開発するための一連のコンポーネントを提供します。関数と HTML5 タグの比較は次のとおりです。
アプレットのコンポーネントは Web コンポーネント標準に基づいていますPolymer フレームワークを使用して Web コンポーネントを実装します5. ビュー - ネイティブ コンポーネント
現在ネイティブによって実装されているコンポーネントは
WebView レイヤーの上にあります
ロジック層がデータを処理しますビュー層に送信し、同時にそれを受け取ります
1. App() アプレットへの入り口; Page() ページへの入り口 3. WeChat ユーザー データ、スキャン、支払い、その他の WeChat 固有の機能。 4. 各ページには独立したスコープがあり、モジュール機能を提供します。 5. データバインディング、イベント配布、ライフサイクル管理、ルーティング管理IOS - JSCoreAndroid - X5 JS parserDevTool - nwjs Chrome kernel1.データ バインディングは、Mustache 構文 (二重中括弧) を使用して変数をラップします。データは、setData メソッドを通じて変更できます。
イベント バインディングは、コンポーネントの属性と同じ方法でキーと値の形式で記述されます。キーはバインドまたはキャッチで始まり、その後にイベントのタイプ (bindtap、catchtouchstart など) が続き、値は必要な文字列です。対応するページに入力する同じ名前の関数を に定義します。
3. アプリサービス - API
APIはJSBridge
を介して通信します。 n ナビゲート先( OBJECT)
現在のページを保持し、アプリ内のページにジャンプし、navigateBack を使用して元のページに戻ります。ページ パスは 5 つのレイヤーのみです
redirectTo(OBJECT)
現在のページを閉じて、アプリケーション内のページにジャンプします。
navigateBack(OBJECT)
現在のページを閉じて、前のページまたは複数レベルのページに戻ります。 getCurrentPages()) を通じて現在のページ スタックを取得し、返すレベルの数を決定できます。
5. ミニ プログラムの開発経験
1. ミニ プログラムの問題
ミニ プログラムは依然としてネイティブ レンダリングではなく、WebView を使用してレンダリングします
独自に開発する必要があり、WeChat 以外の環境では実行できません。
開発者は新しいコンポーネントを拡張できません。
サーバー インターフェイスによって返されるヘッダー (Set-Cookie など) は実行できません。ブラウザ環境に依存する
JSライブラリはJSCoreによって実行され、ウィンドウやドキュメントオブジェクトを持たないため使用できません。
ローカル(画像、フォントなど)はWXSSでは使用できません。
WXSS は、rpx との互換性のために CSS ではなく js に変換されます。
WXSS はカスケード セレクターをサポートしていません。
アプレットはページを開けず、アプリを起動できません。
ミニ プログラムは公式アカウントと同じ名前を持つことができないため、ミニ プログラムの名前は「Optional Stock+」、「Didi Chuxing DiDi」になりました。
2. 小さなプログラムが学べる利点
事前に新しい WebView を作成し、新しいページのレンダリングを準備します。
ビュー層とロジック層は分離されており、データによって駆動され、DOM を直接操作しません。
部分的な更新には仮想 DOM を使用します。
送信中のセキュリティを確保するためにすべてhttpsを使用します。
オフライン機能を使用します。
フロントエンドコンポーネントの開発。
デバイスのサイズを分離し、開発を容易にするために rpx ユニットを追加します。
3. WeChatから独立した「ミニプログラム」: PWA Progressive Applications
PWAの正式名称はProgressive Web Appsで、中国語ではプログレッシブアプリケーションと訳され、2015年6月15日にGoogleによって提案された概念です。
プログレッシブ Web アプリは、Web アプリとネイティブ アプリの最高の機能を組み合わせたエクスペリエンスです。アプリをインストールせずにブラウザから直接アクセスできるので、初めての方には大変お得です。ユーザーがアプリケーションとのつながりを築くにつれて、アプリケーションはますます強力になります。読み込みが速く、弱いネットワーク環境でも関連メッセージをプッシュでき、ネイティブ アプリケーションのようにホーム画面に追加できるため、全画面のブラウジング エクスペリエンスが得られます。
PWA には次の機能があります:プログレッシブ機能強化 - 新しい機能をサポートするブラウザーはより良いエクスペリエンスを得ることができますが、それらをサポートしないブラウザーは元のエクスペリエンスを維持します。
オフライン アクセス - サービス ワーカーは、オフラインまたはネットワーク速度が遅い環境でも作業できます。
ネイティブのようなアプリケーション - App Shell モデルを使用して、ネイティブ アプリケーションのようなエクスペリエンスを実現します。
インストール可能 - ユーザーは、アプリ ストアを経由せずに、便利なアプリをホーム画面に保存できます。
共有が簡単 - アプリは URL 経由で簡単に共有できます。
継続的な更新 - Service Worker の更新プロセスのおかげで、アプリケーションは常に最新の状態を維持できます。
セキュリティ - HTTPS 経由でサービスを提供し、ネットワーク スヌーピングを防止し、コンテンツが改ざんされないようにします。
検索可能 - W3C マニフェストのメタデータと Service Worker の登録により、Web アプリケーションを検索エンジンで見つけられるようにします。
再訪問 - メッセージプッシュなどの機能を通じて、ユーザーが簡単に再訪問できるようにします。
Web アプリ マニフェストにより Web がネイティブに近くなります
Web アプリ マニフェストは、Web アプリケーションの関連構成を JSON 形式で定義します (アプリケーション名、アイコンまたは画像の接続、起動 URL、カスタム機能、起動時のデフォルト構成、全画面設定) 、など)。
Service Worker は Web 機能を強化しますService Works を介したリソースのオフライン キャッシュと更新
App Shell により表示効率が向上します
App Shell (アプリケーション シェル) は、アプリケーションのユーザー インターフェイスに必要な最も基本的な HTML、CSS、JavaScript であり、最初の読み込み直後にキャッシュされるため、キャッシュする必要はありません。使用するたびにダウンロードしますが、UI がローカライズされた状態を維持できるように、必要なデータのみを非同期的にロードします。
ミニプログラムフレームワークに基づいて開発されたtodosアプリ
以下は、このアプリ開発の重要なポイントを紹介します:
1. このアプリのディレクトリ構造と構成は、ここでは紹介されません。詳細については、ドキュメントに記載されています - フレーム部品が非常に詳細に説明されています。このプラットフォームには html と css はなく、wxml と wxss に置き換えられます。 wxss と css にはほとんど違いがありません。欠点は、css ほど強力ではなく、サポートされるセレクターが限られていることです。ただし、プラットフォームが WeChat 1 つだけであるため、互換性の問題がほとんどなく、標準および最新の CSS テクノロジーを使用できるという利点があります。 wxml では、プラットフォームによって提供されるコンポーネントのタグのみを使用できます。HTML タグを直接使用することはできません。wxml での各コンポーネントの使用方法の例は、「ドキュメント - コンポーネント」セクションにあります。したがって、実際には、wxml と wxss を記述することに問題はありません。
2. wxml は以下の機能をサポートします:
テンプレートと参照を除き、その他の機能はすべて todo アプリで使用されます。ただし、各機能の詳細は、必要に応じて適切なもののみが選択されます。アプリの機能。数日前、WeChat アプレットは vue フレームワークに基づいて実装される可能性があるという記事を見たので、vue のドキュメントを調べてみました。データ バインディング、条件付きレンダリング、リスト レンダリング、およびイベントについて、vue の使用法を詳しく説明しました。それに比べて、wxml が提供する機能は vue の関連機能によく似ていますが、機能がそれほど多くないため、vue フレームワークの機能を小さなプログラムに直接使用するのは簡単ではありません。ベストプラクティスはやはり公式ドキュメントに記載されている手順に基づいており、公式ドキュメントに記載されていない機能がある場合、推測で使用すると確実に動作しません。いくつかのオブジェクトのプロトタイプを印刷して確認しましたが、公式ドキュメントよりも多くのインスタンス メソッドは見つかりませんでした。これは、ミニ プログラムのフレームワーク機能が実際に制限されていることを示しています。
3. セレクターがフレームワークの要件を満たしている限り、Wxss は実際にはless または sass で書くことができます。時間の都合上、このアプリでは試しませんでした。
4. 双方向の拘束はありません。 Vue では、Vue インスタンスはビューモデルです。ビューレイヤー内のデータの更新はリアルタイムでモデルにフィードバックされ、モデルの更新もリアルタイムでビューにフィードバックされます。ミニ プログラムでは双方向のバインディングはなく、ビューの更新はモデルに直接同期されません。関連するイベント コールバックでビュー レイヤーからデータを直接取得して、モデルを更新する必要があります。ミニ プログラムは、ミニ プログラム内で setData を使用して、ページを再レンダリングします。たとえば、単一の Todo 項目の場合、トグル操作は次のようになります。
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(); },
上記のコードでは、単一の Todo 項目のチェックボックスの値は e.detail.value[0] を通じて取得され、その完全なステータスはtodo はこの値によって判断されます。最後に、updateData 内で、setData メソッドを通じてモデルのコンテンツが更新されます。この方法でのみ、切り替え操作後にアプリの下部にある統計が更新されます。
5. イベントをバインドする場合、パラメーターを渡すことはできません。渡せるのは 1 つのイベントのみです。たとえば、上記のトグル操作では、実際には現在の Todo の ID をコールバックに渡したいと思っていましたが、あらゆる方法でそれを行うことはできず、最終的には ID メソッド、つまりバインドすることでしか処理できませんでした。 wxml で、イベントのコンポーネントに ID を追加します。この ID はページ全体で繰り返すことができないため、イベントがトリガーされるときに ID の最後に todo ID 値を追加する必要があります。 、e.currentTarget.id を通じて取得できます。コンポーネントの ID については、対応する ID プレフィックスを削除して、todo の ID 値を取得します。これは現在使用されている方法であり、あまりエレガントではないと思います。後で実装するより良い方法を見つけたいと思います。
6. アプリではローディング効果が考慮されており、ボタンコンポーネントのloading属性を使用して実現する必要があります。ただし、読み込みは単なるスタイル コントロールであり、ボタンを繰り返しクリックできるかどうかは制御しません。したがって、ボタンの disabled 属性も使用して、繰り返しクリックされないようにする必要があります。
残りの実装の詳細は、次の 2 つのファイルのソース コードにあります。問題点を指摘してください。
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>
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,在代码层面,给我们这些开发者提供一个最佳实践规范。欢迎有其它的开发思路的朋友,帮我指出我以上实现中的问题。