blog

リアクトシリーズを手書き:リアクトとリアクトドムを実装する

現在、Reactは最も人気のあるフロントエンド開発フレームワークの一つであり、フロントエンドにおけるその重要性は誇張することはできません。今日からは、いくつかのページを使って簡単なReactフレームワ...

Nov 21, 2020 · 4 min. read
シェア

オープニング

現在、Reactはフロントエンド開発において最も人気のあるフレームワークの一つであり、フロントエンドにとって重要であることは自明です。今日からは数ページを使って簡単なReactフレームワークを実装し、Reactの基本的な仕組みを一緒に理解していきましょう。

React

Reactを使ったことがある子はReactのJSX構文を知っているはずですが、実はJSXはHTMLタグと同じような書き方で、パースした後、React.createElementの実際の実装は以下のようになります。

// jsx 
function render() {
 return <div>hello react</div>;
}
// 解析後、実際に実行されるのは
function render() {
 return React.createElement((type: 'div'), {}, 'hello react'); // jsオブジェクトを返す:{type: 'div',attrs: {},children: ['hello react']}
}

そこで、最初の関数であるReact.createElementのモデリングを始めます:

// Reactオブジェクトを作成し、後でそれをエクスポートする。
const React = {};
/*
 * createElement 
 * 3パラメータtype、attrs、childrenなどはすべて同じである。
 *
 */
React.createElement = function (type, attrs, ...children) {
 return {
 type,
 attrs,
 children,
 };
};
export default React;

ReactDOM

上記はReact.createElementの基本的なコアコードですが、次に別のオブジェクト--- ReactDOMを実装します:

// 後でエクスポートされるReactDOMオブジェクトを作成する。
const ReactDOM = {};
/*
 * render 
 * 2次のパラメータ:vnodeはcreateElementによって返されるオブジェクトであり、コンテナはdomノードである、例えば、ドキュメント.getELementById('app')
 *
 */
ReactDOM.render = function (vnode, container) {};
export default ReactDOM;

renderメソッドを定義し、具体的にはvnodeとcontainerというパラメータを分析し、vnodeはReact.createElementで返されるオブジェクト、containerはdomノードで、ReactDOM.renderメソッドを修正します:

// 後でエクスポートされるReactDOMオブジェクトを作成する。
const ReactDOM = {};
/*
 * render 
 * 2次のパラメータ:vnodeはcreateElementによって返されるオブジェクトであり、コンテナはdomノードである、例えば、ドキュメント.getELementById('app')
 *
 */
ReactDOM.render = function (vnode, container) {
 let element;
 // 入力されたvnodeが文字列であるかどうかを判断し、文字列である場合は、直接テキストノードを作成する。
 if (typeof vnode === 'string') {
 element = document.createTextNode(vnode);
 } else {
 // vnodeオブジェクトであれば、直接htmlノードを作成する。
 element = document.createElement(vnode.type);
 }
 // htmlにノード要素を挿入する
 container.appendChild(element);
};
export default ReactDOM;

この時点で、基本的なReactライブラリを実装することができます。

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Document</title>
 </head>
 <body>
 <div id="root"></div>
 </body>
 <!-- react.js ReactとReactDOMの両方のオブジェクトが含まれている。>
 <script src="./react.js"></script>
 <!-- index.js Reactのメソッドを導入し、実行する>
 <script src="./index.js"></script>
</html>

index.jsファイルを見てみましょう。

// JSXが解析され、React.createElement,vnodeオブジェクトを返す
const element = <div>a single react</div>;
// element実際に実行されるのは
// const element = React.createElement(type: 'div', {}, ['a single react'])
ReactDOM.render(element, document.getElementById('root'));

このhtmlファイルをブラウザで実行すると、a single reactという文字が表示されますが、これはReactが正常に実行されていることを意味します。次にindex.jsを変更します:

// JSXが解析され、React.createElement,vnodeオブジェクトを返す
// スパンの余分な層を入れ子にする
const element = (
 <div>
 <span>a single react</span>
 </div>
);
// element実際に実行されるのは
// const element = React.createElement(type: 'div', {}, [React.createElement(type: 'span',{}, ['a single react'])])
ReactDOM.render(element, document.getElementById('root'));

再帰的

ブラウザを更新すると、コンテンツがないことがわかりますが、これはなぜですか?index.jsを参照してください知っている必要があり、スパンの層よりも元の単一の反応に、そうReactDOM.renderでなければなりませんし、変更し、実行の再帰的な方法に変更すると、この問題を解決することができます:

// 後でエクスポートされるReactDOMオブジェクトを作成する。
const ReactDOM = {};
/*
 * render 
 * 2次のパラメータ:vnodeはcreateElementによって返されるオブジェクトであり、コンテナはdomノードである、例えば、ドキュメント.getELementById('app')
 *
 */
ReactDOM.render = function (vnode, container) {
 // 出口に渡されたコンテナがない場合は、フォールトトレラント判断のビットを行う
 if (!container) {
 return;
 }
 let element;
 // 入力されたvnodeが文字列であるかどうかを判断し、文字列である場合は、直接テキストノードを作成する。
 if (typeof vnode === 'string') {
 element = document.createTextNode(vnode);
 } else {
 // vnodeオブジェクトであれば、直接htmlノードを作成する。
 element = document.createElement(vnode.type);
 }
 // htmlにノード要素を挿入する
 container.appendChild(element);
 /*
 * 子があるかどうかを判断する
 * もしあれば、子を繰り返し、ReactDOMを再帰的に実行する。.render
 *
 */
 if (vnode.children && vnode.children.length) {
 vnode.children.forEach((child) => {
 /*
 * 各子で渡す
 * また、domノードとして現在の要素を渡す
 *
 */
 ReactDOM.render(child, element);
 });
 }
};
export default ReactDOM;
Read next

Vuexでコンポーネント間通信を実現するには?

Vuexは一方向のデータフローを実装しており、グローバルにはStateの保存データがあり、Stateのデータを変更するコンポーネントは、Mutationを介して実行する必要があり、Mutationは同時に、外部プラグインがStateデータの更新を取得するために呼び出すためのサブスクライバモデルを提供します。また、すべての非同期操作またはバッチ同期操作が必要な場合は、...

Nov 21, 2020 · 2 min read