高度なクロスプラットフォーム アプリケーション開発 (56): 異常なアプリケーション レンダリングの問題の分析と解決

I.はじめに

前回のモバイル端末のメモリ不足によるiOSページの白画面問題に続き、(ブログ記事「高度なクロスプラットフォームアプリ開発 (50) uni-app ios web-view nested H5 project 白画面問題の分析と解決策」を参照)です。システムの実行中に、APP にiOS黒い画面、黒いブロック、白い画面が頻繁に表示されることがわかりました。

ここに画像の説明を挿入
ここに画像の説明を挿入

2. 問題分析

上記の問題はページ レンダリングの問題が原因で発生します。ページ スタックのオーバーフローやアプリケーションのメモリ リークが原因である可能性があります。

ページ スタック情報を出力した後getCurrentPages()、下部のナビゲーションが切り替えられると、ページ スタック情報がクリアされていることが判明したため、ページ スタック オーバーフローの問題はありません。

引き続き APP キャッシュ クリーニング メカニズムを試し、次のようにクリーニング戦略を策定します。

現在のタブ項目をクリックし、現在のページをスクロールまたは更新してネイティブ メソッドを呼び出し、plus.cache.clearアプリケーション キャッシュをクリアします。別のタブアイテムをクリックすると、間違いなくページ切り替えがトリガーされます。

// 点击 tab 时触发
onTabItemTap (e) {
    
    
	// #ifdef APP-PLUS
	plus.cache.clear( function () {
    
    
		console.log( "Clear application cache successful!" );
	});
	// #endif
},

クリーン キャッシュ ポリシーの実装後、問題は解決されました。

uni.setStorage(OBJECT)このことから、 (macrotask)、uni.setStorageSync(KEY,DATA)(microtask)などの API を呼び出すように派生しuni.downloadFileuni.saveFileデバイスのメモリに格納されますか?

3.拡張読書

3.1 ページジャンプ

  • uni.navigateTo(OBJECT)
    ページ スタックは保持され、navigateBack呼び出しによって返すことができます。ただし、ページスタックオーバーフローの問題があるため、一般的なジャンプ方法としては使えません。

  • uni.redirectTo(OBJECT)
    ページ スタックは保持されず、前のページは返されず、デフォルトのpages.json最初のページが返されます。

  • uni.reLaunch(OBJECT)
    ページ スタックを破棄し、指定されたページにジャンプします。通常、ログアウトしてホームページに戻るために使用されます。

  • uni.switchTab(OBJECT)
    タブの切り替えに使用します。

  • uni.navigateBack(OBJECT)
    協力しnavigateToて使用するページに戻ります。これにより、ページ スタックのオーバーフローを効果的に回避できます。getCurrentPages()現在のページ スタックを取得し、返す必要があるレイヤーの数を決定できます。

プロジェクト コードを解析した結果、ルーティング ジャンプでページ スタック オーバーフロー例外が発生しないことがわかりました。下部のナビゲーション バーが切り替えられると、ページ スタックがクリアされます。ページ スタックには、同じナビゲーション バーの下にあるページ ジャンプのみが含まれます。

2 トラブルシューティングのアイデア

  1. 特定のビジネス シナリオに従って、ページ スタックがオーバーフローしないように、ページ ジャンプとページ リターンを論理的に制御します。getCurrentPages()ページスタックの高さを判断し、webviewオブジェクトの数を確認することで、現在のジャンプ方法が適切かどうかを判断できます。
  2. コードのロジックを確認し、バックグラウンド リクエストが長時間返されないという問題がある場合は、この状況を回避するためにuni.request追加できます。timeout
  3. setInterval作成された ( ) がクリアされていない ( )タイマーがあるかどうか、メモリ オーバーフローをチェックしますclearInterval
  4. uniapp問題自体は、クロスプラットフォームをjs呼び出す必要があるためnative、パフォーマンスは確実に失われます. それが許容できない場合は、ネイティブ言語で開発する必要があります.
  5. キャッシュを削除する場合、データが消去された場合に使用する必要がありますuni.clearStorageSync

次に、実行中にアプリケーションが占有するメモリをチェックして、メモリ リークがあるかどうかを確認する必要があります。

3.2 $nextTick 原則の詳細な分析

Vue応答性を達成するには、データが変更された直後に変更するのではなくDOM、特定の戦略に従って更新する必要がありますDOM$nextTick次のDOM更新、データを変更した後に使用すると$nextTick、コールバックで更新されたものを取得できDOM、次のDOM更新。

簡単に理解すると、データが で更新およびdomレンダリングされると、関数が自動的に実行されます。Vue上記のデータはupdate のdata直後には更新されませんDOM。つまり、dataデータを変更してすぐにDOM上記の値を取得すると、取得されるのは古い値であり、その中にDOM上記の値を取得する操作を入れます。 $nextTick、更新されたデータを取得できます。

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

