react库的核心API

我们用react来编写组件的时候,都是以jsx的形式写编写视图层,除此之外,还可以通过createElement这个方法来编写组件,createElement是react库核心的API之一。

createElement

在没有MVVM框架之前,我们想改变页面的状态达到我们想要的结果,通常情况下,我们都是直接操作dom元素的时候,对性能的消耗是非常大的。后来有了虚拟dom(virtua lDom)这一概念。虚拟dom主要是一个对象,用来描述dom节点的状态。

我们假设这个对象几个字段

  • type: 用来描述dom是哪种类型,可以是原生标签,文本节点,函数组件或者是类组件
  • config: 用来描述dom原生上特有的属性
  • children: 用来描述该节点下的子节点

新建一个存储常量的文件const.js,定义一个变量TEXT用来表示文本节点,这里是为了简单方便而这么做

export const TEXT = "TEXT"
复制代码

先尝试最简版实现createElement,新建一个index.js

function createElement(type, config, ...children) {    
    if (config) {        
        delete config.__self,        
        delete config.__source    
    }    
    const props = {        
        ...config,        
        children: children.map(child =>         
            typeof child === 'object' ? child: createTextNode(child))
    }    
    return {        
        type,        
        props    
    }
}
复制代码

这里children为了简单方便,处理成数组,这里的文本节点需要进一步处理,定义一个函数

function createTextNode(text) {   
    return {     
    type: TEXT,     
    props: {       
            nodeValue: test,       
            children: []     
        }   
    }
}
复制代码

简版的createElement基本实现,这个API是核心API之一,在index.js文件下这导出

export default {  createElement }
复制代码

render

在我们写的react项目中,需要一个主入口文件,需要挂载我们的根节点,这个方法react-dom库帮我们实现,暴露的render函数接收两个参数,新建一个react-dom.js文件,编写核心的render。

我们都知道react的组件类型有函数组件和类组件,为了区别是哪种组件,我们需要定义一个方法来判断,创建一个文件Component.js来简单实现

function Component(props) {    
    this.props = props
}
Component.prototype.isReactComponent = {}
export default Component
复制代码

简略版

import { TEXT } from './const'
function render(vnode, container) {    
    const node = createNode(vnode)    
    container.appendChild(node)
}
复制代码

render方法接收两个参数,一个是react元素,另一个是挂载的容器。

createNode方法用来创建真实dom节点,通过判断type类型来判断,当type类型为函数的时间,需要判断是类组件还是函数组件,通过原型上是否有isReactComponent来判断是不是类组件。

function createNode(vnode) {    
    let node    
    const { type, props } = vnode    
    if (type === TEXT) {        
        node = document.createTextNode('')    
    } else if (typeof type === 'string') {        
        node = document.createElement(type)    
    } else if (typeof type === "function") {        
        node = type.prototype.isReactComponent  ? 
                updateClassComponent(vnode) : 
                updateFunctionComponent(vnode)    
    }    
    reconcieChildren(props.children, node)    
    updateNode(node, props)    
    return node
}
复制代码

当dom节点下面还有许多子节点,需要额外去处理,我们定义一个reconcileChildren方法,接收两个参数分别是当前的children和node节点,会自动去递归判断。

function reconcileChildren(children, node) {    
    for (let i = 0; i < children.length; i++) {        
        let child = children[i]        
        render(child, node)    
    }
}
复制代码

在创建节点的过程中,需要更节点,需要把当前node节点该有的props属性带上

function updateNode(node, nextVal) {    
    Object.keys(nextVal) 
        .filter(k => k !== 'children') 
        .forEach(k => { 
            node[k] = nextVal[k] 
        })
}
复制代码

函数组件和类组件分别由updateFunctionComponent和updateClassComponent这俩函数来处理,类组件上有render方法返回一个react元素,需要通过new实例化一个实例来调用render方法返回react元素。

function updateFunctionComponent(vnode) {    
    const { type, props } = vnode    
    let vvnode = type(props)    
    const node = createNode(vvnode)    
    return node
}

function updateClassComponent(vnode) {    
    const { type, props } = vnode    
    const instance = new type(props)    
    const vvnode = instance.render()    
    const node = createNode(vvnode)    
    return node
}
复制代码

这里只是简单的做了粗略的处理,后期有时间我会继续完善!!!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享