[Implementación del código fuente de React] Principio de implementación de la representación de elementos

prefacio

Este artículo combinará las ideas de diseño de React para realizar la representación de elementos, es decir, JSX语法cómo crear un DOM real y representarlo en la página. Este artículo básicamente no involucra el código fuente de React, pero es consistente con idea de implementación de React, por lo que es muy adecuado para niños pequeños, es de aprendizaje gratuito, se recomienda seguir los pasos para escribir el código, si hay algún error, ¡critique y corrija!

sugerencia:

  1. Si no sabe qué es JSX o no comprende React, se recomienda que primero consulte la documentación oficial de React y siga la documentación para crear un juego para obtener una comprensión general de JSX.
  2. Si también desea conocer el código fuente de Vue, también puede leer este blog, que también es coherente con la idea de implementación de Vue, que convierte el DOM virtual en DOM real.
  3. No te enredes demasiado en cómo se implementa cada método. Si te enredas demasiado, caerás en el infierno de un bucle recursivo infinito. Lo mismo ocurre con el código fuente de React.

Documentación oficial

Primero intentemos crear un proyecto de React:

npx crear-reaccionar-aplicación mi-aplicación

Ideas de implementación

Aquí solo discutimos el principio de implementación de la representación de elementos.

Insertar descripción de la imagen aquí
React usa Babel para traducir archivos de sintaxis JSX a la función React.createElement, llama a la función React.createElement para convertir JSX en un Dom virtual (es decir, un objeto Vnode) y luego usa la función ReactDOM.render para convertir el DOM virtual. en un montaje DOM real. a la página

  • Implementar la función React.createElement
  • Implementar la función de renderizado
  • Representación completa y visualización en la página.

Inicializar proyecto

Cuando creas un proyecto de React mediante el método anterior, también puedes eliminar los archivos redundantes primero y convertirlos en el archivo jsx más simple.
Aquí, solo conservo un archivo.
Insertar descripción de la imagen aquí

import React from 'react';
import ReactDOM from 'react-dom/client';




let element = <h1>Hello, world</h1>;


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  element
);

Si imprimes con éxito un Hola mundo, entonces el primer paso es exitoso.

Reaccionar.createElement

La traducción de Babel implica el conocimiento de los árboles de sintaxis AST. Puedes leer mi blog anterior. No entraré en detalles aquí. Aquí hablaremos directamente sobre los pasos de implementación de Babel para convertir archivos de sintaxis jsx en llamadas a funciones React.createElement y generar DOM virtual. .

Estructura de datos del Dom virtual.

Aquí primero observamos la estructura de datos del Dom virtual generado por React.createElement, lo cual nos resulta beneficioso para crear un Dom virtual escribiendo a mano.

Imprimimos directamente el elemento Dom virtual.

import React from 'react';
import ReactDOM from 'react-dom/client';




let element = <h1>Hello, world</h1>;


console.log(element);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  element 
);

Insertar descripción de la imagen aquí
Como puede ver, su esencia es un objeto. Babel lo traduce a la función createElement. Después de llamarlo, se devuelve un objeto. Este objeto es el Dom virtual, que contiene varios valores clave.

Es decir, la llamada a esta función se convierte en

	React.createElement("h1",{
    
    className:"title",style:{
    
    color:'red'}},"hello")

Esta función acepta tres parámetros,

  • Uno es el tipo de elemento.
  • El segundo es la configuración del elemento.
  • El tercero es el contenido del elemento (tal vez más que texto, también puede ser un nodo de elemento)

valor clave clave

  • clave: utilizada para que React implemente el algoritmo de diferenciación
  • ref: solía conseguir el verdadero Dom
  • tipo: tipo de elemento
  • props: configuración de elementos (por ejemplo, nodos secundarios, estilos)
  • $$typeof: el identificador único del elemento

Implementación

Como se mencionó anteriormente, este método acepta tres parámetros.

  • Uno es el tipo de elemento.
  • El segundo es la configuración del elemento.
  • El tercero es el contenido del elemento (tal vez más que texto, también puede ser un nodo de elemento)
import React from 'react';
import ReactDOM from 'react-dom';






let element2 = React.createElement("h1", {
    
    
  className: "title",
  style: {
    
    
    color: 'red'
  }
}, 'hello world','hi');




console.log(element2);

ReactDOM.render(
  element2,
  document.getElementById('root')
);

Nota 1 : Si intenta agregar otro texto "hola" después de "hola mundo", encontrará que 子节点有多个el atributo de los niños en sus accesorios será 从一个字符串类型变成数组类型. ¡Esto es muy importante!

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

Nota 2 : Si no es un texto, sino un objeto elemento, es un objeto. Si se trata de objetos de elementos múltiples, se convierte en una matriz con objetos de elementos dentro.

import React from 'react';
import ReactDOM from 'react-dom';






let element2 = React.createElement("h1", {
    
    
  className: "title",
  style: {
    
    
    color: 'red'
  }
}, React.createElement("span", null, "hello"));




console.log(element2);

ReactDOM.render(
  element2,
  document.getElementById('root')
);

Insertar descripción de la imagen aquí

función de inicialización

Creamos un nuevo archivo react.js y exponemos este objeto React. Hay una función createElement en él. Usaremos esta función para devolver un dom virtual.


//接受三个参数,元素的类型、元素的配置、元素的节点

function createElement(type,config,children) {
    
    
    //返回一个虚拟dom
    return {
    
    

    }
}


const React = {
    
    
    createElement
}

export default React;

Manejo de claves y árbitros.

Nuestra clave y referencia están escritas en la configuración, por lo que debemos extraer la clave y el valor por separado y eliminarlos de la configuración.


    //第一步,处理key和ref
    let key, ref
    
    if (config) {
    
    
        key = config.key || null
        ref = config.ref || null
        delete config.key
        delete config.ref
    }

Manejo de accesorios y niños.

Descubrimos a través del código fuente que puso el atributo de los niños y todos los elementos de la configuración en el atributo de accesorios.

Insertar descripción de la imagen aquí
El segundo paso es poner todos los elementos de la configuración en accesorios.

    let props =  {
    
    ...config}

El tercer paso es procesar el nodo secundario, aquí hay tres situaciones:

  • sin nodo hijo
  • Hay un nodo secundario: nodo de texto/nodo de elemento
  • Hay varios nodos secundarios

    //第二步,处理children
    if (props) {
    
    
        //有多个儿子
        if (arguments.length > 3) {
    
    
           //多个儿子,就把他们变成一个数组
            props.children = Array.prototype.slice.call(arguments, 2)
            //有一个儿子  (1)文本  (2)元素
        }else if(arguments.length === 3){
    
    
            props.children = children;
        }
        //没有儿子,不需要去处理
    }

``

Manejar $$tipo de

React utiliza esta clave para identificar elementos. Creamos un stant.jsarchivo para exponer todos los tipos de identificación.


//用于标识元素
export const REACT_ELEMENT = Symbol('react.element')

export const REACT_TEXT = Symbol('react.text')

mejoramiento

Al procesar el nodo hijo, cuando tenemos un solo nodo hijo y es un texto, es de tipo cadena, lo procesamos uniformemente en un tipo de objeto para facilitar las operaciones de actualización posteriores, a través del método toObject

import {
    
     REACT_TEXT } from "./stants";


export function toObject(element) {
    
    
    return typeof element === 'string' || typeof element === 'number' ? {
    
    type:REACT_TEXT,content:element} : element
}

código general

reaccionar.js



//实现以下:
// let element2 = React.createElement("h1", {
    
    
//   className: "title",
//   style: {
    
    
//     color: 'red'
//   }
// }, React.createElement("span", null, "hello"));

import {
    
     REACT_ELEMENT } from "./stants"
