はじめに
(初期の質問文が修正依頼のようであるという誤解を多数いただいたので、質問内容を修正させていただきました。
不完全なところありましたらおしらせください。)
今回人生初の自作のサイトで、処理が重くなってしまい、軽量化が初めての経験だったため、前例を得るべく、勉強のために質問させていただきました。
何を求めているのか?
経験談を聞きたいのです。
>サイトを作成したことのある方に質問します。
今まで、サイト作成の途中で作っているサイトが重たくなってしまった経験がある方もいると思うのですが、その時の原因と対処をどのようにしましたでしょうか?
(プログラマとして歴が浅い時のミスによるものやプログラマとして成長した段階でもよくつまづいてしまうことなどの失敗談などによって、自分の今回のサイトの修正の参考になるだけでなく、
他の駆け出しの方の今後の重たくなった時の考える指針になればいいと思っています。)
自分はこれが初めてのコーディングであり、重くなったことの対処法や原因の例が足りなくアイディアが足りないため、質問させていただきました。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/11/24 03:45
2017/11/24 23:07

回答4件
0
ボトルネックを探してくれ、原因はどこかわからないから全部見てくれ、というのはクラウドソーシングへ依頼お願いします。
ご自分で原因を絞り込むのであれば、お使いのブラウザの開発者ツール等で「Performance」とか「Profile」とかいう項目があると思いますので、そちらで検証してください。
投稿2017/11/24 01:58
総合スコア9210
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/11/25 07:31
2017/11/26 09:04

