Reactの背後にあるツーリングシステム

1.
Reactツールチェーンタグクラウドの概要


Rollup    Prettier    Closure Compiler
Yarn workspace    [x]Haste    [x]Gulp/Grunt+Browserify
ES Module    [x]CommonJS Module
Flow    Jest    ES Lint    React DevTools
Error Code System    HUBOT(GitHub Bot)    npm

[x]の付いたPSは、以前は使用されていたが、最近(React 16)は使用されていないことを示します。

簡単な分類は次のとおりです。


开发:ES Module, Flow, ES Lint, Prettier, Yarn workspace, HUBOT
构建:Rollup, Closure Compiler, Error Code System, React DevTools
测试:Jest, Prettier
发布:npm

ESモジュールメカニズムに従ってソースコードを整理し、タイプチェックとLint /フォーマットツールで補完し、Yarnを使用してモジュールの依存関係を処理し、HUBOTチェックPR、ロールアップ+クロージャーコンパイラの構築、エラーコードメカニズムを使用して本番環境でエラー追跡を実装し、DevTools側がバンドルチェックを支援します。 Jestは単一のテストを実行し、バンドルをフォーマットしてビルド結果が十分にクリーンであることも確認します。最後に、新しいパッケージがnpmを介してリリースされます。

プロセス全体はそれほど複雑ではありませんが、エラーコードシステム、二重保険環境(開発/製品環境の区別)、リリースプロセスのツールなど、いくつかの詳細が詳細に検討されています。

2.開発ツール


CommonJS Module + Haste -> ES Module

React 15より前のバージョンは、CommonJSモジュールで定義されています。次に例を示します。


var ReactChildren = require('ReactChildren');
module.exports = React;

ESモジュールに切り替える理由はいくつかあります。

モジュールのインポート/エクスポートの問題を早期に検出するのに役立ちます

CommonJS Moduleは、存在しないメソッドを必要とするのは簡単であり、呼び出しが報告されるまで問題を見つけることはできません。ESモジュールの静的モジュールメカニズムでは、インポートとエクスポートを名前で一致させる必要があります。一致しない場合、コンパイルおよびビルド時にエラーが報告されます。

バンドルサイズの利点

ES Moduleは、ツリーの揺れによってバンドルをよりクリーンにすることができます。基本的な理由は、module.exportsがオブジェクトレベルのエクスポートであり、exportがよりきめ細かいアトミックレベルのエクスポートをサポートしているためです。一方、名前による導入により、ロールアップなどのツールでモジュールをまとめてフラット化でき、圧縮ツールはこれに基づいてより激しい変数名の混乱を実行でき、バンドルサイズをさらに削減できます。

CommonJSモジュールはJestの一部の機能(resetModulesなど)に適しているため、ソースコードのみがESモジュールに切り替えられ、単一のテストケースは切り替えられません(ESモジュールに切り替えた場合でも、モジュール状態の分離が必要なシナリオではrequireを使用する必要があります。切り替えはほとんど重要ではありません)

Hasteに関しては、次のような長い相対パスの問題を解決するためにReactチームによってカスタマイズされたモジュール処理ツールです。


// ref: react-15.5.4
var ReactCurrentOwner = require('ReactCurrentOwner');
var warning = require('warning');
var canDefineProperty = require('canDefineProperty');
var hasOwnProperty = Object.prototype.hasOwnProperty;
var REACT_ELEMENT_TYPE = require('ReactElementSymbol');

Hasteモジュールメカニズムでのモジュール参照は、明確な相対パスを指定する必要はありませんが、プロジェクトレベルの一意のモジュール名で自動的に検索されます。次に例を示します。


// 声明
/**
 * @providesModule ReactClass
 */

// 引用
var ReactClass = require('ReactClass');

表面的には、ロングパス参照の問題を解決します(プロジェクト構造の深いネストの基本的な問題は解決しません)。非標準のモジュールメカニズムを使用することには、いくつかの一般的な欠点があります。

標準と矛盾しているため、標準エコロジーのツールにアクセスするときに適応の問題に直面します

ソースコードは読みにくく、モジュールの依存関係を理解するのは簡単ではありません

React 16は、カスタムモジュールメカニズムのほとんどを削除し(ReactNativeには小さな部分があります)、ノード標準の相対パス参照を使用します。長いパスの問題は、プロジェクト構造をリファクタリングすることで完全に解決され、フラットディレクトリ構造(同じパッケージの下)を使用します。最も深いレベル2の参照、クロスパッケージは、ヤーン処理後に最上位の絶対パスによって参照されます)

Flow + ES Lint
Flowは、タイプエラーをチェックし、次のようなタイプの不一致の潜在的な問題をできるだけ早く検出する役割を果たします。


export type ReactElement = {
  $$typeof: any,
  type: any,
  key: any,
  ref: any,
  props: any,
  _owner: any, // ReactInstance or ReactFiber

  // __DEV__
  _store: {
    validated: boolean,
  },
  _self: React$Element<any>,
  _shadowChildren: any,
  _source: Source,
};

静的な型の宣言とチェックに加えて、Flowの最大の機能はReactコンポーネントとJSXの深いサポートです。


type Props = {
  foo: number,
};
type State = {
  bar: number,
};
class MyComponent extends React.Component<Props, State> {
  state = {
    bar: 42,
  };

  render() {
    return this.props.foo + this.state.bar;
  }
}

PS FlowのReactサポートの詳細については、React inFlowのさらに優れたサポートを参照してください。

エクスポートタイプチェックのフロー「マジック」もあります。これは、モックモジュールのエクスポートタイプがソースモジュールと一致しているかどうかを確認するために使用されます。


type Check<_X, Y: _X, X: Y = _X> = null;
(null: Check<FeatureFlagsShimType, FeatureFlagsType>);
ES Lint负责检查语法错误及约定编码风格错误,例如:

rules: {
  'no-unused-expressions': ERROR,
  'no-unused-vars': [ERROR, {args: 'none'}],
  // React & JSX
  // Our transforms set this automatically
  'react/jsx-boolean-value': [ERROR, 'always'],
  'react/jsx-no-undef': ERROR,
}

Prettier
Prettierは、いくつかの目的で、コードを自動的にフォーマットするために使用されます。

古いコードを統一されたスタイルにフォーマットする

送信する前に、変更された部分をフォーマットします

継続的な統合と協力して、PRコードスタイルが完全に一貫していることを確認します(そうしないと、ビルドが失敗し、異なるスタイルのパーツが出力されます)

IDEに統合し、1日1回フォーマットします

ビルド結果をフォーマットすると、一方ではdevバンドルの読みやすさが向上し、prodバンドル内の冗長なコードを見つけるのにも役立ちます。

統一されたコードスタイルはもちろんコラボレーションに役立ちます。さらに、オープンソースプロジェクトでは、さまざまなスタイルのPRに直面することがよくあります。継続的な統合における必須リンクとしての厳密なフォーマットチェックに関しては、コードスタイルの違いの問題を完全に解決し、オープンソースを簡素化するのに役立ちます。仕事

PSプロジェクト全体の必須の統一フォーマットは少し極端に思えます、それは大胆な試みですが、効果は悪くないと言われています:


Our experience with Prettier has been fantastic, and we recommend it to any team that writes JavaScript.

Yarnワークスペース
Yarnのワークスペース機能は、monorepoのパッケージ依存関係(lernaブートストラップと同様)を解決し、node_modulesの下にソフトリンクを確立することによってノードモジュールメカニズムを「チート」するために使用されます。


Yarn Workspaces is a feature that allows users to install dependencies from multiple package.json files in subfolders of a single root package.json file, all in one go.

package.json / workspacesを使用してYarnワークスペースを構成します。


// ref: react-16.2.0/package.json
"workspaces": [
  "packages/*"
],

注:Yarnの実際の処理は、ソフトリンクを介して実装されるLernaの処理と似ていますが、パッケージマネージャーレベルでmonorepoパッケージサポートを提供する方が合理的です。特定の理由については、Yarnのワークスペース| Yarnブログを参照してください。

次に、糸を取り付けた後、パッケージ全体でそれを喜んで参照できます。


