Sizzle の「コンパイル原則」についての簡単な説明_その他

WBOY
リリース: 2016-05-16 16:04:20
オリジナル
806 人が閲覧しました

Sizzle は、jQuery の作者である John Resig によって書かれた DOM セレクター エンジンで、その速度は業界初として知られています。独立した新しいセレクター エンジンとして、jQuery バージョン 1.3 の後に登場し、John Resig によってオープン ソース プロジェクトとして採用されました。 Sizzle は独立した部分であり、どのライブラリにも依存しません。jQuery を使用したくない場合は、Sizzle を使用することも、Mool、Dojo、YUI などの他のフレームワークで使用することもできます。

数日前、私は jQuery に関する共有 PPT を準備していました。そして同僚に、jQuery の使用方法以外に何か知りたいことはないか尋ねました。ある人は、jQuery のセレクターがどのように実装されているかを知りたいと言いました。 、jQuery のクエリ速度が他のフレームワークとどのように比較されるかについて言及した人もいます。速度に関しては、sizzle の公式 Web サイトからテスト例をダウンロードできます。速度は確かに非常に有利です。しかし、なぜこれほど効率的な実行速度が得られるのかは、ここで説明したい実装原理と関係があります。

Sizzle を理解する前に、まずそのセレクターが何であるかを理解する必要があります。jQuery に精通している学生は、このセレクター形式にも精通している必要があります。

コードをコピーします コードは次のとおりです:

タグ #id .class 、a:first

基本的に左から右に詳細にフィルタリングして、一致する DOM 要素を見つけます。このステートメントはそれほど複雑ではありません。このクエリ ステートメントを自分で実装するのは難しくありません。ただし、クエリ ステートメントには基本的なルールしかなく、セレクターの固定された数や順序はありません。独自のコードを作成する場合、この任意の配置と組み合わせにどのように適応できるでしょうか。 Sizzle は、さまざまな状況の正常な分析と実行を実現できます。

Sizzle のソースコードは確かに複雑で、その考え方を理解するのは困難です。パッケージングの外層を脇に置き、実装全体の中核であると私が個人的に考える 3 つのメソッドを直接見てみましょう:

最初のコアメソッド。ソースコードの 1052 行目にトークン化関数があります:

コードをコピーします コードは次のとおりです:

関数 tokenize(selector, parseOnly) { }

2 番目のパラメーター parseOnly は false です。これは、トークンのシリアル化操作のみが実行され、結果は返されないことを意味します。この場合、シリアル化の結果は後で使用するためにキャッシュされます。セレクターはクエリ ステートメントです。

この関数で処理された後、たとえば selector="#idtag.class, a:first" が渡されると、次の形式のような結果が得られます。

[
[
{matches:" id ",type:"ID"},
{matches:" tag ",type:"TAG"},
{matches:" class ",type:"CLASS"},
...
],
[
    {matches:" a",type:"TAG"},
    ...
],
[…],
…
]
ログイン後にコピー


tokenize 関数の名前とその機能を見ると、「コンパイル原理」という言葉がすぐに思い浮かびます。ここでは字句解析に少し似ていますが、この字句解析はプログラムのコンパイル時に行われる字句解析よりも単純です。

tokenize メソッドは、セレクター内のコンマ、スペース、リレーショナル セレクターの正規表現に基づいて「単語の分割」を実行し、2 次元配列 (この不正確な名前を使用することを許可してください) を取得します。 -次元配列 これらはカンマで区切られており、ソースコードではグループと呼ばれます。

ソース コードをもう一度見てみましょう。405 行目から Expr = Sizzle.selectors = {} の定義があります。567 行目にフィルターの定義があります。ここで、基本的なフィルター タイプを確認できます。 「、TAG」、「CLASS」、「ATTR」、「CHILD」、「PSEUDO」、これらが最終的にtokenizeで分類された種類になります。

「単語の分割」が完了した後も、405 行目で定義されている Expr= Sizzle.selectors = {} を確認してください。私たちがよく知っているセレクターはすべてここにあり、各セレクターはメソッド定義に対応しています。この時点で、Sizzle が実際にセレクターで「単語の分割」を実行し、それを分割してから、対応するメソッドを Expr から見つけて特定のクエリまたはフィルター操作を実行するかどうかを考慮する必要があります。

答えは基本的に「はい」です。しかし、シズル氏はより具体的で賢いアプローチをとっています。私が非常に核心だと思う 2 番目のメソッドを見てみましょう:

ソース コードの 1293 行目に matcherFromTokens 関数があります:

コードをコピーします コードは次のとおりです:

function matcherFromTokens(tokens) { }

渡されるパラメータは、tokenize メソッドから取得されます。 Matcher は、文字通り「マッチング プログラム」として理解できます。この機能の機能は、トークンを介してマッチング プログラムを生成することです。実際、そうなのです。スペースの制限のため、この記事では私が理解している Sizzle の実装原則の一部のみを共有し、ソース コードは掲載しません。後で時間があるときに、より詳細なソース コード分析の記事をまとめるかもしれません。

matcherFromTokens メソッドは、前述の仮定を裏付けています。これは、セレクター「単語分割」と Expr. で定義された照合メソッドの間の接続およびリンクとして機能します。セレクターのさまざまな順列と組み合わせが適用可能であると言えます。 Sizzle の賢いところは、得られた「単語分割」結果を Expr のメソッドと直接照合して 1 つずつ実行するのではなく、まず大きなマッチングメソッドをルールに従って組み合わせて実行することです。最後のステップで。ただし、結合後の実行方法は 3 番目のキーのメソッドに依存します:

ソース コードの 1350 行目に superMatcher メソッドがあります:

コードをコピーします コードは次のとおりです:

superMatcher = function( シード、コンテキスト、xml、結果、expandContext ) { }

このメソッドは直接定義されたメソッドではなく、行 1345 の matcherFromGroupMatchers(elementMatchers, setMatchers) メソッドを通じて返されますが、最終的な実行において重要な役割を果たします。

superMatcher メソッドは、シード、expandContext、およびコンテキストのパラメータに基づいて開始クエリ範囲を決定します。これは、シードから直接クエリおよびフィルタすることも、コンテキストまたはコンテキストの親ノード範囲内にあることもできます。シードから開始しない場合は、まずコード Expr.find["TAG"]( "*", ExpandContext && context.parentNode || context ) を実行し、elems コレクション (配列) を待ちます。次に、要素の走査を実行し、事前に生成されたマッチャー メソッドを使用して要素を 1 つずつ照合します。結果が true の場合、要素は返された結果セットに直接組み込まれます。

ここでの matcher メソッドの元の結果がすべて bool 値であることがわかりました。405 行目に戻り、Expr のフィルターに含まれるメソッドを見てみましょう。これらはすべて bool 値を返します。 PSEUDO(擬似クラス)に対応する擬似クラスメソッドをさらに追加することも同様です。私の当初のアイデアを少し覆すもののようですが、レイヤーごとにチェックを行っているわけではありませんが、上に戻ってマッチングとフィルタリングを行うようなものです。 Expr では、検索と preFilter のみがセットを返します。

結果セットを取得するために 1 つずつのマッチングとフィルタリングの方法を使用する理由についてはまだ疑問がありますが、Sizzle の最も基本的な「コンパイル原理」は明確に説明されるべきだったと思います。

しかし、疑問を残すことはできないので、続けましょう。実はこの記事自体が逆に書かれているように思えます。ソース コードを見ることに興味がある学生は、最初はこれら 3 つの主要なメソッドを目にすることはありません。実際、Sizzle は、これら 3 つのメソッドを入力する前に、他の一連のタスクも実行します。

Sizzle への本当の入り口は、ソース コードの 220 行目であると言えます:

コードをコピーします コードは次のとおりです:

function Sizzle( セレクター、コンテキスト、結果、シード ){ }

このメソッドの最初の段落は比較的理解しやすいものです。セレクターが単一の ID セレクター (#id) に一致する場合、要素は、context.getElementById(m) メソッドを使用して直接見つけることができます。 ID。セレクターを単一の TAG セレクターに一致させることができる場合は、まず context.getElementsByTagName(selector) メソッドを使用して関連する要素を見つけます。現在のブラウザーがネイティブの getElementsByClassName のみを使用し、一致するセレクターが単一の CLASS セレクターである場合は、関連する要素を見つけるために context.getElementsByClassName(m) メソッドも使用されます。これら 3 つのメソッドはすべてブラウザでサポートされているネイティブ メソッドであり、実行効率は間違いなく最も高くなります。

最も基本的なメソッドが利用できない場合は、選択メソッドを入力します。ソースコードの 1480 行目には次の定義があります:

コードをコピーします コードは次のとおりです:

function select( セレクター、コンテキスト、結果、シード、xml ) { }

select メソッドでは、最初に前述した「単語の分割」操作がセレクターに対して実行されます。ただし、この操作の後、照合メソッドの組み立てを直接開始するのではなく、最初にいくつかの検索操作を実行しました。ここでの検索操作は、Expr の検索に対応します。クエリ操作を実行し、結果セットを返します。

select は「単語分割」で得られたセレクターを利用して、まずその種類に応じて find メソッドで検索できる結果セットを探し出すということが分かります。検索操作を実行すると、結果セットはセレクターの順序で左から右に絞り込まれます。トラバーサル後、セレクター内のすべてのセレクターが検索操作を実行できる場合は、結果が直接返されます。それ以外の場合は、前述した「コンパイル」およびフィルタリングのプロセスに入ります。

この時点で、Sizzle のワークフローを基本的に理解することもできます。逆マッチングフィルタリングを実行するとき、その検索範囲はすでにレイヤーごとにフィルターされた最小のセットであるため、上に残った質問は実際にはこの時点ではもう質問ではありません。逆マッチング フィルタリング方法は、擬似クラスなど、対応するセレクタにとって実際には効率的な選択肢です。

Sizzle がなぜ非常に効率的であるかを簡単にまとめてみましょう。

まず、処理の流れとしては、常に最も効率の良いネイティブメソッドを最初に使用して処理します。これまで紹介したのは Sizzle 独自のセレクタ実装方法だけですが、実際に Sizzle を実行すると、まず現在のブラウザが querySelectorAll ネイティブ メソッドをサポートしているかどうかが判断されます (ソース コード 1545 行目)。サポートされている場合、このメソッドが最初に使用されます。ブラウザーによってネイティブにサポートされているメソッドは、Sizzle 独自の JS で記述されたメソッドよりも確実に効率的です。これを最初に使用すると、Sizzle の作業効率も向上します。 (querySelectorAll についての詳細はオンラインで確認できます)。 querySelectorAll メソッドがサポートされていない場合、Sizzle は、問題を解決するために getElementById、getElementsByTag、getElementsByClassName などのメソッドを直接使用できるかどうかの判断も優先します。

第 2 に は、比較的複雑な状況では、Sizzle は常に、最初にネイティブ メソッドを使用してクエリを実行し、可能な限り選択範囲を絞り込み、次に前に紹介した「コンパイル原則」を使用して選択範囲を絞り込むことを選択します。選択範囲の要素が 1 つずつ照合され、フィルタリングされます。 「コンパイル」ステップへのワークフローは少し複雑で、以前の方法よりも効率は確実にわずかに低くなりますが、Sizzle はこれらの方法をできるだけ使用しないように努めており、同時に、より高い効率を得るために、これらのメソッドで処理される結果セットを可能な限り小さく、単純にします。

繰り返しますが、 は、この「コンパイル」プロセスに入った後でも、Sizzle は一時的に無視し、最初にプロセスを説明するために導入しなかったキャッシュ メカニズムも実装しました。ソース コードの 1535 行目は「コンパイル」エントリと呼ばれるもので、3 番目のコア メソッド superMatcher を呼び出します。トレースして、1466 行目を参照してください。compile メソッドは、セレクターに基づいて生成された一致関数をキャッシュします。それだけではなく、1052 行目の tokenize メソッドを見てください。実際には、セレクターに基づいて単語の分割結果がキャッシュされます。つまり、Sizzle (セレクター) メソッドを一度実行し、次回から Sizzle (セレクター) メソッドを直接呼び出すと、その内部で最もパフォーマンスを消費する「コンパイル」プロセスはあまりパフォーマンスを消費せず、以前のキャッシュがパフォーマンスを消費します。メソッドを直接取得するだけで済みます。いわゆる「コンパイル」の最大の利点の 1 つは、キャッシュが容易になることかもしれないと考えています。ここでのいわゆる「コンパイル」は、前処理された関数を生成し、後で使用するために保存することとしても理解できます。

この時点で、これまでセレクターの実装原理と実行効率について懸念していた学生の質問に基本的に答えることができれば幸いです。さらに、この記事の分析結果は、Sizzle の最新バージョンのソース コードに基づいています。この記事で言及されているコード行番号は、http: からダウンロードできるこのバージョンのソース コードに基づいています。 //sizzlejs.com/。時間が限られているため、分析に不備がある場合はご容赦ください。まだ質問がある学生は、オフラインでコミュニケーションを続けてください。

以上がこの記事の全内容です。皆さんに気に入っていただければ幸いです。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート