js和jsx
JSX 是一种 JavaScript 的语法扩展(syntax extension),允许开发者在 JavaScript 代码中以类似 HTML 的声明式方式描述 UI 结构。
但是浏览器不支持运行JSX代码,因此需要通过编译工具(通常是 Babel)将 JSX 转换为标准的 JavaScript 代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import React from 'react';
const App = () => { return <h1>test babel</h1> }
automatic会转换为==============> import React from 'react'; import { jsx as _jsx } from "react/jsx-runtime"; const App = () => { return _jsx("h1", { children: "test babel" }); };
classic会转换为=============> import React from 'react'; const App = () => { return React.createElement("h1", null, "test babel"); };
|
Reaect.createElement 是 React 提供的一个用于创建 React 元素的函数。它接受三个参数:
- type:元素的类型,可以是字符串(表示 HTML 元素)或 React
- config: 当前元素的属性 如{ id: ‘test’}
- children: 当前元素的子节点,可以是字符串、数字、React 元素或数组等
且从React18之后 在JSX中不需要引入React
Refer:git@github.com :ZacharyL2/mini-react.git
从代码来看其实就创建虚拟DOM,然后render
JSX创建Element
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| interface VirtualElement { type: string | Function; props: { [key: string]: any; children: VirtualElement[]; }; }
const createTextElement = (text: string): VirtualElement => ({ type: "TEXT", props: { nodeValue: text, children: [] } })
const isVirtaulElement = (element: unknown): element is VirtualElement => { return typeof element === 'object' && element !== null && 'type' in element && 'props' in element; } const createElement = ( type: VirtualElement, props: Record<string, unknown> = {}, ...child: (unknown | VirtualElement)[] ) => { const children = child.map(c => isVirtaulElement(c) ? c : createTextElement(String(c))); return { type, props: { ...props, children } } }
|
Render:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const updateDom = (DOM, prevProps, nextProps) => { const defaultPropKeys = 'children';
for(const [removePropKey, removePropValue] of Object.entries(prevProps)){ if(removePropKey.startsWith('on')){ const eventType = removePropKey.slice(2).toLowerCase(); DOM.removeEventListener(eventType, removePropValue); } else if(!(removePropKey in nextProps) && !defaultPropKeys.includes(removePropKey)){ DOM[removePropKey] = ''; } }
for(const [addPropKey, addPropValue] of Object.entries(nextProps)){ if(addPropKey.startsWith('on')){ const eventType = addPropKey.slice(2).toLowerCase(); DOM.addEventListener(eventType, addPropValue); } else if(!defaultPropKeys.includes(addPropKey)){ DOM[addPropKey] = addPropValue; } } }
const createDOM = (fiberNode) => { const {type, props} = fiberNode; const DOM = null;
if(type === 'TEXT'){ DOM = document.createTextNode(''); }else if(typeof type === 'string'){ DOM = document.createElement(type); }
if(DOM !== null){ updateDOM(DOM, {}, props); } return DOM; }
const render = (element, container) => { const DOM = createDOM(element); if(Array.isArray(element.props.children)){ for(const child of element.props.children){ return (child, DOM); } } container.appendChild(DOM); }
|