到目前为止MyReact还没解决的问题
- 只有一个全局“组件”
- 每次state改变,都要手动调用MyReact.render函数,触发完整虚拟dom的对比
组件基类-MyReact.Component解决上述两个问题
- 通过组件基类可以创建组件实例
- 组件实例内部维护自己的props和state,每次state改变会自动触发此组件对应的虚拟dom的对比,也就意味着状态不变其他组件不受影响
组件基类的构造函数为组件实例注入props和初始化state,它还应该有setState方法来改变state,并且自动触发对比函数。
//组件基类的定义
import { reconcile } from './render'
class Component{
constructor(props){
this.props = props
this.state = this.state || {}
}
setState(partitalState){
this.state = Object.assign({}, this.state, partitalState)
updateInstance(this.__internalInstance)
} }
//由于组件状态改变触发的组件更新
function updateInstance(internalInstance){
const element = internalInstance.element
const parentDom = internalInstance.dom.parentNode
reconcile(element, internalInstance, parentDom)
}
export default Component
有两种element
- dom element:对应原生html元素,type的值是字符串,如"div",“TEXT ELEMENT”,对应dom实例
- component element:对应自定义组件元素,如<MyComponent>,type的值是构造函数,对应组件实例
有三种和组件相关的实例:
- 外层实例-wrapperInstance:构造函数返回的对象,保存了props,state和对组件实例的引用-__internalInstance
- 内部实例-childInstance:挂在外层实例的render函数定义的实例对象,有且只有一个
- 组件实例-instance:{ dom, element, childInstance, wrapperInstance }
function instantiate(element){ const { type, props} = element if(typeof type == 'string'){ const isTextElement = type === TEXT_ELEMENT //创建节点 const dom = isTextElement ? document.createTextNode(''): document.createElement(type) //更新属性和事件 updateDomProperties(dom, [], props); //添加子实例 const children = props.children const childInstances = [] children.forEach(child => { const childInstance = instantiate(child) dom.appendChild(childInstance.dom) childInstances.push(childInstance) }) //返回element对应的实例 return { element, dom, childInstances } }else{ const instance = {} const wrapperInstance = createWrapperInstance(element, instance) const childElement = wrapperInstance.render() const childInstance = instantiate(childElement) const dom = childInstance.dom // instance = { dom, element, childInstance, wrapperInstance } Object.assign(instance, { dom, element, childInstance, wrapperInstance }) return instance }}