View State(ビューステート)とは
ページの一部を書き換えるAjax通信(VisualforceタグのreRender属性など)の際、
画面の状態を保持するために必要な情報。
実態はinput type="hidden"
のvalueに入っている暗号化された文字列。
暗号化の方法はBase64でのエンコード。
post backリクエストの際にVisualforceの状態を保持するための情報が入っている。
(post backリクエスト=自ページへのリクエスト)
最大サイズは170KBまでとなっており、それを超えるとエラーで落ちます。
ビューステートが作られる条件
ビューステートはapex:form
タグがある時のみ作られます。
- 作られる時のコード(
apex:form
あり)
<apex:page controller="reRenderTest">
<h1>reRenderテスト</h1>
<apex:form id="formId">
<apex:outputText>{!testStr}</apex:outputText>
<apex:commandButton reRender="formId" action="{!reRenderStrAction}" value="テストボタン" />
</apex:form>
</apex:page>
- 作られる
input type="hidden"
<span xmlns="http://www.w3.org/1999/xhtml" id="ajax-view-state-page-container" style="display: none">
<span id="ajax-view-state" style="display: none">
<input type="hidden" id="com.salesforce.visualforce.ViewState" name="com.salesforce.visualforce.ViewState"
value="エンコードされた文字列"
autocomplete="off">
<input type="hidden" id="com.salesforce.visualforce.ViewStateVersion"
name="com.salesforce.visualforce.ViewStateVersion" value="数字" autocomplete="off">
<input type="hidden" id="com.salesforce.visualforce.ViewStateMAC" name="com.salesforce.visualforce.ViewStateMAC"
value="エンコードされた文字列"
autocomplete="off">
<input type="hidden" id="com.salesforce.visualforce.ViewStateCSRF"
name="com.salesforce.visualforce.ViewStateCSRF"
value="エンコードされた文字列"
autocomplete="off">
</span>
</span>
- 作られない時のコード(
apex:form
なし)
<apex:page controller="reRenderTest">
<h1>reRenderテスト</h1>
<form>
<input type="text" name="test" value="test" />
</form>
</apex:page>
apex:commandButton
等のreRenderを使ってページの一部を書き換える系タグが、
apex:form
内に存在しないとデプロイエラーになるのは、
apex:form
のビューステートを作ってAjax時の情報保持をするのに必要だからだったんですね。
主なビューステートの対象
対象1:Visualforceコンポーネント
-
Visualforceタグはビューステートサイズが増える
-
apex:form
の中でも外でも増える - reRender属性を持っていてもいなくても増える
-
-
標準HTMLタグ(pタグ、inputタグなど)は増えない
-
apex:form
の中でも外でも増えない
-
対象2:Apexの変数
- publicもprivateも同量のビューステートサイズ
- ビューステートにカウントされるのは、値が代入されている変数のみ
各タグのビューステートサイズ実験結果
- 量が多いので別ページに分離しました
- 目立って増加しているのは以下の項目でしょうか
- Component Tree
- Internal
- 特にInternalの増え幅が大きいですね
(ちなみに)internalとは
Visualforce ページによって使用される内部 Salesforce データを表します。
開発者はこのノードを制御できません。
ビューステートサイズのうち、内部要素がどの程度を占めているかを表示するには、[State (状態)] フォルダをクリックします。
開発者ガイドより
完全にピンとは来ないですが、
変数やコンポーネントのViewStateサイズと分離して表示しているあたり、
開発者が視認してるコードより更に内部的な情報(メモリとかそういうレベル)なんでしょうか。
docsには「制御できない」と書いてありますが、
StandardSetControllerでページネーションを実装したときの「1ページあたりのレコード表示数」によってInternalは増減するようなので、ある程度は調整が効きますね。
ビューステートサイズの確認方法
以下の2通りがあります。
- Visualforceページから確認する方法
- 開発者コンソールから確認する方法
開発者コンソールからの確認は僕の環境ではできませんでしたが、
見れる内容はVisualforceページとほぼ同じなので一旦気にせず進むとします。
ビューステートサイズを確認する際の注意点
- ビューステートサイズが制限を超えて落ちる場合、ビューステートタブも見れなくなる
一旦ビューステートサイズを削ってエラーが起きないようにし、
どこがビューステートを割合多く消費しているか確認するというデバッグ方法になります。
- ビューステートモードだとVisualforceの実行時間が以上に長くなって、CPUガバナで落ちることがある
増えてる部分がスタックログにも出てこないので、
何きっかけでCPU時間が増えているのか分からないですが、この場合一旦CPU時間を削ってのデバッグが必要になります。
ビューステートの減らし方
transient修飾子を付ける
transient 修飾子を付けて変数を宣言することによって、その変数をビューステートの対象外とすることができます。
ただし「ビューステートによりpost back間で値が保持される」という恩恵は受けられなくなるので、
Visualforceタブのaction属性を実行したりすると、変数がリセットされて空になります。
- 変数リセットの回避方法
- reRenderの範囲外に記述する
- ※あくまでviewの表示上のリセット回避で、サーバーサイドではリセットされている
- action属性にメソッドを指定 → 指定したメソッド内で再代入する
- reRenderの範囲外に記述する
変数を静的にする
静的な変数はビューステートに含まれません。
transient キーワードの使用より
Visualforceタグの数を減らす
- VisualforceタグをHTMLの標準タグに置き換えるなど
-
apex:repeat
などを使ってると、思ってる以上にVisualforceタグが描画されてたりするので注意が必要
apex:inputXXXXコンポーネントは、特にapex:repeatやテーブルの中に置いた場合、多くの内部ビュー状態を発生させることがあります
How to reduce a large internal view state / what is in the internal view state?より
StandardSetControllerクラスを使ってページネーションを導入する
ちなみにStandardSetControllerでページネーションを実装した場合、
1ページあたりのレコード数が多くても少なくても、
StandardSetControllerインスタンスのビューステートサイズは変わりません。
※ただしInternalの量は増えます
JSで作成した変数をVisualforceにバインドする
JSで作成した変数をバインドした場合、ビューステートサイズが10分の1以下になるようです。
SFDC:【大量データ処理】Apex開発とJavaScript開発 - ViewStateの比較より
その他
ビューステートの表示速度への影響
ヘルプによると、
一般に、ビューステートサイズが小さいほど読み込み時間が短くなります。
とのこと。
実験結果
- ビューステート:2KB
- VISUALFORCE項目:0.1秒
- ビューステート:70KB
- VISUALFORCE項目:1.5秒
VISUALFORCE項目が約15倍になっていますね。
(10KBあたり0.2秒増加)
コードの変更点は以下のみなので、増加分はビューステートの生成時間とかでしょうか。
- Visualforceにバインドしてないメンバ変数のリスト要素数を増やした
- ※view側の描画量は全く同じ
まとめ
ビューステートはガバナ制限としての遭遇率こそ低いですが、
Visualforceのajaxを支える根幹技術だと知って驚きました。
大きいシステムだとサイズ制限やパフォーマンス向上のために考慮が必要なので、
この記事がお役に立てれば幸いです。
参考文献
[salesforce]ビューステートについて
ビューステートを意識したVisualforce【セールスフォース】
Visualforceのビューステートおよび画面間情報維持について
[Salesforce]ビューステート
ビューステートの最大表示サイズ
ビューステートの解消方法
SFDC:【大量データ処理】Apex開発とJavaScript開発 - ViewStateの比較
ページング使ってパフォーマンス改善
【SALESFORCE】VIEW STATEエラーのINTERNALについて
開発モードフッターの使用
How to reduce a large internal view state / what is in the internal view state?