正しい使い方: vue が data 内のデータを変更した後、vue.$nextTick()メソッドを使用して js オブジェクトをラップし、後続のコードを実行します。

ここに画像の説明を挿入
ここに画像の説明を挿入

3.2.1 $nextTick() を使用する場合

Vueライフサイクルのcreated()フック関数で行うDOM操作はVue.nextTick()コールバック関数に配置する必要がある. 理由は,created()フック関数が実行されるとき, DOMは実際には何の描画も行わず, ここでDOM操作を行っても意味がないからである. time. 無駄とは違うので、ここでは DOM 操作の js コードをVue.nextTick()コールバック関数に入れる必要があります。

プロジェクトで関数のデータが変更されdata、新しい dom に基づいて何かを実行したい場合は、新しい一連の js 操作をコールバック関数にDOM入れる必要があります。Vue.nextTick()

3.2.2 $nextTick() の実行原理

VueDOM更新は非同期で実行されます。データの変更が検出される限り、Vueタスクキューが開かれ、同じタイム ループで発生するすべてのデータ変更がバッファリングされます。同じものwatcherが、キューにプッシュされるのは 1 回だけです。(バッファリング中のこの重複排除は、不要な計算やDOM操作)

そして、次のイベントループ「tick」で、Vueキューをフラッシュし、タスクキュー (重複排除) の作業を実行します。

VuePromise.then内部的には、非同期キューにネイティブ (マイクロタスク) を使用しようとしますMutationObserver。実行環境がサポートしていない場合は、代わりに (マクロタスク)setImmediateを使用します。setTimeout(fn, 0)

3.3 JS の動作メカニズム

JS実行はシングル スレッドであり、イベント ループ ベースです。イベント ループは、次の手順に大別されます。

  1. すべての同期タスクはメイン スレッドで実行され、実行スタックが形成されます。
  2. メインスレッドに加えて、「タスクキュー」 ( task queue) もあります。
  3. 非同期タスクに実行結果がある限り、イベントは「タスク キュー」に配置されます。
  4. 「実行スタック」内のすべての同期タスクが実行されると、システムは「タスク キュー」を読み取り、そこにあるイベントを確認します。それらの対応する非同期タスクは待機状態を終了し、実行スタックに入り、実行を開始します。メインスレッドは、上記の 3 番目のステップを繰り返し続けます。

ここに画像の説明を挿入

メイン スレッドの実行プロセスはティックであり、すべての非同期結果は「タスク キュー」を通じてスケジュールされます。タスクキュー」には、タスクが1つずつ格納されます。仕様では、タスクをマクロ タスクマイクロ タスクの2 つのカテゴリに分類することが規定されています

ここに画像の説明を挿入

3.3.1 マイクロタスク

とも呼ばれjob、通常、現在実行中のスクリプトの直後に発生する処理に使用されます。たとえば、一連の動作に反応する場合や、新しいタスクを作成せずに非同期タスクを実行する場合などです。javascript各タスクの最後に、実行スタックで他に何も実行されていない限り、マイクロタスク キューはコールバック後に処理されます。マイクロタスク中にキューに入れられた他のマイクロタスクは、このキューの最後に追加されます。

一般的なマイクロタスクはMutationObsever、、、ですPromise.then$nextTiock

3.3.2 マクロタスク

javascript / domマクロ タスクの役割は、ブラウザが内部からコンテンツを取得できるようにし、実行スタックが順次処理できるようにすることです。HTML の解析、マウス クリック イベントのコールバックの取得など、スケジューリングはいたるところにあります。

一般的なマクロ タスクはsetTimeoutMessageChannelのとおりですpostMessagesetImmediate

vueDOM 更新の場合、非同期キュー制御のために内部的にも呼び出されますnextTick自分で呼び出すとnextTick、DOM を更新するマイクロタスクの後に独自のコールバック関数が追加され、DOM が更新された後にコードが実行されるようになります。

setTimeoutマクロタスクであり、遅延実行のみであり、遅延実行方式ではDOM更新される場合とされない場合があります。

3. ソリューション

注意⚠️:

  • uni-appアプリ側の組み込みHTML5+エンジンにより js は豊富なネイティブ機能を直接呼び出すことができます。

    条件付きコンパイルの呼び出しHTML5+: アプレットや H5 などのプラットフォーム用のHTML5+拡張ため、 の拡張仕様を呼び出すuni-app場合は、条件付きコンパイルの使用に注意する必要があります。HTML5+そうしないと、h5 やアプレットなどのプラットフォームで実行したときにplus is not definedエラー。

    // #ifdef APP-PLUS
    var appid = plus.runtime.appid;
    console.log('应用的 appid 为:' + appid);
    // #endif
    
  • uni-appロジック レイヤーはjscore、ローカル マシンに依存しない独立した環境で実行されるwebviewため、一方ではブラウザーの互換性の問題がなく、その上でコードAndroid4.4を実行es6できますが、他方では、window、document、navigator、localstorage他のブラウザー固有の jsを実行することはできません。 API。

    jscore標準のjsエンジンで、if、for、各種文字列、日付処理など、標準のjsが正常に動作します。js とブラウザの違いを区別する必要があります。

  • uni-appjsは実行されず、独立しているwebkit remote debugため、アプリ側にアプリなどはありませんuni-appwebviewjscore

  • ブラウザGUI レンダリング スレッドJS エンジン スレッドは相互に排他的であるため、JS エンジンが実行されると、JS エンジンの実行が終了するまで GUI スレッドは中断されます。ページに長いタスクが多数ある場合、ページ UI がブロックされ、インターフェイスが動かなくなり、フレームがドロップされます。

GUI レンダリング スレッドhtml、css: ページの描画とレンダリング、リソースの解析、DOM ツリーの生成、およびページの描画を担当する、よく知られているすべてのスレッドがこのスレッドを担当します。
JS エンジン スレッド: すべての非同期および同期タスクの js 解析と実行、実行スタックの維持、最初に同期コードを 1 つずつ処理する責任を負います。非同期タスクに遭遇すると、イベントを使用してスレッドをトリガーします。

ブラウザのプロセスとスレッドの情報は次のとおりです。
ここに画像の説明を挿入
そのうち、フロントエンドは主にブラウザ カーネルのレンダリング プロセスに焦点を当てています。ブラウザ カーネルは主にhtml、css、jsリソースの解析とレンダリングを担当し、イベント ループと非同期リクエストも担当します。 .

データストレージ: uni-app のキャッシュと H5 キャッシュはメモリに保存されますか?

  • ネイティブplus Storageモジュールは、アプリケーション データの保存と読み取りに使用される、アプリケーションのローカル データ ストレージ領域を管理します。アプリケーション ローカル データ と との違いは、データが異なるドメインで有効であるlocalStorageことです. 前者はアプリケーション内のドメイン間で操作できます. データの保存期間は永続的で、容量制限はありません. アプリケーション ローカル データ管理オブジェクトは、から取得できます。sessionStorageplus.storage

  • uni-appアプリケーションuni.setStorage(OBJECT)(マクロタスク) およびuni.setStorageSync(KEY,DATA)(マイクロタスク) がデータを保存する場合、本質的にはネイティブを使用しplus.storage、サイズの制限はなく、キャッシュではなく、sqlite永続化のためにデータベース ファイルにデータを保存することです。

  • このアプリは、SQLiteIO ファイルなどのローカル ストレージ ソリューションもサポートしています。

  • H5 アプリケーションlocalStoragesessionStorageデータ ストレージの場合、ブラウザーはサイズを 5M に制限します。これはキャッシュの概念であり、クリーンアップされる可能性があります。

  • アプリケーションはVuex状態管理を実装します。

  • ネイティブplus.cache管理アプリケーションのキャッシュ データにはwebview、プログラムで生成されたデータのみが含まれ、拡張機能api( uni.setStorage(OBJECT)(マクロタスク)、uni.setStorageSync(KEY,DATA)(マイクロタスク) およびその他の API など) を使用してビジネス ロジックに保存されたデータは含まれません。

  • 各アプレットには独自の が付属しておりstorage api、データ ストレージのライフ サイクルはアプレット自体と一致しています。つまり、ユーザーが積極的に削除したり、一定期間後に自動的にクリーンアップされたりしない限り、データは常に利用可能です。

  • uni-appフレームワーク コンポーネント自体で解決できない問題については、フレームワーク コンポーネントを破棄し、ネイティブ コンポーネントを使用して最適化することができます。

最後に、下部のタブを切り替えるときにキャッシュ クリーニングを行うことを選択します (ここでのキャッシュ クリーニングは、plus.cache.clearアプリケーションのキャッシュ データをクリアするために使用されることに注意してください)。アプリケーション キャッシュ データのクリアには、プログラムでの使用によって生成されたデータのみが含まれ、ビジネス ロジックの拡張機能によって保存されたデータはwebview含まれません。apiコードは以下のように表示されます:

// 点击 tab 时触发
onTabItemTap (e) {
    
    
	console.log('-----------点击 tab 时触发-----------', e);
	// #ifdef APP-PLUS
	plus.cache.clear( function () {
    
    
		console.log( "component Clear application cache successful!" );
	});
	// #endif
},

4. 拡張読書

おすすめ

転載: blog.csdn.net/sunhuaqiang1/article/details/129224778