import {
    
     toObject } from "./utils"






function createElement(type,config,children) {
    
    
    

    if (config == null) {
    
     
        config = {
    
    }
    }

    //第一步,处理key和ref
    let key, ref
    
    if (config) {
    
    
        key = config.key || null
        ref = config.ref || null
        delete config.key
        delete config.ref
    }





   // 第二步,就是将config中的所有元素都放入到props中
    let props =  {
    
    ...config}


    //第三步,处理children
    if (props) {
    
    
        //有多个儿子
        if (arguments.length > 3) {
    
    
           //多个儿子,就把他们变成一个数组
            props.children = Array.prototype.slice.call(arguments, 2).map(toObject)
            //有一个儿子  (1)文本  (2)元素
        }else if(arguments.length === 3){
    
    
            props.children =  toObject(children)  ;  //统一转变成对象
        }
        //没有儿子,不需要去处理
    }





    //返回一个虚拟dom
    return {
    
      //vnode
        key,
        ref,
        $$typeof:REACT_ELEMENT,
        props,
        type: type,

    }
}





const React = {
    
    
    createElement
}

export default React;

Introduzca nuestro propio archivo de reacción en index.js para probarlo, aquí implementamos la función React.createElement y generamos un Dom virtual.
Insertar descripción de la imagen aquí

Función reaccionar.render

Esta función es la función clave para convertir dom virtual en dom real. Aquí aceptamos dos parámetros, uno es el dom virtual y el segundo es el nodo de montaje, que es para implementar esta función.

 ReactDOM.render(
   element2,
  document.getElementById('root')
 );

función de inicialización


//将虚拟dom转变成真实dom的方法
function createDOM(vnode) {
    
     
	let dom //真实dom


    return dom
}


function render(vnode, container) {
    
    
    
    //将虚拟dom转变成真实dom
    let dom = createDOM(vnode)

    //将真实dom挂载到container上
    container.appendChild(dom)


}


const ReactDOM = {
    
    
    render
}

export default ReactDOM;

Procesar el tipo y generar el nodo de elemento correspondiente.

Mire nuevamente la estructura del nodo virtual que generamos.

  • clave: utilizada para que React implemente el algoritmo de diferenciación
  • ref: solía conseguir el verdadero Dom
  • tipo: tipo de elemento
  • props: configuración de elementos (por ejemplo, nodos secundarios, estilos)
  • $$typeof: el identificador único del elemento

Hemos realizado una optimización arriba: si es texto, lo procesamos nosotros mismos en una estructura de datos de objeto.

{
    
    
	type:REACT_TEXT,
	content:element
}
    //将虚拟dom转变成真实dom的方法
function createDOM(vnode) {
    
     
  
            let {
    
     type, props, content } = vnode

            let Ndom;
            //1、判断type是什么类型的,是文本还是元素并生成对应的节点
            if (type === REACT_TEXT) {
    
       //如果是一个文本类型的
                 Ndom = document.createTextNode(content)  //注意:我们在前面已经把所有的文件节点处理为一个对象类型的了
            } else {
    
    
                  Ndom = document.createElement(type)  //div
            }


            //2、处理属性   {
    
    children  style:{
    
    color:red,fontsize:16px} className="title" }
            if (props) {
    
     
                console.log("props",props)
                //为了后续处理更新操作
                updateProps(Ndom, {
    
    }, props)
            }





        //3、处理子节点
        
        
        return Ndom

}

Atributos de manejo




