ミニ プログラム フレームワークに基づいて開発された Todos アプリ、WeChat ミニ プログラムの使い方をすぐに理解します
WeChat 公式は、WeChat ミニ プログラムの公式ドキュメントと開発者ツールをオープンしました。過去 2 日間、私は小さなプログラムの開発方法を理解するために関連ニュースを読んでいました。公式ドキュメントが公開された後、すぐにそれらに目を通し、ドキュメントの 2 つの部分 (フレームワークとフレームワーク) を理解することに集中しました。コンポーネントを作成し、通常の ToDo アプリを作成するための簡単なチュートリアルに基づいています。このアプリは WeChat アプレット プラットフォームに基づいており、todo アプリの通常の機能を実装すると同時に、実際の作業シナリオに近づけるために、読み込みコンポーネントとトースト コンポーネントを使用して対話とフィードバックを完了します。いくつかの操作の。このプラットフォームについての私の直観的な印象は、技術レベルでは Vue に似ていますが、Vue よりもはるかに強力ではなく、開発アイデアは Vue とは異なり、バックボーンに似ているということです。したがって、backbone や vue などの mvc、mvvm フレームワークを使用したことがある人は、このプラットフォームを簡単に始めることができます。この記事では、このtodoアプリの実装のポイントを中心に紹介します。
まず、この記事に関連する情報を追加します:
公式ドキュメント: https://mp.weixin.qq.com/debug/wxadoc/dev/index.html
公式開発者ツールのダウンロード: https://mp. weixin.qq.com/debug/wxadoc/dev/devtools/download.html
この記事の todo アプリの機能デモ:
注: todo テキストを直接編集するには、todo テキストを長押しする必要があります。携帯電話なのでダブルクリックイベントが編集に使えず、長押しイベントに変更しました。ミニ プログラム プラットフォームは、ダブルクリック イベントのバインドも提供しません。
関連ソースコード: https://github.com/liuyunzhuge/blog/tree/master/todos/wx
このプロジェクトをローカルで実行したい場合は、最初に開発者ツールをインストールする必要があり、ドキュメント内の簡単なチュートリアルでは、まずプロジェクトをビルドします。
構築が完了したら、開発者ツールがプロジェクトを開きます。
次に、ディスク上でビルドされたプロジェクトのフォルダーを見つけて、その中のすべてのコンテンツを削除し、すべてを貼り付けます。
次に、開発者ツールを再度開き、最初に編集タブに入り、次にコンパイルボタンをクリックすると、アプリの機能を表示するために直接デバッグインターフェイスに入ります:
これの開発を紹介しましょう。 app の要点:
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: '' }); } });
最後に、このアプリは限られた時間内に WeChat の公式文書に従って開発されたことを付け加えておく必要があります。したがって、ここでの実装方法が合理的であるかどうかはわかりません。このアプリは、ミニ プログラム プラットフォームの使用法を理解するためにのみ使用します。 WeChat関係者がより包括的な、できればプロジェクトベースのデモを立ち上げて、私たち開発者にコードレベルでのベストプラクティス仕様を提供できることを願っています。他の開発アイデアを持つ友人は、上記の実装の問題点を指摘するのを手伝ってくれるでしょう。
この記事を通じて、WeChatミニプログラムの枠組みを皆さんに理解していただければ幸いです。このサイトを応援していただきありがとうございます。
WeChat ミニ プログラム フレームワークの詳細とアプリケーション例に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。