ホームページ  >  記事  >  ウェブフロントエンド  >  JS フロントエンドのパフォーマンス最適化の概要

JS フロントエンドのパフォーマンス最適化の概要

小云云
小云云オリジナル
2018-02-28 13:42:481727ブラウズ

最善のリソース最適化は、リソースをロードしないことです。キャッシュは最も効果的な最適化方法でもあります。正直に言うと、クライアント側のキャッシュはブラウザ側で発生しますが、キャッシュは主にサーバーによって制御され、フロントエンドとはほとんど関係がありません。しかし、それでも理解する必要があります。

キャッシュには、サーバー側のキャッシュとクライアント側のキャッシュが含まれます。この記事では、クライアント側のキャッシュについてのみ説明します。いわゆるクライアント キャッシュは主に http キャッシュです。 HTTP キャッシュは主に強制キャッシュとネゴシエート キャッシュに分けられます。

強制キャッシュ

  • Expires (http1.0)

強制キャッシュを実行するには、http1.0 で Expires を使用します。 Expires の値は、サーバーから返されたデータの有効期限です。再リクエスト時のリクエスト時間が返された時間よりも短い場合は、キャッシュされたデータがそのまま使用されます。ただし、サーバー時間とクライアント時間は異なる可能性があるため、これもキャッシュ ヒット エラーの原因となります。

  • Cache-Control

Cache-Control には多くの属性があり、属性が異なれば意味も異なります。

  1. private: クライアントはキャッシュできます

  2. public: クライアントとプロキシサーバーの両方がキャッシュできます

  3. max-age=t: キャッシュされたコンテンツは t 秒後に期限切れになります

  4. no-cache : キャッシュされたデータを検証するには、ネゴシエーション キャッシュが必要です

  5. no-store: すべてのコンテンツはキャッシュされません。

キャッシュをネゴシエートする

ブラウザが初めてデータをリクエストすると、サーバーはキャッシュ ID とデータをクライアントに応答し、クライアントはそれらをキャッシュにバックアップします。再度リクエストする場合、クライアントはキャッシュ内の識別子をサーバーに送信し、サーバーはこの識別子をもとに判断します。有効期限が切れていない場合は、304 ステータス コードが返され、ブラウザはこのステータス コードを受信した後、キャッシュされたデータを直接使用できます。

  • Last-Modified

サーバーがリクエストに応答すると、リソースの最終変更時刻がブラウザに通知されます

  • if-Modified-Since

ブラウザがサーバーをリクエストしたとき再度、リクエスト ヘッダーにはこのフィールドが含まれ、その後にキャッシュで取得された Last-Modified (最終変更時刻) が続きます。サーバーがこのリクエスト ヘッダーを受信し、if-Modified-Since がある場合、リクエストされたリソースの最終変更時刻と比較し、それがリクエストされたリソースの最終変更時刻よりも大きい場合、ブラウザは 304 を返します。キャッシュからリソースを取得します。要求されたリソースの最終変更時刻よりも前の場合、200 が返され、ブラウザは最新のリソースをサーバーから取得してキャッシュします。

  • Etag

サーバーによって生成された各リソースの一意の識別文字列

  • If-None-Match

サーバーが再度リクエストされると、ブラウザのリクエストメッセージヘッダーにはこのフィールドが含まれます。次の値はキャッシュで取得された識別子です。メッセージを受信した後、サーバーは If-None-Match を見つけ、それを要求されたリソースの一意の識別子と比較します。それらが同じである場合は、リソースが変更されていないことを意味し、304 を返し、ブラウザはキャッシュからリソースを取得します。それらが異なる場合、リソースが変更されたことを意味し、200 を返し、最新のリソース。ブラウザはサーバーから最新のリソースを取得し、キャッシュします。

Last-Modified と ETag は一緒に使用できます。サーバーは最初に ETag を検証し、それらが一致している場合は、Last-Modified の比較を続け、最終的に 304 を返すかどうかを決定します。

フロントエンドパッケージ化ツールを使用すると、パッケージ化するときにファイルにバージョン番号またはハッシュ値を追加でき、リソースの有効期限が切れているかどうかを区別することもできます。

httpリクエストを削減

  • CDNを使用して静的リソースをホスト

  • gulp、webpack、その他のパッケージ化ツールを使用して、js、css、その他のファイルをマージおよび圧縮できます

  • 画像の遅延読み込み、オンデマンド画像は、画像の表示領域にのみロードされます。

  • 小さな画像や基本的に変更されていない画像は、base64 エンコードを使用して送信されます。 Base64 を悪用しないでください。base64 エンコード後には、小さな画像であっても長い文字列が生成されます。画像を変更するとスプライト画像全体が変化するため、スプライト画像は基本的に変更されない画像に使用されます。再生されるため、むやみに使用すると逆効果になります。

  • httpリクエストリソースのサイズを削減します

webpack、gulp、その他のツールを使用してリソースを圧縮します

  • サーバー上でgzip圧縮を有効にします(圧縮率は非常に優れており、通常は30%以上です)

  • 便利なパッケージ化ツール、パッケージ化の最適化をうまく行う必要がある場合、パブリックリソース、サードパーティのコード抽出、パッケージ化する必要のないライブラリ...

  • レンダリングの最適化

  • 以前のjs実行を読んだ人ブラウザからURLを入力して、そのページが画面に表示されるまでに何が起こったのかを機構は知っているはずです(TCPハンドシェイクやDNS解決などは知識の範囲外です)。

FPS 16ms、10ms未満が最適です。Google devtoolを使用してフレームレートを確認してください

ブラウザの FPS が 60 に達すると、よりスムーズに表示されます。ほとんどのモニターの更新周波数は 60 Hz であり、ブラウザはこの周波数でアニメーションを自動的に更新します。
FPS 60 に基づいて計算すると、平均フレーム時間は 1000ms/60 = 16.7ms となるため、各レンダリング時間は 16ms を超えることはできず、この時間を超えるとフレーム損失と遅延が発生します。

Chrome ブラウザの開発者ツールのタイムラインでリフレッシュ レートを確認でき、すべてのフレーム レートの消費時間と特定のフレームの実行を確認できます。タイムラインの使用方法のチュートリアル: https://segmentfault.com/a/11...

通常の FPS を確保するには、レンダリング パフォーマンスの最適化がまだ必要です。以下は、レンダリングの最適化に関連するすべての戦略です。

  • アニメーションには css3 を使用してみてください

ご存知のとおり、css のパフォーマンスは js よりも速いため、css を使用して、それを実現するために js を使用しないようにすることができます

  • setTimeout やsetInterval、requestAnimationFrame を使用してみてください。アニメーションまたは高頻度の Dom 操作を実行します。

setTimeout や setInterval はコールバック関数の実行タイミングを保証できないため、フレームの最後に実行されてフレームロスが発生する可能性がありますが、requestAnimationFrame はコールバック関数がフレームの先頭で実行されることを保証できます。アニメーションの各フレーム
requestAnimationFrame の中国語 MDN アドレス: https://developer.mozilla.org...

  • 複雑な計算操作には Web Workers を使用します

アニメーションの走査や合計などの複雑なデータ操作が必要な場合は、要素を含む配列の場合、Web Workers がこれ以上に適しています。

Web ワーカーは、JavaScript スクリプトをバックグラウンド スレッドで実行できるようにすることができ (子スレッドの作成と同様)、バックグラウンド スレッドはメイン スレッドのページに影響を与えません。ただし、Web Workers を使用して作成されたスレッドは DOM ツリーを操作できません。
Web Workers の詳細については、MDN の詳細な説明を参照してください: https://developer.mozilla.org...

  • css が先頭に配置され、js が末尾に配置されます。

以前の js 実行メカニズムを読んだことのある人は、ページのレンダリングのプロセスを知っているはずなので、詳細については説明しません。 CSS を先頭に配置すると、HTML ツリーの生成後に再レイアウトされるスプラッシュ スクリーン現象が回避されます。一般に、CSS はページに大きな影響を与えるため、通常は最後の実行のために最後に配置されます。

  • イベントのデバウンスとスロットリング

高頻度でトリガーされるイベント (mousemove、scroll) やその他のイベントの場合、制御されていない場合、短期間に多くのイベントがトリガーされます。

関数のアンチシェイクは、頻繁にトリガーされた場合、十分な空き時間があるときにコードが 1 回だけ実行されることを意味します。シナリオ: 登録時のメール入力ボックスで、ユーザーが入力すると、最初の入力イベントがトリガーされたときにメール形式が正しいかどうかをリアルタイムで判断し、800ms 後にチェックを実行するタイミングを設定します。 100 ミリ秒だけが経過し、最後のタイマーが実行されていない場合は、タイマーをクリアして、時間を 800 ミリ秒に再設定します。最新の入力までは隣接する入力がなく、この最新の入力のタイミングが終了し、最終的にチェックコードが実行される。

const filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;  
$("#email").on("keyup",checkEmail());  
function checkEmail(){  
    let timer=null;  
    return function (){  
        clearTimeout(timer);  
        timer=setTimeout(function(){  
            console.log('执行检查');  
        },800);  
    }  
}

関数スロットルとは、js メソッドが一定期間内に 1 回だけ実行されることを意味します。つまり、当初は 1 秒あたり 100 回実行されていたものが、1 秒あたり 10 回になります。
シナリオ: 機能調整アプリケーションの実際のシナリオ。そのほとんどは、ページ要素のスクロール イベントを監視するときに使用されます。

var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }

    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};
  • Dom 操作

フロントエンド開発者は皆、Do 操作が非常に時間がかかることを知っています (私はスタイルを追加するために 30*30 テーブルの走査を個人的にテストしました)。したがって、頻繁な Dom 操作を避けるようにしてください。それができない場合は、DOm 操作を最適化してください。

1.:缓存Dom查询,比如通过getElementByTagName('p')获取Dom集,而不是逐个获取。

2: 合并Dom操作,使用createDocumentFragment()
    var frag = document.createDocumentFragment()
    for (i<10) {
        var li = document.createElement(&#39;li&#39;)
        frag.appendChild(li)
    }
    document.body.appendChild(frag)
rree
  • 再描画とリフローを避けるようにしてください

js を使用して要素の色や背景色を変更する場合、ブラウザが後で再描画を行うため、再描画のコストは依然として比較的高くなります。特定の DOM 要素の視覚効果が変更された場合は、この DOM 要素内のすべてのノードを確認してください。

要素のサイズと位置を変更すると、リフローが発生し、特定の DOM 要素の位置が変更された後にトリガーされ、すべての要素の位置とそれらが占める領域が再計算されます。これにより、ページの特定の部分またはページ全体が再レンダリングされます。

  • css3ハードウェアアクセラレーション

ブラウザがレンダリングするとき、通常レイヤーと複合レイヤーの2つのレイヤーに分割されます。

通常のドキュメントフローは複合レイヤーとして理解できますが、絶対レイアウトと固定レイアウトは通常のドキュメントフローから分離できますが、依然として通常のレイヤーに属しており、ハードウェアアクセラレーションはアクティブ化されません。上記の再描画とリフローは、通常のレイヤー上での再描画とリフローを指します。

複合レイヤーによりハードウェアアクセラレーションが可能になります。これは通常のレイヤーと同じレイヤー上にないため、要素が複合レイヤーに昇格されてからその要素が操作されても、通常のレイヤーが再描画されることはありません。これにより、レンダリングのパフォーマンスが向上します。

ハードウェアアクセラレーションを開始する方法:

1.translate3dとtranslateZを使用します

webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);

webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);

2.使用opacity
需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态

3.使用will-chang属性
这个属性比较不常用,一般配合opacity与translate使用

针对webkit浏览器,启用硬件加速有些时候可能会导致浏览器频繁闪烁或抖动,可以使用下面方法消除:

-webkit-backface-visibility:hidden;
-webkit-perspective:1000;
如果使用硬件加速,请使用z-index配合使用, 因为如果这个元素添加了硬件加速,并且index层级比较低, 那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且releative或absolute属性相同的), 会默认变为复合层渲染,如果处理不当会极大的影响性能
  • 避免强制同步布局和布局抖动

浏览器渲染过程为:js/css(javascript) > 计算样式(style) > 布局(layout) > 绘制(paint) > 渲染合并图层(Composite)

JavaScript:JavaScript实现动画效果,DOM元素操作等。
Style(计算样式):确定每个DOM元素应该应用什么CSS规则。
Layout(布局):计算每个DOM元素在最终屏幕上显示的大小和位置。
Paint(绘制):在多个层上绘制DOM元素的的文字、颜色、图像、边框和阴影等。
Composite(渲染层合并):按照合理的顺序合并图层然后显示到屏幕上。

在js中如果读取style属性的某些值就会让浏览器强行进行一次布局、计算,然后再返回值,比如:

offsetTop, offsetLeft, offsetWidth, offsetHeight

scrollTop/Left/Width/Height

clientTop/Left/Width/Height

width,height

请求了getComputedStyle(), 或者 IE的 currentStyle

所以,如果强制浏览器在执行JavaScript脚本之前先执行布局过程,这就是所谓的强制同步布局。
比如下面代码:

requestAnimationFrame(logBoxHeight);

// 先写后读,触发强制布局
function logBoxHeight() {
    // 更新box样式
    box.classList.add('super-big');

    // 为了返回box的offersetHeight值
    // 浏览器必须先应用属性修改,接着执行布局过程
    console.log(box.offsetHeight);
}

// 先读后写,避免强制布局
function logBoxHeight() {
    // 获取box.offsetHeight
    console.log(box.offsetHeight);

    // 更新box样式
    box.classList.add('super-big');
}

在JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。因此,如果你在当前帧获取属性之前又对元素节点有改动,那就会导致浏览器必须先应用属性修改,结果执行布局过程,最后再执行JavaScript逻辑。

如果连续多次强制同步布局,就会导致布局抖动
比如下面代码:

function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + &#39;px&#39;;
  }
}

作者:SylvanasSun
链接:https://juejin.im/post/59da456951882525ed2b706d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们知道浏览器是一帧一帧的刷新页面的,对于每一帧,上一帧的布局信息都是已知的。
强制布局就是使用js强制浏览器提前布局,比如下面代码:

// bed  每次循环都要去获取left ,就会发生一次回流
function logBoxHeight() {
  box.style.left += 10
  console.log(box.style.left)
}

// goog 
var width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = width + &#39;px&#39;;
  }
}
  • DOMContentLoaded与Load

DOMContentLoaded 事件触发时,仅当DOM加载完成才触发DOMContentLoaded,此时样式表,图片,外部引入资源都还没加载。而load是等所有的资源加载完毕才会触发。

1. 解析HTML结构。
2. 加载外部脚本和样式表文件。
3. 解析并执行脚本代码。
4. DOM树构建完成。//DOMContentLoaded
5. 加载图片等外部文件。
页面加载完毕。//load
  • 视觉优化

等待加载时间可以合理使用loading gif动图一定程度上消除用户等待时间的烦躁感

代码性能

代码对性能的影响可大可小,但是养成一个良好的写代码习惯和高质量的代码,会潜移默化的提高性能,同时也能提高自己的水平。废话不多说,直接看我总结的部分要点(因为这一部分知识点太多,需要大家写代码的时候多多总结)。

  • 避免全局查找

访问局部变量会比访问全局变量快,因为js查找变量的时候现在局部作用局查找,找不到在逐级向上找。

// bad
function f () {
    for (...){
        console.log(window.location.href)
    }
}

//good
function f () {
    var href = window.location.href
    for (...){
        console.log(href)
    }
}
  • 循环技巧

// bed 
for(var i = 0; i < array.length; i++){
    ....
}
// good
for(var i = 0, len = array.length; i < len; i++){
    ....
}
// 不用每次查询长度
  • 不要使用for in 遍历数组

for in是最慢的,其他的都差不多,其中直接使用for循环是最快的。for in只是适合用来遍历对象。

  • 使用+''代替String()吧变量转化为字符串

var a = 12
//bad
a = String(a)

// good
var a = 12
a = a + &#39;&#39;

这个还有很多类似的,比如使用*1代替parseInt()等都是利用js的弱类型,其实这样对性能提升不是很大,网上有人测试过,进行十几万次变量转换,才快了零点几秒。

  • 删除dom

删除dom元素要删除注册在该节点上的事件,否则就会产生无法回收的内存,在选择removeChild和innerHTML=''二者之间尽量选择后者,据说removeChild有时候无法有效的释放节点(具体原因不明)

  • 使用事件代理处理事件

任何可以冒泡的事件都可以在节点的祖先节点上处理,这样对于子节点需要绑定相同事件的情况就不用分别给每个子节点添加事件监听,而是都提升到祖先节点处理。

  • 通过js生成的dom对象必须append到页面中

在IE下,js创建的额dom如果没有添加到页面,这部分内存是不会被回收的

  • 避免与null比较

可以使用下面方法替换与null比较
1.如果该值为引用类型,则使用instanceof检查其构造函数
2.如果该值为基本类型,使用typeof检查类型

  • 尽量使用三目运算符代替if else

if(a>b){num = a}
else{num = b}

// 可以替换为
num = a > b ? a : b
  • 当判断条件大于3中情况时,使用switch代替if

因为switch的执行速度比if要快,也别是在IE下,速度大约是if的两倍。

相关推荐:

CSS解读前端性能优化的具体分析

在HTML5中如何提高网站前端性能的示例代码分析

web前端性能优化方法


以上がJS フロントエンドのパフォーマンス最適化の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。