import {enableUserTimingAPI} from 'shared/ReactFeatureFlags';
import getComponentName from 'shared/getComponentName';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';

PSさらに、YarnとLernaはシームレスに組み合わせることができます。依存関係処理部分は、useWorkspacesオプションを介してYarnに渡されます。詳細については、Lernaとの統合を参照してください。

HUBOT
HUBOTは、GitHubロボットを指し、通常は次の目的で使用されます。

継続的な統合に接続し、PRはビルド/チェックをトリガーします

問題を管理し、非アクティブなディスカッション投稿をオフにします

バンドルサイズの継続的な最適化を促すために、バンドルサイズに対するPRの影響に対応するために、Reactチームプラン(まだ実行されていない)ロボットなど、主にPRとIssueに関するいくつかの自動化された処理を実行します。

現在、バンドルサイズの変更はビルドごとにファイルに出力され、変更はGit(送信済み)によって追跡されます(例:)。


// ref: react-16.2.0/scripts/rollup/results.json
{
  "bundleSizes": {
    "react.development.js (UMD_DEV)": {
      "size": 54742,
      "gzip": 14879
    },
    "react.production.min.js (UMD_PROD)": {
      "size": 6617,
      "gzip": 2819
    }
  }
}

欠点は想像できます。このjsonファイルは頻繁に競合します。競合をマージするためにエネルギーを浪費する必要があるか、この自動的に生成された厄介なファイルを送信してバージョンが遅れる必要がないため、GitHubBotを介してこの問題を取り除く予定です。

3.ビルドツール
バンドルフォームの
前に、2つのバンドルフォームが提供されていました。

外部依存関係として使用されるUMD単一ファイル

セルフビルドバンドルをサポートするために使用されるCJSバルクファイル(ソースコードの依存関係としてReactを使用)

いくつかの問題があります:

自己構築バージョンに一貫性がありません:異なるビルド環境/構成によって構築されたバンドルが異なります

バンドルのパフォーマンスを最適化する余地があります。アプリをパッケージ化してクラスライブラリを構築することは適切ではなく、パフォーマンスを改善する余地があります。

実験的な最適化の試みを助長しない:パッケージングや圧縮などの最適化方法をバルクファイルモジュールに適用することはできません

React16はバンドルフォームを調整しました。

CJSバルクファイルは提供されなくなりました。npmから得られるのは、構築され、統合され、最適化されたバンドルです。

Web環境とノード環境にそれぞれUMD単一ファイルとCJS単一ファイルを提供します(***)

分離できないクラスライブラリの姿勢では、バンドルフォームによってもたらされる制限を取り除くために、すべての最適化リンクが取り込まれます

。Gulp/ Grunt + Browserify-> Rollupの

以前のビルドシステムは、Gulp / Grunt + Browserifyによる一連のツールに基づいています。後で次のような拡張の観点からツールに限定されます。

ノード環境でのパフォーマンスの低下:頻繁なprocess.env.NODE_ENVアクセスは***パフォーマンスを低下させますが、Uglifyはこれに依存して不要なコードを削除するため、クラスライブラリの観点から解決する方法はありません。

したがって、React ***のパフォーマンスのベストプラクティスには、通常、「Reactを再パッケージ化し、ビルド時にprocess.env.NODE_ENVを削除する」があります(もちろん、React 16ではこれを行う必要はありません。理由は、上記のバンドルフォームの変更を参照してください)。

過度に複雑なカスタムビルドツールは破棄され、より適切なロールアップが使用されます。


It solves one problem well: how to combine multiple modules into a flat file with minimal junk code in between.

PS Haste-> ESModuleまたはGulp / Grunt + Browserify-> Rollupが非標準のカスタマイズされたソリューションから標準のオープンソリューションに切り替えられたかどうかにかかわらず、「手でこする」という側面から学ぶ必要があります。私たちのシナリオには当てはまりませんが、自分で作成する必要がありますか?

模擬モジュールの
構築は、動的な依存関係のシナリオに直面する可能性があります。バンドルが異なれば、機能は似ているが実装が異なるモジュールに依存します。たとえば、ReactNativeのエラー通知メカニズムは赤いボックスを表示することであり、Web環境はコンソールに出力されます。

