翻译自:https://engineering.hexacta.c...上一节的代码有一些问题:每次更新都会带来整个虚拟DOM树的一致性检查;国家是全球性的(没有私有国家);发生更改后必须手动调用render方法以反映页面上的更改。组件可以帮助我们解决以上问题,同时带来一些新特性:允许自定义JSX标签名生命周期钩子(本节暂不介绍这部分内容)。首先,我们需要定义一个Component基类。创建后所有其他组件都必须继承自该类。我们需要一个带有props输入参数和setState方法的构造函数。setState方法可以接收partialState作为输入参数来更新组件状态:classComponent{constructor(props){this.props=props;this.state=this.state||{}}setState(partialState){this.state=Object.assign({},this.state,partialState);}}我们在创建组件的时候会继承上面的类。组件的使用方法和div、span等原生标签一样,就像这个。而我们的createElement不需要修改,元素的type属性直接设置为组件类即可,其余props属性不需要特殊处理。我们需要一个方法,可以根据传入的元素创建组件的实例(称为公共实例,实际上是基于这个构造函数new的对象)。函数createPublicInstance(element,internalInstance){const{type,props}=element;constpublicInstance=新类型(道具);//这里的类型对应组件的构造函数publicInstance.__internalInstance=internalInstance;returnpublicInstance;}组件的内部实例包含了组件对应的dom元素(内部实例就是我们前面章节提到的实例,是通过调用instantiate方法生成的)。公共实例和内部实例之间的引用关系将被保存。通过这个引用关系,可以找到公共实例对应的内部实例和虚拟DOM。当公共实例的状态发生变化时,我们只能更新变化的内部实例及其对应的那部分虚拟DOM:classComponent{constructor(props){this.props=props;this.state=this.state||{}}setState(partialState){this.state=Object.assign({},this.state,partialState);updateInstance(this.__internalInstance);}}functionupdateInstance(internalInstance){constparentDom=internalInstance.dom.parentNode;constelement=internalInstance.element;调和(parentDom,internalInstance,元素);一些改造。对于组件,我们需要先创建一个公共实例(先新建一个组件),然后调用组件的render方法获取组件内部元素,最后将获取到的元素传递给instantiate方法。函数实例化(元素){常量{类型,道具}=元素;constisDomElement=typeoftype==='string';if(isDomElement){//如果是原生dom元素,直接创建实例constisTextElement=type===TEXT_ELEMENT;constdom=isTextElement?document.createTextNode(''):document.createElement(type);updateDomProperties(dom,[],props);常量childElements=props.children||[];constchildInstances=childElements。地图(实例化);constchildDoms=childInstances.map(childInstance=>childInstance.dom);childDoms.forEach(childDom=>dom.appendChild(childDom));constinstance={dom,element,childInstances};返回实例;}else{//否则先创建公共实例,然后调用instantiate方法创建内部实例constinstance={};//这里的元素是一个对象,其类型属性是一个构造函数constpublicInstance=createPublicInstance(element,instance);缺点tchildElement=publicInstance.render();constchildInstance=instantiate(childElement);constdom=childInstance.dom;Object.assign(instance,{dom,element,childInstance,publicInstance});返回实例;该实例与原生DOM元素对应的实例不同。组件内部实例只会有一个子元素,就是render方法返回的内容,而原生DOM元素可以包含多个子元素。因此,对于组件内部实例,它们将具有一个childInstance属性,而不是一个childInstances数组。另外,由于在执行一致性检查时需要调用组件的render方法,所以组件的内部实例会存储一个对公共实例的引用(反过来,公共实例也会存储对内部实例的引用)。接下来,我们来处理组件实例的一致性检查。因为组件内部实例只包含一个子元素(所有元素都有一个统一的父类),所以只需要更新public实例的props属性,执行render方法获取子元素,然后进行一致性验证.functionreconcile(parentDom,instance,element){if(instance==null){constnewInstance=instantiate(element);parentDom.appendChild(newInstance.dom);返回新实例;}elseif(element==null){parentDom.removeChild(instance.dom);返回空值;}elseif(instance.element.type!==element.type){constnewInstance=instantiate(element);parentDom.replaceChild(newInstance.dom,instance.dom);返回新实例;}elseif(typeofelement.type==='string'){updateDomProperties(instance.dom,instance.element,props,element.props);instance.childInstances=reconcileChildren(实例,元素);instance.element=元素;返回实例;}else{instance.publicInstance.props=element.props;//更新公共实例的propsconstchildElement=instance.publicInstance.render();//获取最新的子元素constoldChildInstance=instance.childInstance;constchildInstance=reconcile(parentDom,oldChildInstance,childElement);instance.dom=childInstance.dom;instance.childInstance=childInstance;instance.element=元素;返回实例;}}现在,我们的Didact.js可以支持这里的组件了你可以在线编辑代码看看效果。使用组件后,我们可以创建自定义的JSX标签,并且拥有组件的内部状态,当组件发生变化时,只会改变自己的部分dom内容。相关内容到此结束。