在之前的认知基础上,对react的功能进行多一步的了解。上一步是对react的JSX的渲染功能的实现,但这只是基础,但是react的意义在于他的组件化,接下来就是聊一下我对react组件化的认知
组件
React定义组件的方式可以分为两种:函数和类,函数定义可以看做是类定义的一种简单形式。
createElement的变化
在上一篇中,我们对React.createElement
的实现 已经完成了:
function createElement( tag, attrs, ...children ) {
return {
tag,
attrs,
children
}
}
在前面我只是用来渲染原生的dom,对于组件来说createElement得到的参数是不同的:如果JSX片段的元素有组件在的话,那么createElement的第一个参数就是一个方法了,不会是字符串了。
坦白来说,我有个组件<HelloWorld name="Hello" />,那么他的第一个参数就会是我定义的HelloWorld的方法了
function HelloWorld( props ) {
return <h1>Hello, {props.name}</h1>;
}
其实这里只是告诉我们区别就是,如果有组件的在的话需要渲染,则tag会是一个函数而已
React.Component 组件类
上面说了通过函数的方法,这边讲一下类的方法:Reac.Component,这里看文档说是有一个继承React.Component
class HelloWorld extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
要搞懂如何继承, 首先先来实现这个类
Component
React.Component包含了一些预先定义好的变量和方法,我们来一步一步地实现它:
先定义一个Component
类:
class Component {}
state& props
通过继承React.Component定义的组件 他是会有自己的私有状态State(感觉这里跟vue的有点像了,本人是在从事vue开发工作),根据this.state获取到这个私有状态State之后,同时也能同通过this.props来传入数据
class Component {
constructor( props = {} ) {
this.state = {};
this.props = props;
}
}
在这点看上去Vue其实跟React已经非常接近甚至相似了,
SetState
组件内部的state的渲染结果相关,当state改变时通常会触发渲染,为了让React知道了改变了私有状态State,我们要通过setState方法去修改,这里我讲一句跟vue 的大差不差vue也是用这种方法进行的,不过他们更为明显的用了getter 和setter去修改和劫持相关的属性。
回到正题这里用Object.assign来做一个实现 在每次更新state后,我们会需要调用renderComponent来渲染组件的
import { renderComponent } from '../react-dom/render'
class Component {
constructor( props = {} ) {
// ...
}
setState( stateChange ) {
// 将修改合并到state
Object.assign( this.state, stateChange );
renderComponent( this );
}
}
render
在上一篇已经实现了reander的封装与实现,他只实现了渲染原生的DOM,现在我们需要进行更改让他支持渲染组件。
在这前面加多一段话
unction render( vnode, container ) {
return container.appendChild( _render( vnode ) );
}
function _render( vnode ) {
if ( vnode === undefined || vnode === null || typeof vnode === 'boolean' ) vnode = '';
if ( typeof vnode === 'number' ) vnode = String( vnode );
if ( typeof vnode === 'string' ) {
let textNode = document.createTextNode( vnode );
return textNode;
}
// 支持渲染组件
if ( typeof vnode.tag === 'function' ) {
const component = createComponent( vnode.tag, vnode.attrs );
setComponentProps( component, vnode.attrs );
return component.base;
}
const dom = document.createElement( vnode.tag );
if ( vnode.attrs ) {
Object.keys( vnode.attrs ).forEach( key => {
const value = vnode.attrs[ key ];
setAttribute( dom, key, value );
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
return dom;
}
组件渲染和生命周期
上面的方法中用到了createComponent
和setComponentProps
两个方法,组件的生命周期方法也会在这里面实现。
所谓生命周期方法大家都很熟了,也是一个关键点,他是一些在特殊时机执行的函数,例如componentDidMount方法会在组件挂载后执行
这里需要对createComponent来进行扩展修改了
// 创建组件
function createComponent( component, props ) {
let inst;
// 如果是类定义组件,则直接返回实例
if ( component.prototype && component.prototype.render ) {
inst = new component( props );
// 如果是函数定义组件,则将其扩展为类定义组件
} else {
inst = new Component( props );
inst.constructor = component;
inst.render = function() {
return this.constructor( props );
}
}
return inst;
}
setComponentProps
方法用来更新props
,在其中可以实现componentWillMount
,componentWillReceiveProps
两个生命周期方法(这里不太理解了,我个人原以为是在生命周期执行中经历这些事,但是现在看来似乎并不是这样,而是在执行中,调用了生命周期函数
// set props
function setComponentProps( component, props ) {
if ( !component.base ) {
if ( component.componentWillMount ) component.componentWillMount();
} else if ( component.componentWillReceiveProps ) {
component.componentWillReceiveProps( props );
}
component.props = props;
renderComponent( component );
}
renderComponent方法是用来渲染组件的,在上面的setState里会调用这个方法进行重新渲染,在这里可以实现几个生命周期的实现,componentWillUpdate
,componentDidUpdate
,componentDidMount这几个
export function renderComponent( component ) {
let base;
const renderer = component.render();
if ( component.base && component.componentWillUpdate ) {
component.componentWillUpdate();
}
base = _render( renderer );
if ( component.base ) {
if ( component.componentDidUpdate ) component.componentDidUpdate();
} else if ( component.componentDidMount ) {
component.componentDidMount();
}
if ( component.base && component.base.parentNode ) {
component.base.parentNode.replaceChild( base, component.base );
}
component.base = base;
base._component = component;
}
到此组件渲染已经完成了
在此感觉是从API层面来实现了react的功能,但是目前的是重新渲染整个组件,这很明显非常的损耗功能,减少dom操作才是框架该做的,等待后续更新把~!