实现Component
上一章节中有讲到,当渲染的是一个类组件的时候,声明的组件都是继承Component组件的,或者PureComponent组件。
在react文件中新创建一个文件component.js文件,index.js文件中引入:
import Component from './component'
const React = {
createElement,
Component
}
function createElement(tag, attrs, ...children) {
return {
tag,
attrs,
children
}
}
export default React
Component,每一个继承于它的组件都可以直接使用this.setState()。所以这个方法应该就在Component组件中。并且状态的改变会阴气dom的重新渲染,所以这里应该调用了renderComponents函数,传递的是当前组件实例:
import { renderComponent } from '../react-dom'
class Component {
constructor(props = {}){
this.props = props;
this.state={}
}
setState(stateChange) {
Object.assign(this.state, stateChange);
// 更改参数以后需要重新渲染组件
renderComponent(this)
}
}
export default Component;
测试一下:
import React from './react'
import ReactDOM from './react-dom'
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1
}
}
handlerClick() {
this.setSate({
num: this.state.num + 1,
})
}
render() {
return (
<div className='active'>
{this.state.num}
<button key='9' onClick={this.handlerClick.bind(this)}>改变状态</button>
</div>
)
}
}
ReactDOM.render(<Home title='home' />, document.getElementById('app'));
点击按钮页面会发生改变。
这里只是实现了状态的改变--视图的更新,但是并没有实现生命周期。
实现生命周期函数
- componentWillMount 将要加载,还么有生成虚拟dom
- componentDidMount 已经加载,生成虚拟的dom
- componentWillReceiveProps 将要接收新的参数
- componentWillUpdate 将要更新
- componentDidUpdate 已经更新
- componentWillUnmount 将要加载
componentWillMount和componentWillReceiveProps
componentWillMount,dom元素生成前调用,所以应该在renderComponent之前去调用
componentWillReceiveProps组件props改变,引起了重新渲染,但是还没有开始重新渲染
所以这两个周期函数会在setComeponentProps中调用。
判断有没有生成dom节点,也就是看看有没有base属性,如果已经生成,说明不是第一次渲染,是状态的更新引起的渲染,就调用componentWillReceiveProps;如果还没有生成,说明是首次加载,就调用componentWillMount周期函数。
function setComeponentProps(comp, props) {
if(!comp.base && comp.componentWillMount ) comp.componentWillMount();
else if(comp.componentWillReceiveProps) comp.componentWillReceiveProps();
// 设置组件的props
comp.props = props;
renderComponent(comp)
}
componentDidMount
在渲染组件的阶段,如果还没有真实的dom节点,就说明是第一次渲染,可以调用componentDidMount
componentWillUpdate
如果base属性存在,说明是重新渲染,调用,componentWillUpdate
componentDidUpdate
然后调用_render函数,渲染真是的dom节点,这里应该是渲染和更新,会在下一章节中讲更新。
在渲染组件的过程中,判断一下,base如果存在,并且父节点也存在,说明已经渲染过,这里就需要替换掉原来的节点。
渲染结束后,base存在,就可以调用componentDidUpdate函数
export function renderComponent(comp) {
// v虚拟的dom对象
const v = comp.render();
console.log(v)
const base = _render(v);
if(comp.base && comp.componentWillUpdate) comp.componentWillUpdate();
if(comp.base && comp.componentDidUpdate) comp.componentDidUpdate();
else if (comp.componentDidMount) comp.componentDidMount();
if(comp.base && comp.base.parentNode) {
comp.base.parentNode.replaceChild(base, comp.base)
}
// 生成真实的dom
comp.base = base;
}
测试一下生命周期的调用:
import React from './react'
import ReactDOM from './react-dom'
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1
}
}
componentWillMount() {
console.log('组件将要加载')
}
componentDidMount() {
console.log('组件加载完成')
}
componentWillReceiveProps() {
console.log('组件将要接受新的参数')
}
componentWillUpdate() {
console.log('组件将要更新')
}
componentDidUpdate() {
console.log('组件更新完成')
}
componentWillUnmount() {
console.log('组件卸载')
}
handlerClick() {
this.setSate({
num: this.state.num + 1,
})
}
render() {
return (
<div className='active'>
{this.state.num}
<button key='9' onClick={this.handlerClick.bind(this)}>改变状态</button>
</div>
)
}
}
ReactDOM.render(<Home title='home'/>, document.getElementById('app'));
第一次渲染:
点击按钮: