最近、小紅書で多くの人が自分の微信小程序が広告で月に数万円を稼いでいることを紹介しているのを見かけた。
その話は決して嘘ではないが、実際にはそれほど簡単なことではない。広告収入の多さはユーザー数に大きく依存するため、単に小程序を作ったからといって大量のユーザーを惹きつけることは難しい。
しかし、全く不可能というわけでもない。人は夢を持つことが必要だからだ。また、簡単な小程序の開発コストは高くなく、特に雲開発によって支えられている。後からドメインやサーバーなどの追加費用はかからない。すべてのコンテンツは微信プラットフォームに配置できる。もし小程序が人気にならなかった場合、毎月約20元の基本的な費用しかかからない。これは唯一の支出である。したがって、広告収入が20元を超えるなら、それは利益になる。
ちょうど今、暇な時間があったので、私は微信小程序の開発プロセスが以前とどう変わったのかを調べてみることにし、その過程を記録して今後の参考にしようと考えた。それがこの記事のきっかけとなった。
開発前の準備
微信公众平台の登録
まず、微信公众平台にアクセスしてQRコードをスキャンします。新規ユーザーの場合、スキャン時にウェブ登録を促されます。
必要な資料はそれほど複雑ではありませんが、微信が異なる登録主体に対して設定している制限に注意が必要です。個人主体では小程序を登録できますが、支払いなどの資金取引機能は使用できません。これは、小紅書での宣伝が広告収入に焦点を当てている理由です。なぜなら、富貴な人物が真心話大冒险で勝負に負けたとしても、直接送金する以外は広告収入が個人主体の小程序の唯一の収益源となるからです。
登録リンク:https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN
リンク:https://mp.weixin.qq.com
微信小程序の作成
一つの微信公众平台アカウントは、単なる開発者アカウントであり、**AppID(小程序ID)**に対応しています。
微信公式は微信開発者ツールを提供しており、小程序の開発に使用されます。
したがって、小程序の開発プロセスは次のようになります:
- アカウントの登録
- AppIDの取得
- 微信開発者ツールのダウンロードとインストール
- 微信開発者ツールでプロジェクトを作成し、AppIDをバインドして開発を開始
微信開発者ツールのダウンロード先:https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html
オプション:小程序の備案
すでに小程序の内容(名前/ロゴ)が確定している場合は、事前に小程序の備案を行うことができます。
詳細については:小程序備案
開発を始める
小程序の開発方法はVueに似ていますが、全体的に微信によって「魔改」されています。WXMLを使用してページ構造を構築し、WXSSを使用してスタイルを記述し、WXJSを使用してページをレンダリングします。
注意すべき点は、WXSSはCSSに似ていますが、多くのCSS3の特徴がWXSSに継承されていないことです。また、小程序のレンダリング方式は従来のウェブとは異なります。そのため、開発時には異なるデバイスにおけるページスタイルの互換性と表示効果に特に注意する必要があります。
全体説明
小程序を作成すると、次のような基本ディレクトリが得られます。
your-app/
│
├── assets/ # 静的リソースファイル(画像、フォントなど)を格納
│ ├── logo.png
│ └── bg.jpg
│
├── components/ # 自作コンポーネント
│ ├── header/ # ヘッダーコンポーネントのフォルダ
│ │ ├── header.wxml # コンポーネントの構造ファイル
│ │ ├── header.wxss # コンポーネントのスタイルファイル
│ │ ├── header.js # コンポーネントのロジックファイル
│ │ └── header.json # コンポーネントの設定ファイル
│ │
│ └── footer/ # フッターのコンポーネント
│ ├── footer.wxml
│ ├── footer.wxss
│ ├── footer.js
│ └── footer.json
│
├── pages/ # 小プログラムページを格納
│ ├── index/ # トップページ
│ │ ├── index.wxml # ページ構造ファイル(HTML-like)
│ │ ├── index.wxss # ページスタイルファイル(CSS-like)
│ │ ├── index.js # ページロジックファイル(JS)
│ │ └── index.json # ページ設定ファイル
│ │
│ └── other/ # 他のページ
│ ├── other.wxml
│ ├── other.wxss
│ ├── other.js
│ └── other.json
│
├── utils/ # ツール関数などの共有コードを格納
│ ├── util.js
│ └── helper.js
│
├── app.js # 小プログラムのエントリーファイル
├── app.json # 小プログラムのグローバル設定ファイル
├── app.wxss # 小プログラムのグローバルスタイルファイル
└── project.config.json# 小プログラムプロジェクト設定ファイル(IDE用)
ページの説明
各ページまたはコンポーネントは、同名のwxml、wxss、js、jsonの4つのファイルで構成され、これらのファイルは自動的に関連付けられ、追加の参照設定は必要ありません。
wxml (WeChat Markup Language、HTMLに似た)
- ページの構造とレイアウトを担当し、ページ内の要素を定義します。
- WXMLを使ってページのビジュアル構造を記述します。
<!-- wxml ファイル -->
<view class="container">
<text>{{title}}</text>
</view>
wxss(WeChat Stylesheet、CSSに似た)
- ページのスタイル設定を担当し、ページ内要素の外観、レイアウトなどを定義します。
- wxssはCSSの拡張版で、大部分の標準的なCSS特性をサポートしています。さらに、サイズ単位rpx(レスポンシブピクセル)などの独自の機能も含まれています。
/* wxss ファイル */
.container {
width: 100%;
padding: 20px;
background-color: #f0f0f0;
}
js(JavaScript)
- ページのロジック処理に使用され、データの処理、イベントのバインディング、ページライフサイクルの管理などが含まれます。
Page() またはComponent() を使ってページの振る舞いやライフサイクルメソッド(例えばonLoad、onShow など)を定義します。
// js ファイル
Page({
data: {
title: 'こんにちは、小程序!'
},
onLoad: function() {
console.log('ページロード');
}
});
json(設定ファイル)
- ページの設定を担当し、ナビゲーションバー、タイトル、ウィンドウ背景色などの設定を行います。
- ページおよびコンポーネントの設定もjsonファイルで指定され、ページが共有されるかどうか、WeChatの下スクロールリフレッシュが有効かどうかなどの設定が含まれます。
{
"navigationBarTitleText": "ホーム",
"enablePullDownRefresh": true
}
コンポーネント
コンポーネントはページと同様で、同名のwxml、wxss、js、jsonの4つのファイルで構成されます。
簡単に言うと、コンポーネントは「再利用可能な小さなページ」または「機能モジュール」です。
コンポーネントの意義は、再利用可能なUI構造とロジックをカプセル化することにあります。例えば、小程序では各ページに同じヘッダーがあることが多いですが、コンポーネントを使わなければ各ページで一度ずつ記述しなければなりません。コンポーネントを使うことで、ヘッダーのコンポーネントを1つだけ作成し、すべてのページで参照すれば、冗長なコードを減らすことができ、開発効率を高め、必要に応じてコンポーネントのコードを変更することで、すべてのページを一括更新でき、見落としや重複修正を防ぐことができます。
コンポーネントの参照
対応するページのjson設定ファイル(例:index.json)でusingComponentsを使用してコンポーネントを参照します。例えば、コンポーネントがcomponents/my-component/my-componentディレクトリにある場合:
{
"usingComponents": {
"my-component": "/components/my-component/my-component"
}
}
ページのwxmlファイルでHTMLタグのようにコンポーネントを使用します:
<my-component></my-component>
ライフサイクル
小程序はjs中のPage()またはComponent()によってページまたはコンポーネントを区別し、それぞれ異なるライフサイクルを持っています。
小程序の開発は主にページのライフサイクルを中心に進行します。
ページ
ページ内のカスタムメソッドは以下の表に示されています:
ページが対応するノードに到達したときにライフサイクル関数がトリガーされます。宣言しない場合はトリガーされません。
| ライフサイクル関数 | トリガータイミング | 適用場面 |
|---|---|---|
| onLoad(options) | ページロード時(一度のみ) | ページ遷移パラメータの取得、初期データの設定 |
| onReady() | ページ初回レンダリング完了時(一度のみ) | DOMノード情報の取得 |
| onShow() | ページ毎回表示時 | ページが前面に表示されたとき、データのリフレッシュに適している |
| onHide() | ページ非表示時 | 他のページに遷移したとき |
| onUnload() | ページアンロード時 | リソースの解放、タイマーのクリアに適している |
| onPullDownRefresh() | ユーザーが下スクロールリフレッシュ時 | ページデータのリフレッシュ(jsonで有効にする必要あり) |
| onReachBottom() | ユーザーが画面の底にスクロール時 | ページングデータのロードに適している |
| onShareAppMessage() | ユーザーがシェアボタンをクリック時 | カスタムシェア内容の設定 |
| onShareTimeline() | ユーザーがタイムラインにシェア時 | タイムラインシェアに適している |
| onPageScroll(event) | ページスクロール時 | スクロール位置の監視 |
| onResize(event) | ページサイズ変更時 | 画面回転などの際に適している |
注意:小程序のページ管理方法はブラウザの単ページアプリ(SPA)と似ており、スタック管理機構を使用しています。つまり:
- ページは即座に破棄されず、ページスタックに保存されます。
- 新しいページがスタックされ、古いページはメモリに残り、onLoadがトリガーされません。
- 古いページに戻るときは、古いページは再読み込みされませんが、onShowがトリガーされます。
| 操作 | onLoad | onShow | onHide | onUnload |
|---|---|---|---|---|
| ページAにアクセス | ✅ トリガー | ✅ トリガー | ❌ | ❌ |
| AからBにジャンプ | ❌ | ❌ | ✅ トリガー | ❌ |
| B→Aに戻る | ❌ | ✅ トリガー | ❌ | ❌ |
| A→B→Bを閉じる | ❌ | ✅ トリガー | ❌ | ✅(Bのアンロード) |
例
Page({
/**
* ページの初期データ
*
* ページの初期データはオブジェクトで、さまざまな属性を含むことができる。これらの属性はページレンダリングに使用される。
* ページのデータはページロード時に自動的にページのwxmlに渡される。
* dataで設定されたデータは、wxmlでバインディングを行い、ページでレンダリングされる。
* 例えば、次のデータはwxmlで{{message}}を使って表示できる。
* dataのデータはページのjsでthis.dataでアクセスできる。
* this.setDataメソッドでdataのデータを変更すると、ページが再レンダリングされる。
*/
data: {
message: "こんにちは、小程序!",
count: 0
},
/**
* 生命サイクル関数 - ページロードを監視
* ページが初めてロードされるときに一度だけトリガーされる
* ページパラメータの取得、初期データの設定などに使用可能
*/
onLoad(options) {
console.log("ページロード:onLoad", options);
// オプションからページ遷移時のパラメータを取得可能
if (options.id) {
console.log("ページパラメータ ID:", options.id);
}
},
/**
* 生命サイクル関数 - ページの最初のレンダリングが完了したことを監視
* ページ構造がレンダリングされているが、必ずしも表示されているわけではない
* DOMの操作に適している
*/
onReady() {
console.log("ページレンダリング完了:onReady");
},
/**
* 生命サイクル関数 - ページ表示を監視
* どのページにも戻るたびにトリガーされる(戻るページも含む)
*/
onShow() {
console.log("ページ表示:onShow");
},
/**
* 生命サイクル関数 - ページ隠蔽を監視
* ページがバックグラウンドに入るか、他のページに移動するときにトリガーされる
*/
onHide() {
console.log("ページ隠蔽:onHide");
},
/**
* 生命サイクル関数 - ページアンロードを監視
* ページが閉じたり削除されたりしたときにトリガーされる(例:wx.navigateBack()で戻る)
* 清掃操作、タイマーの解放などに適している
*/
onUnload() {
console.log("ページアンロード:onUnload");
},
/**
* ユーザーが下スクロールアクションを監視(ページリフレッシュ用)
* page.jsonで"enablePullDownRefresh": trueを有効にする必要がある
*/
onPullDownRefresh() {
console.log("ユーザーが下スクロールリフレッシュをトリガー:onPullDownRefresh");
// データのリフレッシュをシミュレート
setTimeout(() => {
this.setData({ message: "データが更新されました" });
wx.stopPullDownRefresh(); // リフレッシュアニメーションを停止
}, 1000);
},
/**
* ページ上スクロール終了イベント(データのロードに適している)
* ページングデータロードに適している
*/
onReachBottom() {
console.log("ページスクロール終了:onReachBottom");
this.setData({ count: this.data.count + 1 });
},
/**
* ユーザーが右上角のシェアをクリックしたことを監視(カスタムシェア内容を設定可能)
* 通常のシェアにはonShareAppMessageが適している
*/
onShareAppMessage() {
console.log("ユーザーがシェアをクリック:onShareAppMessage");
return {
title: "これはシェアのタイトル",
path: "/pages/index/index?id=123" // パラメータを含めることができる
};
},
/**
* ユーザーが右上角のタイムラインにシェアしたことを監視
* タイムラインへのシェアにはonShareTimelineが適している
*/
onShareTimeline() {
console.log("ユーザーがタイムラインにシェア:onShareTimeline");
return {
title: "タイムラインにシェアするタイトル"
};
},
/**
* ページスクロールを監視(スクロール位置の監視に適している)
*/
onPageScroll(event) {
console.log("ページスクロール:onPageScroll", event.scrollTop);
},
/**
* ページサイズ変化時にトリガー(通常、画面回転の適応に適している)
*/
onResize(event) {
console.log("ページサイズ変化:onResize", event);
}
});
コンポーネント
コンポーネントの振る舞いはページと似ています。一般的にはコンポーネントは自動的にアンロードされず、ページと一緒にキャッシュされます。
コンポーネントのライフサイクルはページとは異なり、具体的なライフサイクルは以下の通りです:
コンポーネントが対応するノードに到達したときにライフサイクル関数がトリガーされます。宣言しない場合はトリガーされません。
| ライフサイクル | 役割 |
|---|---|
| created() | コンポーネントのインスタンス化時にトリガーされ、データはバインディングされておらず、DOM構造も生成されていません。 |
| attached() | コンポーネントがページノードツリーにマウントされたときにトリガーされ、propertiesを取得できます。 |
| ready() | コンポーネントのビューがレンダリング完了し、DOMの操作に適しています。 |
| moved() | コンポーネントが別の場所に移動したとき(あまり使われません)。 |
| detached() | コンポーネントが削除されたときにトリガーされ、リソースのクリーンアップ(タイマー、イベントリスナーなど)に適しています。 |
| error() | コンポーネント内でエラーが発生したときにトリガーされます。 |
| pageLifetimes.show() | コンポーネントが属するページのonShow時にトリガーされます。 |
| pageLifetimes.hide() | コンポーネントが属するページのonHide時にトリガーされます。 |
| pageLifetimes.resize(size) | コンポーネントが属するページのサイズが変化したときにトリガーされます。 |
注意:小程序のコンポーネントのライフサイクルはページよりも細かく、コンポーネントの使用方法に応じていくつかの点に注意する必要があります:
- コンポーネントはページのonShow()で自動的にトリガーされません。データの更新を手動で行う必要があります。
- wx:ifを使用するとコンポーネントが再作成され、データをリフレッシュできます。
- hiddenを使用するとコンポーネントは非表示になりますが、コンポーネントは破壊されません。
| 動作 | ページonShow()がトリガー? | コンポーネントattached()がトリガー? | コンポーネントdetached()がトリガー? |
|---|---|---|---|
| wx.navigateTo() | ✅ | ❌ | ❌ |
| wx.navigateBack() | ✅ | ❌ | ❌ |
| wx.switchTab() | ✅ | ❌ | ❌ |
| wx:ifでコンポーネントを制御 | - | ✅(コンポーネントが再作成) | ✅(コンポーネントが破壊) |
| hiddenでコンポーネントを制御 | - | ❌ | ❌ |
例
Component({
/**
* コンポーネントのプロパティリスト(外部から渡される)
*/
properties: {
title: {
type: String,
value: "デフォルトタイトル"
},
count: {
type: Number,
value: 0
}
},
/**
* コンポーネントの内部データ
*/
data: {
innerValue: "内部データ"
},
/**
* コンポーネントのメソッドリスト
*/
methods: {
/**
* カスタムメソッド - カウントを増やす
*/
increment() {
this.setData({
count: this.data.count + 1
});
this.triggerEvent("countChanged", { count: this.data.count }); // 親コンポーネントにイベントを通知
},
/**
* カスタムメソッド - コンポーネントがクリックされた
*/
handleTap() {
console.log("コンポーネントがクリックされました");
this.triggerEvent("componentTap", { message: "コンポーネントがクリックされました" });
}
},
/**
* コンポーネントのライフサイクル関数
*/
lifetimes: {
/**
* created:コンポーネントのインスタンス化時にトリガー(コンポーネントがページノードツリーに追加される前)
*/
created() {
console.log("コンポーネントのインスタンス化:created");
},
/**
* attached:コンポーネントがページにマウントされたときにトリガー(ページのonLoadに類似)
*/
attached() {
console.log("コンポーネントがページにマウントされました:attached");
},
/**
* ready:コンポーネントのビューがレンダリング完了したときにトリガー(ページのonReadyに類似)
*/
ready() {
console.log("コンポーネントのビューがレンダリング完了しました:ready");
},
/**
* moved:コンポーネントが他のノードに移動したときにトリガー(ほとんど使われません)
*/
moved() {
console.log("コンポーネントが移動しました:moved");
},
/**
* detached:コンポーネントがページから削除されたときにトリガー(ページのonUnloadに類似)
*/
detached() {
console.log("コンポーネントが破壊されました:detached");
},
/**
* error:コンポーネント内でエラーが発生したときにトリガー(例:setData失敗)
*/
error(err) {
console.error("コンポーネントでエラーが発生しました:", err);
}
},
/**
* 旧版ライフサイクル(lifetimesと同等の機能を持ち、旧バージョンの小程序に互換性があります)
*/
pageLifetimes: {
/**
* コンポーネントが属するページが表示されたときにトリガー(onShowに類似)
*/
show() {
console.log("コンポーネントが属するページが表示されました:pageLifetimes.show");
},
/**
* コンポーネントが属するページが非表示になったときにトリガー(onHideに類似)
*/
hide() {
console.log("コンポーネントが属するページが非表示になりました:pageLifetimes.hide");
},
/**
* コンポーネントが属するページがアンロードされたときにトリガー(onUnloadに類似)
*/
resize(size) {
console.log("コンポーネントが属するページのサイズ変化:pageLifetimes.resize", size);
}
}
});
コンポーネントの書き方とページはほぼ同じです。通常、繰り返しページで出現する部分は個別にコンポーネントとして書くことをお勧めします。これにより、異なるページで参照するだけで済み、ページとコンポーネント間でデータをやり取りできます。
イベントバインディング
微信小程序では、イベントバインディングはページとユーザーとの相互作用の中心です。イベントはページ要素にバインディングされ、ユーザーが要素とインタラクションしたときに、対応するJavaScriptメソッドがトリガーされ、予期された操作を実行します。
小程序は多様なイベントタイプを提供しており、主に以下のようなものがあります:
| イベントタイプ | 説明 |
|---|---|
| tap | 軽触イベント(クリック) |
| longpress / longtap | 長押し(750ms以上) |
| touchstart | 指がスクリーンに触れると |
| touchmove | 指がスクリーン上でスライド |
| touchend | 指がスクリーンから離れると |
| touchcancel | タッチが中断される、例えば着信 |
| scroll | スクロールイベント |
| input | 入力フィールドの内容変化 |
| blur | 入力フィールドがフォーカスを失った |
| focus | 入力フィールドがフォーカスを得た |
| change | 選択肢が変更された(picker、checkbox、radioなど) |
| confirm | 入力フィールドのエンターキーイベント |
| submit | フォームのサブミットイベント |
| load | 画像のロード完了 |
| error | コンポーネントのロード失敗 |
イベントのバインディング方法
微信小程序には多种のイベントバインディング方法があり、以下のようなものです:
wxmlにおいて、イベントバインディングは「バインディング方式+イベントタイプ="js中の関数名"」です。
| 方式 | 説明 |
|---|---|
| bind | イベントがバブル |
| catch | イベントがバブルしない |
| capture-bind | イベントがキャプチャ段階でトリガーされる(親から子) |
| capture-catch | イベントがキャプチャ段階でトリガーされ、バブルしない |
イベントのバブル
微信小程序のイベントはバブルイベントと非バブルイベントに分かれます。
これらのイベントは子コンポーネントから親コンポーネントに伝えられます:
- tap(クリック)
- longpress(長押し)
- touchstart / touchmove / touchend(タッチ)
- confirm(キーボードのエンターキー)
バブルイベントは、wxmlにおいて、一つのビューコンテナがもう一つのビューコンテナにネストされている場合、内部のビューコンテナをクリックしたときに、外部のビューコンテナの対応するイベントも応答されるということを意味します。これは、内部コンポーネント自体が外部コンポーネントの内部の一部であるためです。
以下の例を参照してください:
🔹 bindtap="childTap" → イベントがバブルし、親コンポーネントの parentTap() もトリガーされる 🔹 catchtap="childTap" → イベントがバブルせず、parentTap() はトリガーされない
<view bindtap="parentTap">
<view bindtap="childTap" catchtap="childTap">クリックしてください</view>
</view>
Page({
parentTap(event) {
console.log("親コンポーネントがクリックされました");
},
childTap(event) {
console.log("子コンポーネントがクリックされました");
}
});
イベントオブジェクト
すべてのイベントはeventパラメータを伴います:
handleTap(event) {
console.log(event);
// event.currentTarget // イベントがバインディングされた要素
// event.target // 実際のイベントをトリガーした要素
// event.detail // イベントの詳細情報(入力の値など)
// event.touches // 現在のタッチポイント
// event.timeStamp // イベント時間
}
例:
<view bindtap="handleTap" data-id="123">クリックしてください</view>
handleTap(event) {
console.log(event.currentTarget.dataset.id); // "123"が出力されます
}
イベントをバインディングした要素はdata-を使用して値を設定することができます。イベントメソッドではevent.currentTarget.datasetを使用して値を取得できます。
つまり、WXMLで記述されているのが
<view bindtap="handleTap" data-demo="123">クリックしてください</view>の場合、handleTapメソッドではevent.currentTarget.dataset.demoを使用して値を取得できます。
コンポーネントのカスタムイベント
コンポーネントもカスタムイベントをサポートしており、そのイベントタイプとバインディング方法はページと同様です。違いは、コンポーネントのカスタムイベントはmethodsオブジェクト内で対応するメソッドを定義する必要があります。
コンポーネントのカスタムイベントでは、this.triggerEventメソッドを使用してページにデータを送信します。以下のようにします:
- 子コンポーネント:
<button bindtap="sendData">クリックして送信</button>
Component({
methods: {
sendData() {
this.triggerEvent('myEvent', { name: '小程序' });
}
}
});
- 親コンポーネント:
<custom-component bind:myEvent="handleEvent"></custom-component>
Page({
handleEvent(event) {
console.log(event.detail.name); // "小程序"
}
});
雲開発
正直なところ、雲開発の良さを語るのは難しいです。本格的な企業プロジェクトであれば、自前でサーバーを用意するのがおすすめです。これは、自前でサーバーを構築することで、より大きな後端操作空間を得ることができ、多くのロジックを後端で処理することができ、柔軟性が高いからです。
雲開発のコアには雲関数、雲ストレージ、そして雲データベースがあります:
- 雲関数:リモートにNode.jsコードをデプロイでき、微信小程序が認証なしで直接呼び出すことができます(ネットワーク内呼び出しのように、追加のセキュリティ検証は必要ありません)。
- 雲データベース:MongoDBに類似したNoSQLデータベースまたはMySQLをサポートしています。
- 雲ストレージ:様々なファイルリソースを保存するために使用されます。
機能的には、雲開発は基本的なサーバーサイド能力を備えていますが、いくつか非常に奇妙なソフトウェア制限もあります。例えば、雲データベースを使用する際、1回のクエリで最大20件のデータしか取得できず、より多くのデータを取得するにはページングを介して徐々に取得する必要があります。
ただし、全体的に見れば、マイクロプロジェクトのために訪問数が少ない小程序のために一台のサーバーを用意するのは少し無駄です。雲開発は使用量に応じて課金されるモデルを採用しており、最低でも20元のプランで基本的な要件を満たすことができ、自前のサーバーと比べてコストが大幅に低くなります。したがって、雲開発を選ぶかどうかは、具体的なニーズによります。
注意: 雲開発の機能を利用する前に、wx.cloud.init() を使用して初期化する必要があります。一般的には、ルートディレクトリの app.js の onLaunch メソッドで一度実行する必要があります。
雲ストレージ
上传
メソッドで、wx.cloud.uploadFile メソッドを使用してファイルをアップロードできます。
- cloudPath:ファイルがクラウドに保存されるパス、任意の文字列にすることができます(推奨:フォルダ構造を加える)。
- filePath:ローカルファイルのパス、例えば
wx.chooseImage()で選んだ画像のパス。 - success:アップロード成功時のコールバック、res.fileID はファイルのユニークIDで、後続のアクセスに使用できます。
wx.cloud.uploadFile({
cloudPath: 'example-folder/my-image.png', // 保存パス
filePath: tempFilePath, // ローカルファイルのパス(wx.chooseImageで取得)
success: res => {
console.log('アップロード成功、ファイルID:', res.fileID);
},
fail: err => {
console.error('アップロード失敗', err);
}
});
アクセスまたはダウンロード
アップロード成功後に、fileID を使用してファイルの一時的なアクセスURLを取得できます。
wx.cloud.getTempFileURL({
fileList: ['cloud://your-env-id/example-folder/my-image.png'],
success: res => {
console.log('ファイルのアクセスURL:', res.fileList[0].tempFileURL);
},
fail: err => {
console.error('ファイルURLの取得失敗', err);
}
});
ファイルの削除
クラウドストレージ上のファイルを削除するには、wx.cloud.deleteFile メソッドを使用します。
wx.cloud.deleteFile({
fileList: ['cloud://your-env-id/example-folder/my-image.png'],
success: res => {
console.log('削除成功', res.fileList);
},
fail: err => {
console.error('削除失敗', err);
}
});
雲関数
雲関数のコアは「クラウド関数」そのものです。つまり、微信クラウドプラットフォームにデプロイされたNode.jsコードであり、この雲関数に名前を指定します。その後、wx.cloud.callFunction を使用してこの関数を呼び出せます。
大まかなフローは次の通りです:
wx.cloud.callFunction({
name: 'cloud-name', // 雲関数の名前
data: {}, // 雲関数に渡すデータ
success: res => {
console.log('雲関数の結果:', res);
},
fail: err => {
console.error('雲関数の呼び出し失敗', err);
}
});
ある賢い友人は、「小程序はすでに雲データベースと雲ストレージを直接使用できるのに、雲関数の意味は何ですか?」と疑問に思うかもしれません。
実は、雲関数の意味は明らかです。ユーザーがアップロードしたデータをクリーンアップ、計算、結合など複雑な操作を行う必要がある場合、またはクラウド側の高度なデータ処理が必要な場合、雲関数は非常に役立ちます。クライアント側でこれらの処理を行うと、小程序の負担が大きくなりますが、雲関数を用いることでこれらのロジックをクラウドにカプセル化し、クライアントの計算負荷を軽減し、パフォーマンスを向上させることができます。
また、企業主体であれば、第3者サービスとの統合が必要な場合があります。例えば、支払い処理、SMS送信、メールサービス、ソーシャルログインなど。これらの外部サービスは通常、後端で操作されるべきであり、小程序クライアントで直接実装することは不適切です。雲関数を通じて、安全に外部APIとやり取りし、結果を小程序に返すことができます。
最後に、定期的なタスク(例:定期的なメッセージ配信、データベースのクリーンアップ、レポートの生成など)が必要な場合、雲関数はクラウドで定期的に実行でき、クライアントの操作に依存しません。
雲データベース
まず、データベースインスタンスを取得します。これは通常、wx.cloud.database() を使用して取得します。
ページでデータベースを使用するときは宣言できます。または、小程序起動時に app.js でグローバル変数として登録できます。
データの取得
データを取得するには get() メソッドを使用します。これは Promise オブジェクトを返し、.then() または async/await を使用してデータを取得できます。
const db = wx.cloud.database()
db.collection('users').get({
success: res => {
console.log('取得結果', res.data); // 取得されたすべてのデータ
},
fail: err => {
console.error('取得失敗', err);
}
});
条件でデータを取得することも可能です。微信小程序は db.command を使用してこれらの操作演算子を処理します。サポートされているのは次の通りです:
- gt():より大きい
- lt():より小さい
- gte():より大きいまたは等しい
- lte():より小さいまたは等しい
- eq():等しい
- neq():等しくない
- in():ある配列内
- and():複数の条件を結合
users テーブルで city = '成都' and age > 25 のユーザーを取得したい場合:
db.collection('users').where({
age: db.command.gt(25),
city: "成都"
}).get({
success: res => {
console.log('取得結果', res.data);
},
fail: err => {
console.error('取得失敗', err);
}
});
データの追加
雲データベースにデータを挿入するには add() メソッドを使用します。データをオブジェクト形式で渡し、雲データベースは _id, _openid を自動的に生成します。
const db = wx.cloud.database()
db.collection('users').add({
data: {
name: '张三',
age: 25,
city: '成都',
},
success: res => {
console.log('データ挿入成功', res);
},
fail: err => {
console.error('データ挿入失敗', err);
}
});
データの更新
データを更新するには update() メソッドを使用します。_id を指定して特定のドキュメントを更新し、特定のフィールドの値を変更します。
const db = wx.cloud.database()
db.collection('users').doc('ドキュメントID').update({
data: {
age: 26,
},
success: res => {
console.log('データ更新成功', res);
},
fail: err => {
console.error('データ更新失敗', err);
}
});
データの削除
データを削除するには remove() メソッドを使用します。指定された _id のデータを削除します。
const db = wx.cloud.database()
db.collection('users').doc('ドキュメントID').remove({
success: res => {
console.log('データ削除成功', res);
},
fail: err => {
console.error('データ削除失敗', err);
}
});
いくつかの落とし穴
雲データベースのより多くの操作は公式ドキュメントを参照してください。
雲データベースは使いやすいように見えますが、いくつか隠れた落とし穴があります。以下のようなものです:
- 微信雲データベースのクエリ操作はデフォルトで1回あたり最大20件のデータを返します。もっとデータを取得したい場合は、ページングクエリを実行する必要があります。
skip()とlimit()を使用してページング操作を行います。自分でページングロジックを制御し、一度に1ページのデータを取得し、データがなくなるまで繰り返します。 - 雲データベースがサポートするクエリ条件は比較的単純で、大部分の一般的な要件に適していますが、いくつかの制限があります。例えば、正規表現クエリはサポートされていません。すべてのクエリは明確なフィールド条件で行われなければなりません。ORクエリは直接実行できません。複数の条件クエリを使用するか、クエリ後に手動で結果をマージする必要があります。
- 雲データベースは本質的なバッチ書き込み操作(バッチ更新、バッチ削除など)をサポートしていません。大量のデータをバッチ処理する必要がある場合、各レコードをループ処理する必要があります。性能コストが発生します。
- 雲データベースは強一貫性モデルを採用しており、すべての書き込み操作(
add、updateなど)はデータがグローバルに同期されることを保証します。複数のユーザーが同時にデータを編集する場合、データ衝突が発生する可能性があります。雲データベース自体は衝突解決メカニズムを提供していません。そのため、ビジネスロジックを自分で処理する必要があります。
開発が完了したら
備案と認証
微信小程序を正式にリリースする前に、備案と認証という重要なステップが必要です。
小程序備案
備案プロセスは3ステップで構成されています。
- 小程序の基本情報を補完する:名称、アイコン、説明など。
- 主要業種を設定する:小程序が所属する業務カテゴリを選択し、wechatの運用規則に適合していることを確認します。
- 審査を提出する:まず Tencent が審査を行い、審査が通過した後、管理局に最終的な備案を提出します。
備案が通過した後、小程序は正式なバージョンをリリースする資格を持ちます。それがないと、正常な運営ができません。内容は簡単で、個人主体は身分証明書、個人情報、メールアドレスなどがあれば十分です。
当日または翌日、Tencentから電話が来て、問題はありません。主に個人情報が正確かどうかを確認し、あなたの名前、身分証明書の最後の6桁、審査する小程序の名前、その機能などを尋ねます。
途中で突然「干支」を尋ねられたことがあります。おそらく第三者の備案を防ぐためです。
備案は主に管理局側で遅延し、通常3〜5日から1週間かかります。コンテンツは審査されず、開発前に備案を行うことも可能で、時間を節約できます。
小程序認証
認証は小程序主体の合法性和運営の合規性を確保するためです。WeChatは第三者的な審査機関に委託して以下の内容を審査します:
- 主体の真実性:個人または企業の身分情報の検証。
- 業界資質の有効性:小程序が行う事業が関連業界の要件に合致しているかの確認。
- 命名の合規性:小程序名がWeChatの命名ルールに合致しているか。
- オンラインサービスの可用性:小程序の機能が正常に動作しているかのテスト。
個人認証の費用は30元、企業認証の費用は年間300元です。認証を通過した後、小程序は共有や検索が可能になります。それ以外の場合は、スキャンまたは直接アクセスでしか使用できません。
最後
微信開発者ツールで、開発者は直接アップロードコードを行います。アップロード後、コードはバージョン管理 → 開発管理ページに表示されます。誤りがないことを確認した後、審査を提出できます。
提出する前に、十分なテストを実施することが重要です。なぜなら、一旦審査が通過すれば、コードは正式にリリースされます(備案/認証が完了していることを前提に)。未完成のテストを提出した場合、ユーザーエクスペリエンスに影響を与える可能性があり、オンラインバグが発生する可能性があります。
審査は通常速く、1営業日以内で完了します。ただし、小程序リリース後90日以内に備案が完了していない場合、使用できなくなります。そのため、備案が完了した後に正式リリースすることを推奨します。そうすることで、備案の問題が小程序の正常な運営に影響を与えないようにします。
ここまでで、登録から開発、テスト、備案と認証まで、すべてのプロセスが終わりました。初めて小程序を開発する場合は、プロセスが煩雑に感じられるかもしれませんが、WeChatが提供する雲開発や開発者ツールにより、開発のハードルは大幅に低下しています。もちろん、技術は基礎であり、小程序が成功するかどうかは製品の価値とユーザー体験にかかっています。もし小程序開発を試してみたいだけであれば、雲開発は良い選択肢であり、サーバーのコストやバックエンド開発の手間を省けます。しかし、より深く発展させたい場合は、自身の要件に合わせて自前のバックエンドを構築するか、より柔軟な技術方案を検討することをおすすめします。