jsx是通過(guò)babel轉(zhuǎn)換成react.CreateElement()方法的調(diào)用
轉(zhuǎn)換前
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
轉(zhuǎn)換后
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
React 17 在 React 的 package 中引入了兩個(gè)新入口,這些入口只會(huì)被 Babel 和 TypeScript 等編譯器使用。
新的 JSX 轉(zhuǎn)換不會(huì)將 JSX 轉(zhuǎn)換為 React.createElement,而是自動(dòng)從 React 的 package 中引入新的入口函數(shù)并調(diào)用
Babel 的 v7.9.0 及以上版本可支持全新的 JSX 轉(zhuǎn)換。
function App() {
return <h1>Hello World</h1>;
}
轉(zhuǎn)換后
// 由編譯器引入(禁止自己引入?。?import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
參數(shù)表現(xiàn)不一樣,但最后都是生成ReactElement,用js對(duì)象表示dom結(jié)構(gòu)。
react的主要元素類(lèi)型
null,文本,元素標(biāo)簽,函數(shù)組件和類(lèi)組件。
示例jsx
function FnComp() {
return <div>Hello FnComp</div>
}
class ClassComp extends React.Component {
render() {
return <div>Hello ClassComp</div>
}
}
const virtualDOM =
<div>
{null}
react
<h1>h1標(biāo)簽</h1>
<FnComp></FnComp>
<ClassComp></ClassComp>
</div>
轉(zhuǎn)換后
function FnComp() {
return React.createElement("div", null, "Hello FnComp");
}
class ClassComp extends React.Component {
render() {
return React.createElement("div", null, "Hello ClassComp");
}
}
const virtualDOM = React.createElement("div", null,
null,
"react",
React.createElement("h1", null, "h1\u6807\u7B7E"),
React.createElement(FnComp, null),
React.createElement(ClassComp, null));
實(shí)現(xiàn)createElement
function createElement(type, props, ...children) {
let newChildren = []
children.forEach(child => {
if (child === null || child === false || child === true) {
return
}
// 處理過(guò)的子節(jié)點(diǎn)
if (typeof child === 'object') {
newChildren.push(child)
}
//文本節(jié)點(diǎn)
if (typeof child === 'string') {
newChildren.push({
type: 'text',
props: { textContent: child },
})
}
})
return {
type,
props: Object.assign({ children: newChildren }, props),
}
}
實(shí)現(xiàn)render
function render(VDOM, container) {
let element = mountElement(VDOM)
container.appendChild(element)
}
function mountElement(VDOM) {
let element = null
if (typeof VDOM.type === 'function') {
// class組件有render方法
if (VDOM.type.prototype.render) {
element = mountClassElement(VDOM)
} else {
element = mountFnElement(VDOM)
}
} else {
element = mountNativeElement(VDOM)
}
return element
}
function mountNativeElement(VDOM) {
let element = null
if (VDOM.type === 'text') {
element = document.createTextNode(VDOM.props.textContent)
} else {
element = document.createElement(VDOM.type)
// 設(shè)置屬性
setAttr(element, VDOM)
VDOM.props.children && VDOM.props.children.forEach(child => {
element.appendChild(mountElement(child))
})
}
return element
}
function mountFnElement(VDOM) {
let element = VDOM.type()
return mountElement(element)
}
function mountClassElement(VDOM) {
let element = new VDOM.type()
return mountElement(element.render())
}
function setAttr(element, VDOM) {
VDOM.props && Object.keys(VDOM.props).forEach((propName) => {
let value = VDOM.props[propName]
if (propName.startsWith('on')) {
let eventName = propName.substring(2)
element.addEventListener(eventName, value)
} else if (propName === 'className') {
element.setAttribute('class', value)
//children屬性不需要在標(biāo)簽上顯示
} else if (propName !== "children") {
element.setAttribute(propName, value)
}
})
}
Component先定義個(gè)空類(lèi)就行
class Component {
}
測(cè)試代碼
要借助babel編譯jsx后給react調(diào)用
import myReact from "./myReact"
function FnComp() {
return <div>Hello FnComp</div>
}
class ClassComp extends myReact.Component {
render() {
return <div>Hello ClassComp</div>
}
}
const virtualDOM =
<div>
{null}
react
<h1 className='myClass' extraProp='extraProp' onclick={() => { console.log(1111); }}>h1標(biāo)簽</h1>
<FnComp></FnComp>
<ClassComp></ClassComp>
</div>
myReact.render(virtualDOM, document.getElementById("root"))