ソース コードを読む際の難点の 1 つは、バックボーンを整理しないというジレンマに陥ることです。このシリーズの記事では、React フレームワーク (JSX/仮想 DOM/コンポーネント/...) のバックボーン コンテンツを整理します。 ) (x)react.を実装しながら
前の記事 JSX と仮想 DOM では、インターフェースへの JSX レンダリングのプロセスを説明し、対応するコードを実装しました。コード呼び出しは次のとおりです。
このセクションでは、コンポーネントがインターフェイス プロセスにどのようにレンダリングされるかを引き続き調査していきます。ここでは、コンポーネントの概念を紹介します。コンポーネントは本質的に関数です
。以下は標準コンポーネント コードです:
import React from 'react' import ReactDOM from 'react-dom' const element = ( <p> hello<span>world!</span> </p> ) ReactDOM.render( element, document.getElementById('root') )
组件本质上就是一个函数
,如下就是一段标准组件代码:import React from 'react' // 写法 1: class A { render() { return <p>I'm componentA</p> } } // 写法 2:无状态组件 const A = () => <p>I'm componentA</p> ReactDOM.render(<a></a>, document.body)
<a name="componentA"></a>
是 JSX 的写法,和上一篇同理,babel 将其转化为 React.createElement() 的形式,转化结果如下所示:
React.createElement(A, null)
可以看到当 JSX 中是自定义组件的时候,createElement 后接的第一个参数变为了函数,在 repl 打印 <a name="componentA"></a>
,结果如下:
{ attributes: undefined, children: [], key: undefined, nodeName: ƒ A() }
注意这时返回的 Virtual DOM 中的 nodeName 也变为了函数。根据这些线索,我们对之前的 render
函数进行改造。
function render(vdom, container) { if (_.isFunction(vdom.nodeName)) { // 如果 JSX 中是自定义组件 let component, returnVdom if (vdom.nodeName.prototype.render) { component = new vdom.nodeName() returnVdom = component.render() } else { returnVdom = vdom.nodeName() // 针对无状态组件:const A = () => <p>I'm componentsA</p> } render(returnVdom, container) return } }
至此,我们完成了对组件的处理逻辑。
在上个小节组件 A 中,是没有引入任何属性和状态的,我们希望组件间能进行属性的传递(props)以及组件内能进行状态的记录(state)。
import React, { Component } from 'react' class A extends Component { render() { return <p>I'm {this.props.name}</p> } } ReactDOM.render(<a></a>, document.body)
在上面这段代码中,看到 A 函数继承自 Component。我们来构造这个父类 Component,并在其添加 state、props、setState 等属性方法,从而让子类继承到它们。
function Component(props) { this.props = props this.state = this.state || {} }
首先,我们将组件外的 props 传进组件内,修改 render 函数中以下代码:
function render(vdom, container) { if (_.isFunction(vdom.nodeName)) { let component, returnVdom if (vdom.nodeName.prototype.render) { component = new vdom.nodeName(vdom.attributes) // 将组件外的 props 传进组件内 returnVdom = component.render() } else { returnVdom = vdom.nodeName(vdom.attributes) // 处理无状态组件:const A = (props) => <p>I'm {props.name}</p> } ... } ... }
实现完组件间 props 的传递后,再来聊聊 state,在 react 中是通过 setState 来完成组件状态的改变的,后续章节会对这个 api(异步)深入探究,这里简单实现如下:
function Component(props) { this.props = props this.state = this.state || {} } Component.prototype.setState = function() { this.state = Object.assign({}, this.state, updateObj) // 这里简单实现,后续篇章会深入探究 const returnVdom = this.render() // 重新渲染 document.getElementById('root').innerHTML = null render(returnVdom, document.getElementById('root')) }
此时虽然已经实现了 setState 的功能,但是 document.getElementById('root')
<a name="componentA"></a> > 前回の記事と同様に、babel で React.createElement() の形式に変換すると以下のようになります。 JSX、createElement の後に最初のパラメータが関数になり、repl で
<a name="componentA"></a>
を出力すると、結果は次のようになります。
Component.prototype.setState = function(updateObj) { this.state = Object.assign({}, this.state, updateObj) _render(this) // 重新渲染 }
返された仮想 DOM も機能するように変更されました。これらの手がかりに基づいて、前の render
関数を変換しました。
function render(vdom, container) { let component if (_.isFunction(vdom.nodeName)) { if (vdom.nodeName.prototype.render) { component = new vdom.nodeName(vdom.attributes) } else { component = vdom.nodeName(vdom.attributes) // 处理无状态组件:const A = (props) => <p>I'm {props.name}</p> } } component ? _render(component, container) : _render(vdom, container) }
この時点で、コンポーネントの処理ロジックが完成しました。
プロパティと状態の実装
前のセクションのコンポーネント A では、プロパティと状態が導入されませんでした。コンポーネント (プロパティ) 間でプロパティを転送でき、コンポーネント (状態) 内で状態を記録できることを期待しています。
function _render(component, container) { const vdom = component.render ? component.render() : component if (_.isString(vdom) || _.isNumber(vdom)) { container.innerText = container.innerText + vdom return } const dom = document.createElement(vdom.nodeName) for (let attr in vdom.attributes) { setAttribute(dom, attr, vdom.attributes[attr]) } vdom.children.forEach(vdomChild => render(vdomChild, dom)) if (component.container) { // 注意:调用 setState 方法时是进入这段逻辑,从而实现我们将 dom 的逻辑与 setState 函数分离的目标;知识点: new 出来的同一个实例 component.container.innerHTML = null component.container.appendChild(dom) return } component.container = container container.appendChild(dom) }
上記のコードでは、A 関数が Component から継承していることがわかります。この親クラス Component を構築し、それに state、props、setState、その他の属性メソッドを追加して、サブクラスがそれらを継承できるようにしましょう。
class A extends Component { constructor(props) { super(props) this.state = { count: 1 } } click() { this.setState({ count: ++this.state.count }) } render() { return ( <p> <button>Click Me!</button> </p><p>{this.props.name}:{this.state.count}</p> ) } } ReactDOM.render( <a></a>, document.getElementById('root') )
rrreee
コンポーネント間での props の転送が完了したら、react では setState を使用して状態について説明します。コンポーネントの状態を完了します。変更については、後続の章でこの API (非同期) について詳しく説明します。ここでの簡単な実装は次のとおりです。document.getElementById('root' )
setState にノードを書き込むのは明らかにそうではありません。_render 関数に関連する dom ノードを転送します: rrreee
当然、それに関連する render 関数を再構築します:rrreee
render 関数 この関数の目的は、setState 関数で _render ロジックを呼び出せるようにすることです。完全な _render 関数は次のとおりです:rrreee
次のユースケースを使用して、書かれた React を実行してみましょう!rrreee
レンダリングは次のとおりです: コンポーネントは関数です。JSX がカスタムコンポーネントの場合、Babel 変換後の React.createElement(fn, ..) の最初のパラメーターは関数になります。また、他のロジックや HTML 要素についても同様です。 JSX; 🎜🎜 さらに、state/props/setState などの API を親クラス React.Component にカプセル化して、これらのプロパティとメソッドをサブクラスで呼び出すことができるようにしました。 🎜🎜上記がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。 🎜🎜関連する推奨事項: 🎜🎜🎜 vue の config/index.js の詳細な説明: 構成🎜🎜🎜🎜 Angular テンプレート構文の説明🎜🎜🎜以上がReact コンポーネントと state|props の解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。