//初始化和更新props的方法
function updateProps(dom, oldProps, newProps) {
    
    
    //初始化
    if (newProps) {
    
    
         //遍历新的属性对象
    for (let key in newProps) {
    
    
        if (key === 'children') {
    
    
            continue
        } else if (key === 'style') {
    
      //如果是style的话就一个个追加进去
            let styleObj = newProps[key]
            for (let attr in styleObj) {
    
    
                dom.style[attr] = styleObj[attr]
            }
        } else {
    
       //例如className就直接放上去即可
            dom[key] = newProps[key]
        }

    }
    }
   

    //更新操作,如果有旧节点
    if (oldProps) {
    
    
        //旧的属性在新的属性中没有,则删除
        for (let key in oldProps) {
    
     
            if(!newProps[key]){
    
    
               dom[key] = null
        }
    }

}

            //2、处理属性   {
    
    children  style:{
    
    color:red,fontsize:16px} className="title" }
            if (props) {
    
     
                //为了后续处理更新操作
                updateProps(dom, {
    
    }, props)
            }

Procesar nodos secundarios

//处理子节点
//接收两个参数,一个是子节点,另一个是挂载节点
function changeChildren(children, dom) {
    
    

     //有一个儿子的情况  对象
    if (typeof children == 'object'&& children.type ) {
    
    
        render(children, dom)  //递归调用
            //有多个儿子的情况  数组
    } else if (Array.isArray(children)) {
    
    
        //循环处理
        children.forEach(child =>  
            render(child, dom)
        )
     }


}

código general

import {
    
     REACT_TEXT } from "./stants"


    //初始化和更新props的方法
function updateProps(dom, oldProps, newProps) {
    
    
        //初始化
        if (newProps) {
    
    
            //遍历新的属性对象
            for (let key in newProps) {
    
    
                if (key === 'children') {
    
    
                    continue
                } else if (key === 'style') {
    
      //如果是style的话就一个个追加进去
                    let styleObj = newProps[key]
                    for (let attr in styleObj) {
    
    
                        dom.style[attr] = styleObj[attr]
                    }
                } else {
    
       //例如className就直接放上去即可
                    dom[key] = newProps[key]
                }

            }
        }
   

        //更新操作,如果有旧节点
        if (oldProps) {
    
    
            //旧的属性在新的属性中没有,则删除
            for (let key in oldProps) {
    
    
                if (!newProps[key]) {
    
    
                    dom[key] = null
                }
            }

        }
}
    

//处理子节点
//接收两个参数,一个是子节点,另一个是挂载节点
function changeChildren(children, dom) {
    
    

     //有一个儿子的情况  对象
    if (typeof children == 'object'&& children.type ) {
    
    
        render(children, dom)  //递归调用
            //有多个儿子的情况  数组
    } else if (Array.isArray(children)) {
    
    
        //循环处理
        children.forEach(child =>  
            render(child, dom)
        )
     }


}


    //将虚拟dom转变成真实dom的方法
function createDOM(vnode) {
    
     
  
            let {
    
     type, props,content } = vnode
            let Ndom; //新的dom节点
            //1、判断type是什么类型的,是文本还是元素并生成对应的节点
             if (type === REACT_TEXT) {
    
       //如果是一个文本类型的
                Ndom = document.createTextNode(content)  //注意:我们在前面已经把所有的文件节点处理为一个对象类型的了
            } else {
    
    
                Ndom = document.createElement(type)  //div
            }


            //2、处理属性   {
    
    children  style:{
    
    color:red,fontsize:16px} className="title" }
             if (props) {
    
    
                //为了后续处理更新操作
                updateProps(Ndom, {
    
    }, props)

                
                //3、处理子节点
                let children = props.children
                 if (children) {
    
    
                    changeChildren(children, Ndom)
                }

            }




        
        
        return Ndom

}




function render(vnode, container) {
    
    

    //将虚拟dom转变成真实dom
    let dom = createDOM(vnode)

    //将真实dom挂载到container上
    container.appendChild(dom)

}



const ReactDOM = {
    
    
    render
}

export default ReactDOM;

Resumir

Desde entonces, básicamente hemos entendido cómo React implementa el proceso de renderizar elementos en vistas.

Supongo que te gusta

Origin blog.csdn.net/m0_46983722/article/details/132475777
Recomendado
Clasificación