0
軽量化の第一歩はボトルネックを死ぬ気で探すこと。
console.timeとconsole.timeEndを埋め込みまくったり、
短いコードを100万回走らせたりしてみて記録をとりつつ覚えよう。
まず、整数の変数を宣言する時は$a = $b|0;
のように縦棒0を付けた方が若干速いとか、
インクリメント($i++
)より足したほうが速い($i += 1;)
とか、
細々したテクニックは全体何十秒もかかるようなクソコードの前では全く意味がない。
ボトルネックを全て潰し終わった後に、全体10msで終わる処理を9.9msにしたい時にチューニングの時に初めて威力を発揮するから今回は採用すべきではないね。
以下は鉄則
- ループ回数は最小限
- DOMはできれば触るな、避けて通れないなら最小限
- 重い計算は何度もせずに変数に一時保存
Reactに限っては以下も追加
- JSの比較の仕様を覚える
以下1つずつ解説
ループ回数は最小限というのは特に配列操作で一番大事な鉄則
例えば100個の配列と50個の配列を普通に掛け算すれば100*50=5000
回の演算になるけど、
少しの工夫で100+50=150
回の演算に出来る。
例として、ユーザー一覧配列では県コード、県一覧配列の2つがあり、ユーザーの一覧を表示する時に都道府県名で表示したいケースを考える
JavaScript
1const users = [ 2 {name: "taro", pref: 1}, 3 {name: "juro", pref: 13}, 4 // ...こういうデータが100件 5]; 6const prefs = [ 7 {id: 1, name: "北海道"}, 8 {id: 13, name: "東京都"}, 9 // ...こういうデータが都道府県分 10]; 11let usersWithPref; 12 13// 100 * 50になるパターン 14usersWithPref = users.map(user => { 15 const pref = prefs.find(pref => user.pref === prefs.id); 16 return {...user, prefName: pref.name}; 17}); 18 19// 100 + 50になるパターン 20let prefById = {}; // わかりやすさ重視でreduceを使わない方法にした 21prefs.forEach(pref => prefById[pref.id] = pref.name); 22usersWithPref = users.map(user => { 23 const pref = prefById[user.pref]; 24 return {...user, prefName: pref.name}; 25});
実際にはArray.findは条件に合致した要素を発見次第探索を打ち切るので、
1ユーザー平均20回程度の確認で終了すると思われるが、もし複雑な計算をした結果得られる情報を元に比較検証しなければならないとしたら大惨事だ。
最初のキャッシュを47ループ分のコストを使って作っておいたお陰で、userが都道府県名を取り出すコストはほぼ0になり、約2000回の計算はわずか150回に……この差は結構でかい。
2つ目のDOMを触るなっていうのは、単純にGUIへのレンダリングはコストがデカいから。
ループ文で少しずつ変更するんじゃなくて、全てのデータが揃ってから一撃で変えた方が良い。
3ツ目の変数へのキャッシュも分かりやすいと思うので割愛。
Reactは賢く値が変更されたか変更されていないかで再描画をするしないを決める。
値が変更されたって何が基準?基準は===
だ
(仕様をちゃんと見たわけではないけど、そのように見える)
JavaScriptはプリミティブ型とそれ以外では===で比較した時の結果が違う。
ちょっと下記のコードをコンソール開いて打ち込んでみよう。
そして、思った通りの結果になるか確かめて欲しい。
JavaScript
11 === 1; 2"test" === "test"; 3true === true; 4[1, 2, 3] === [1, 2, 3]; 5{test: 1} === {test: 1}; 6(it => it + 1) === (it => it + 1);
例えばPHPでは[1, 2, 3] === [1, 2, 3]
はtrueと評価されるが、JSではfalseになってしまう。
JSの世界ではFunctionやArray、Objectといったものは、
作成される度に見えない管理IDが割り振られ、比較すると管理IDを基準に同一の存在か否かでtrue/falseが決定される。
ReactではPropの値が変わらなければ再描画はされない。
しかしJSの比較演算子の仕様の問題で、毎回Propの中でオブジェクトや配列を生成していると、同じものとみなされる事はない。
常に再描画でDOM作り直しになる可能性があり、それが全てのコンポーネントで発生していれば、Reactの賢い部分的な再描画という特性は全く活かせない。
このように同じものとして認識してほしければ変数等に代入して束縛しておこう。
JavaScript
1var a = {test: 1}; 2a === a; // true
投稿2017/11/25 04:40
総合スコア21384
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
一般的な高速化(軽量化)の手法
JavaScriptの高速化(軽量化)の手法は多岐にわたる為、一般的に「これだけやればOK」な解はありません。
大雑把にいえば、次の2つに大別されます。
- スペックを上げる
- 無駄をなくす
ここから更に細かく分別していくと、書ききれない量になるので、「もう少し具体的なところまで切り分けて下さい」と多くの方から指摘されているのだと思います。
いくつか例を挙げると、次のようになります。
- クライアントPCのスペックを上げる(スペックを上げる)
- サーバマシンのスペックを上げる(スペックを上げる)
- 回線速度を増強する(スペックを上げる)
- html/css/jsファイルを圧縮(minified)する
- HTTPサーバ(Apache等)のgzip圧縮を使う
- jsファイルのHTTPリクエスト数を減らす(jsファイルを一つにまとめる)
- script[defer], script[async]
- script要素を </body> 手前に置く(レンダリング停止の影響範囲を下げる)
- ビルトイン関数を含めたアルゴリズム、コード上のアルゴリズムを書きだして、アルゴリズム上の無駄をなくす
- リフロー/リペイント回数を減らす(アルゴリズム上の無駄をなくす)
- スコープチェーンを辿る回数を減らす(アルゴリズム上の無駄をなくす)
- プロトタイプチェーンを辿る回数を減らす(アルゴリズム上の無駄をなくす)
- 同一プロパティの参照回数を減らす(アルゴリズム上の無駄をなくす)
- 無駄にクロージャを生産しない(アルゴリズム上の無駄をなくす)
- 無駄に形式変換しない(アルゴリズム上の無駄をなくす)
- 無駄に同一オブジェクトを何度も生産しない(アルゴリズム上の無駄をなくす)
- 実装(ブラウザ)の最適化に合わせる
上から3つ目までが「スペックを上げる」、4つ目以降が「無駄をなくす」です。
抽象的な高速化系の質問
経験上、高速化系統の質問は、質問が抽象的であるほどに不毛な結果に終わる傾向があります。
初心者は「難しすぎて分かりません」と答え、熟練者は「Aは高速ですが、保守性が悪いのでBを採用しますね」のように高速化以外の判断基準で切り捨てられることが多いです。
高速化は突き詰めると、密結合になる為、保守性を重視する人には敬遠される傾向にあります。
例えば、下記スレッドですが、
- JavaScript - 配列で特定位置の周囲の値を効率よく調べたい アルゴリズム(92687)|teratail
- benchmark - create-square-aa-1.0.1.js - JSFiddle
私は高速化を極限まで追求した為、限界まで密結合し、最大限の結果を得られました(著しく速くなりました)。
蜜結合とは、つまり、「関数 createSquareAa()
」は単一の関数であり、複数の関数では構成されていません。
(対する疎結合では、処理が分割されている都合上、アルゴリズム上の無駄をなくすのにも限界があります。)
このコードを「可読性が悪い」が評価する人がいれば、「これぐらいなら読める」と評価する人もいるでしょう。
その人にとって「最低限これぐらいの規模なら読める」という処理上の「最小限の単位」が異なる為、人によって蜜結合/疎結合のバランスの度合いも変わってきます。
高速化と保守性はトレードオフの関係にあるので、絶対的な基準を作るのは難しいと私は思います。
なので、高速化系の質問であっても、それ以外のコーディング上のポリシーを質問文に添えてほしいと良く思います。
熟練者であるほど、拘りがあるので、ポリシーが書かれているだけで無駄に回答せずにすみます。
(関係ない話ですが、qiitaでの「jQuery vs 仮想DOM」の質問への回答も未だに悩んでます。高速化以外での拘りが相当あるような気がします。)
Re: MOTOMUR さん
投稿2017/11/25 02:56
総合スコア18194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
「このようなコードで、このように表現したいけど表現できない。何故なのか?」
と言うような時の場であるので、質問者さんの悩みも分かりますが、ここでは無い気がします。
自分なりに思うのは「無駄な記述(コード等)をしない」だと思います。「A」を表現するのに10のコードで書いたものを7や8、あるいは5で納めることでしょうか。(圧縮等は前提です。)
何が必要で何が不要なのかを選択するには質問者さんの知識・経験等が不可欠となり、それを得るのがこれからの課題だと思います。頑張ってください。
投稿2017/11/24 21:37
総合スコア3525
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。