剖析源码有利于帮忙了解数据流动,并且学习到高级的编程技巧。

首次应用

一个可运行案例

Peact

本文以 Peact 做为库的名称。

// 接口命名const Peact = {    /**     * 发明 Peact element     * @param {*} type (String) dom 类型     * @param {*} props (Object) Peact element 属性     * @param {*} children (Peact element or Dom or Basic type) 子节点     */    createElement(type, props, children){},    /**     * 发明 Peact class     * @param {*} sepc Peact class 申明     */    createClass( sepc ){}}const PeactDom = {    /* 渲染函数      * @param {*} element Peact class 或 Peact element      * @param {*} container 容器DOM节点     */    render(element, container){}}

实现 render 和 createElement

Peact 以实现两种组件类型,PeactElement 和 PeactClass 。
PeactElement 的定义:

// es6 class 实现class PeactDOMComponent {    constructor(element /* Peact element */){        this._currentElement = element    }    // 装置DOM节点    mountComponent(container){        // create HTML dom        const domElement = document.createElement(this._currentElement.type);        // 显式体现后果        const children = this._currentElement.props.children;        const props = this._currentElement.props        if( typeof children === "string" ){            const textNode = document.createTextNode(children);            domElement.appendChild(textNode);        }        container.appendChild(domElement);        return domElement;    }}// 或其余实现function PeactDOMComponent(element) {    this._currentElement = element}PeactDOMComponent.prototype.mountComponent = function(container){    // create HTML dom    const domElement = document.createElement(this._currentElement.type);    // 显式体现后果    const children = this._currentElement.props.children;    const props = this._currentElement.props    if( typeof children === "string" ){        const textNode = document.createTextNode(children);        domElement.appendChild(textNode);    }    container.appendChild(domElement);    return domElement;}

ps.当前均已 class 实现

createElement 的简略实现:

/* create a Peact element */function createElement (type, props, children){    const element = {        type,        props: props || {}    };    if (children) {       element.props.children = children;    }    return element;}

render 的简略实现:

function render(element /* Peact class or Peact element */, container){    const componentInstance = new PeactDOMComponent(element);    return componentInstance.mountComponent(container);}

实际

// create a Peact elementlet MyDiv = Peact.createElement("div", null, "this is a div")Peact.render(    MyDiv,    document.body)

实现 createClass

createClass 的简略实现:

function createClass( sepc ){    // create a Peact class    function ElementClassConstructor(props){        this.props = props        //    }    // render 为必须函数    if( !sepc.render ){        console.error("Required render function!")        return {};    }    ElementClassConstructor.prototype.render = sepc.render    return ElementClassConstructor}

此时如果用 createClass 创立 PeactClass 是不能间接用 Peact.render,应将PeactClass 转化成 PeactElement再利用 createClass 中配置 render 返回 PeactElement 理论进行绘制。

// create a Peact class & return Peact elementconst MyPeactClass = Peact.createClass({    render(){        this.props = { msg: "Hi!" }        return Peact.createElement('h1', null, this.props.msg );    }})Peact.render(    MyPeactClass,    document.body)

将PeactClass 转化成 PeactElement

class PeactCompositeComponentWrapper {    constructor(element) {        this._currentElement = element;    }    // 装置 component    mountComponent(container) {        const Component = this._currentElement;    if(typeof Component === 'function') {        // render 为 Peact class 申明时的 render        element = (new Component()).render();    }        // 确保此处的 element 为 Peact element        const domComponentInstance = new PeactDOMComponent(element);        domComponentInstance.mountComponent(container);    }}

调整 render 对立接口

function render(element /* Peact class or Peact element */, container){    const componentInstance = new PeactCompositeComponentWrapper(element);    return componentInstance.mountComponent(container);}

是否还能改良?

  • 对 render 函数的第一个参数 element 做了封装,并批改了 PeactCompositeComponentWrapper 使其反对子节点

利用一个高阶函数或 class

const ComponentWrapper = function(props) {    this.props = props;};ComponentWrapper.prototype.render = function() {    return this.props;};

调整 render

render(element /* Peact class or Peact element */, container){    // wrapperElement { type: ComponentWrapper, props: element, children: undefined }     const wrapperElement = this.createElement(ComponentWrapper, element);    const componentInstance = new PeactCompositeComponentWrapper(wrapperElement);    return componentInstance.mountComponent(container);}

调整 PeactCompositeComponentWrapper

class PeactCompositeComponentWrapper {    constructor(element) {        this._currentElement = element;    }    mountComponent(container) {        // this._currentElement { type: ComponentWrapper, props: element, children: undefined }        // Component 就是 ComponentWrapper 构造函数        const Component = this._currentElement.type;        // this._currentElement.props 就是 Peact.render() 第一个参数 element        const componentInstance = new Component(this._currentElement.props);        // 如果是 Peact element,则 element 是 Peact.render() 第一个参数,如果是 Peact class,element 就是 Peact.createClass 外部的构造函数 ElementClass        let element = componentInstance.render();        // 对 element 类型判断决定,如果是 Peact class 则element 是构造函数,如果是 Peact element,element 是string,        while (typeof element === 'function') {            // render 为 Peact class 申明时的 render            element = (new element(element.props)).render();        }        // 确保此处的 element 为 Peact element        const domComponentInstance = new PeactDOMComponent(element);        domComponentInstance.mountComponent(container);    }}

至此,一个繁难的Peact渲染模型搭建实现,延长局部还应包含对根底类型(number, string)和dom的反对

对根底类型(number, string)的反对

文本组件 PeactTextComponent

// Peact 文本组件 string or numberfunction PeactTextComponent (text) {    this._currentElement = '' + text;}PeactTextComponent.prototype.mountComponent = function(container){    const domElement = document.createElement("span");    domElement.innerHTML = this._currentElement    container.appendChild(domElement);    return domElement}

调整 render

function render(element /* Peact class or Peact element */, container){    // 类型判断    if( element.isPeactCreate && element.isPeactCreate() ){        // wrapperElement { type: ComponentWrapper, props: element, children: undefined }        const wrapperElement = this.createElement(ComponentWrapper, element);        const componentInstance = new PeactCompositeComponentWrapper(wrapperElement);        return componentInstance.mountComponent(container);    }    // DOM or basic    else if(typeof element === "string" || typeof element === "number"){        const componentInstance = new PeactTextComponent(element);            return componentInstance.mountComponent(container);    }}

isPeactCreate 是手动实现的Peact element 和 Peact class 判断,也能够疏忽。

// 类型判断:是否为 Peact 元素function isPeactCreate () {  const t = this._t_  if( t === "PeactElement" || t === "PeactClass" ) {    return true  }  return false;}

减少子节点遍历

调整 createElement

  createElement(type, config, children) {    /* create a Peact element */    function Element(type, key, props) {      this.type = type      this.key = key;      this.props = props;    }    let props = extend({}, config)    // 反对不定参数,并均合并至 children 中    var childrenLength = arguments.length - 2;    if (childrenLength === 1) {      props.children = Array.isArray(children) ? children : [children];    } else if (childrenLength > 1) {      var childArray = [];      for (var i = 0; i < childrenLength; i++) {        childArray[i] = arguments[i + 2];      }      props.children = childArray;    }    return new Element(type, null, props)  }

调整 PeactDOMComponent

class PeactDOMComponent {  constructor(element /* Peact element */ ) {    this._currentElement = element  }  mountComponent(nodeID) {    // create HTML dom     this._nodeID = nodeID    const children = this._currentElement.props.children;    const props = this._currentElement.props    const domElement = document.createElement(this._currentElement.type);    domElement.setAttribute("data-peactid", nodeID)    // 注册事件监听    if (props.onClick) {      domElement.onclick = props.onClick    }    children.forEach((child, key) => {      let childComponentInstance = PeactDOM.instantiatePeactComponent(child)      let childID = nodeID + "." + key      childComponentInstance._mountIndex = key;      let childDomElement = childComponentInstance.mountComponent(childID)      domElement.appendChild(childDomElement)    })    return domElement;  }}

最初残缺代码

const ComponentWrapper = function (props) {  this.props = props;};ComponentWrapper.prototype.render = function () {  return this.props;};function PeactDOMTextComponent(text) {  this._currentElement = '' + text;}PeactDOMTextComponent.prototype.mountComponent = function () {  const domElement = document.createElement("span");  domElement.innerHTML = this._currentElement  return domElement}class PeactDOMComponent {  constructor(element /* Peact element */ ) {    this._currentElement = element  }  mountComponent(nodeID) {    // create HTML dom     this._nodeID = nodeID    const children = this._currentElement.props.children;    const props = this._currentElement.props    const domElement = document.createElement(this._currentElement.type);    domElement.setAttribute("data-peactid", nodeID)    // 注册事件监听    if (props.onClick) {      domElement.onclick = props.onClick    }    children.forEach((child, key) => {      let childComponentInstance = PeactDOM.instantiatePeactComponent(child)      let childID = nodeID + "." + key      childComponentInstance._mountIndex = key;      let childDomElement = childComponentInstance.mountComponent(childID)      domElement.appendChild(childDomElement)    })    return domElement;  }}class PeactCompositeComponentWrapper {  constructor(element) {    this._currentElement = element;  }  mountComponent(container) {    const Component = this._currentElement.type;    const componentInstance = new Component(this._currentElement.props);    let element = componentInstance.render();    while (typeof element === 'function') {      element = (new element(element.props)).render();    }    const domComponentInstance = new PeactDOMComponent(element);    domComponentInstance.mountComponent(container);  }}// Peact 实现const Peact = {  createElement(type, config, children) {    /* create a Peact element */    function Element(type, key, props) {      this.type = type      this.key = key;      this.props = props;    }    let props = extend({}, config)    var childrenLength = arguments.length - 2;    if (childrenLength === 1) {      props.children = Array.isArray(children) ? children : [children];    } else if (childrenLength > 1) {      var childArray = [];      for (var i = 0; i < childrenLength; i++) {        childArray[i] = arguments[i + 2];      }      props.children = childArray;    }    return new Element(type, null, props)  },  createClass(sepc) {    // create a Peact class    function ElementClassConstructor(props) {      this.props = props    }    if (!sepc.render) {      console.error("Required render function!")      return {};    }    if (Object.assign) {      ElementClassConstructor.prototype = Object.assign(ElementClassConstructor.prototype, sepc)    }    // extend 手动反对    else {      ElementClassConstructor.prototype = extend(ElementClassConstructor.prototype, sepc)    }    return ElementClassConstructor  },}const PeactDOM = {  instantiatePeactComponent(node) {    // 文本节点的状况    if (typeof node === "string" || typeof node === "number") {      return new PeactDOMTextComponent(node);    }    // 自定义的元素节点及原生DOM    const wrapperNode = Peact.createElement(ComponentWrapper, node);    return new PeactCompositeComponentWrapper(wrapperNode);  },  render(element /* Peact class or Peact element */ , container) {    const rootID = "peact"    const componentInstance = PeactDOM.instantiatePeactComponent(element)    const component =  componentInstance.mountComponent(rootID);    container.appendChild(component);  }}

实际

    function notThis() {      console.log(this)    }    // create a Peact class    const MyPeactClass = Peact.createClass({      test() {        console.log("this is test")        console.log('this is THIS pointer: ', this)        console.log('this is PROPS: ', this.props)        console.log('this is STATE: ', this.state)      },      say: notThis,      render() {        this.props = {          msg: "Hi!"        }        // this.test()        // this.say()        return Peact.createElement('h1', {          onClick: function () {            alert("Hi!")          }        }, this.props.msg);      }    })    PeactDOM.render(      MyPeactClass,      document.body    )    // create a Peact element    let MyDiv = Peact.createElement("div", null, "this is a div")    PeactDOM.render(      MyDiv,      document.body    )    // just render    PeactDOM.render("this is text", document.body)        // create a Peact element    let MyH3 = Peact.createElement(      "div", null,       Peact.createElement("h3", null, "this is inner child!")    )    PeactDOM.render(      MyH3,      document.body    )