2つの一般的な解決策があります。

実行時の動的依存関係(注入):両方をバンドルに入れ、実行時の構成または環境に応じて選択します

ビルド中の依存関係の処理:さらにいくつかのコピーをビルドします。異なるバンドルには、独自に必要な依存モジュールが含まれています

明らかに、ビルド時のプロセス、つまりモックモジュールはクリーンです。この開発の違いを気にする必要はありません。特定の依存関係は、構築中の環境に応じて自動的に選択されます。これは、単純なロールアッププラグイン(動的な依存関係の構成+ビルド中の依存関係の置換)を手書きすることで実現されます。

クロージャーコンパイラ
google / Closure -compilerは、3つの最適化モード(compilation_level)を備えた非常に強力なミニファイアです。

WHITESPACE_ONLY:コメント、余分な句読点、空白文字を削除します。論理的にはソースコードと同等です。

SIMPLE_OPTIMIZATIONS:デフォルトモード。WHITESPACE_ONLYに基づいて、変数名(ローカル変数と関数パラメーター)がさらに短縮され、ロジック関数は基本的に同等です。特殊なケース(eval( 'localVar')など)は、名前でローカル変数にアクセスし、fn.toString(を解析します。 ))例外

ADVANCED_OPTIMIZATIONS:SIMPLE_OPTIMIZATIONSに基づいて、より強力な名前変更(グローバル変数名、関数名、属性)を実行し、不要なコード(到達不能、不要)を削除し、インラインメソッド呼び出しと定数(費用効果が高い場合は関数を呼び出します)関数本体の内容を置き換え、定数をその値に置き換えます)

PScompile_levelの詳細については、ClosureCompilerのコンパイルレベルを参照してください。

ADVANCEDモードは強力すぎます:


// 输入
function hello(name) {
  alert('Hello, ' + name);
}
hello('New user');

// 输出
alert("Hello, New user");

PSはClosureCompilerServiceでオンラインで試すことができます

移行と切り替えには一定のリスクがあるため、Reactは引き続きSIMPLEモードを使用しますが、バンドルサイズを最適化するためにクロージャーコンパイラを最大限に活用するために、将来的にADVANCEDモードを開く計画があるかもしれません。


Error Code System
In order to make debugging in production easier, we’re introducing an Error Code System in 15.2.0. We developed a gulp script that collects all of our invariant error messages and folds them to a JSON file, and at build-time Babel uses the JSON to rewrite our invariant calls in production to reference the corresponding error IDs.

つまり、詳細なエラー情報は、prodバンドル内の対応するエラーコードに置き換えられます。本番環境では、ランタイムエラーをキャッチし、エラーコードとコンテキスト情報をスローしてから、エラーコード変換サービスにスローして完全なエラーメッセージを復元します。これにより、prodバンドルが可能な限りクリーンになるだけでなく、開発環境と同じ詳細なエラー報告機能が保持されます。

たとえば、実稼働環境での不正なReactElementエラー:


Minified React error #109; visit https://reactjs.org/docs/error-decoder.html?invariant=109&args[]=Foo for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

非常に興味深いテクニックで、開発体験の向上に多くのことを費やしました


いわゆるenvificationには、例えば、環境によって構築することです。


// ref: react-16.2.0/build/packages/react/index.js
if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

一般的に使用される方法で、process.env.NODE_ENVを、構築中にターゲット環境に対応する文字列定数に置き換えます。冗長なコードは、後続の構築プロセス(パッケージングツール/圧縮ツール)で削除されます。

パッケージエントリファイルに加えて、二重保険として内部でも同じ判断が行われます。


// ref: react-16.2.0/build/packages/react/cjs/react.development.js
if (process.env.NODE_ENV !== "production") {
  (function() {
    module.exports = react;
  })();
}

さらに、開発者が開発バンドルを悪用してオンラインにするのではないかと心配しているので、ReactDevToolsにリマインダーを追加しました。


This page is using the development build of React. 

おすすめ

転載: blog.51cto.com/15080030/2592712