react 运行函数组件
import React from './react';
import ReactDOM from './react-dom';
function FunctionComponent(props){
return <div className="title" style={{ color: 'red' }}>
<h1>{props.name}</h1>
<h3>{props.children}</h3>
</div>
}
ReactDOM.render(
<FunctionComponent name="hello">world</FunctionComponent>,
document.getElementById('root')
);
1. jsx 转成 js 代码
由于 js 不认识 jsx 代码,所以需要将下面 jsx 语法转化成 js 代码
function FunctionComponent(props){
return <div className="title" style={{ color: 'red' }}>
<h1>{props.name}</h1>
<h3>{props.children}</h3>
</div>
}
点击此 jsx to react 复制上述代码进去即可转化成 js 代码, 代码如下:
function FunctionComponent(props) {
return React.createElement("div", {
className: "title",
style: {
color: 'red'
}
},
React.createElement("h1", null, props.name), // 第二个参数及之后都是子元素
React.createElement("h3", null, props.children)
);
}
下面组件 jsx 代码
<FunctionComponent name="hello">world</FunctionComponent>
也会转化成:
React.createElement(FunctionComponent, { // 区别就是 参数1 变成函数
name: "hello"
}, "world");
在 create-react-app 项目中,我们通过 react-scripts start
可以运行 react 项目是因为 react-scripts 内配置的 webpack 中的插件 @babel/plugin-syntax-jsx
将 jsx 转化成了 js 代码。
2. React.createElement 接收函数参数
现在知道为什么没有使用 react 都需要 import React from 'react';
引入了吧, 因为 jsx 转 js 后的代码会用到 react。
执行如下方法会生成什么呢?
React.createElement(FunctionComponent, { // 区别就是 参数1 变成函数
name: "hello"
}, "world");
执行如上方法,其实就是执行如下内容:
function FunctionComponent(props) {
return React.createElement("div", {
className: "title",
style: {
color: 'red'
}
},
React.createElement("h1", null, props.name), // 第二个参数及之后都是子元素
React.createElement("h3", null, props.children)
);
}
React.createElement(FunctionComponent, { // 区别就是 参数1 变成函数
name: "hello"
}, "world");
执行如下方法:
ReactDOM.render(
<FunctionComponent name="hello">world</FunctionComponent>,
document.getElementById('root')
);
其实就是执行如下内容:
function FunctionComponent(props) {
return React.createElement("div", {
className: "title",
style: {
color: 'red'
}
},
React.createElement("h1", null, props.name), // 第二个参数及之后都是子元素
React.createElement("h3", null, props.children)
);
}
ReactDOM.render(
React.createElement(FunctionComponent, { // 区别就是 参数1 变成函数
name: "hello"
}, "world"),
document.getElementById('root')
)
可见 ReactDOM.createElement
方法还是【系列 1】 那个方法返回 vdom,
需要改变的只是 React.render
的 参数1 需要兼容传递函数 vdom
修改代码如下:
function render(vdom, container) {
mount(vdom, container);
}
export function mount(vdom, container) {
const newDOM = createDOM(vdom);
container.appendChild(newDOM);
}
export function createDOM(vdom) {
const { type, props } = vdom;
let dom;
if (type === REACT_TEXT) {
dom = document.createTextNode(props.content);
// + 添加一个是函数的判断
} else if (typeof type === "function") {
return mountFunctionComponent(vdom);
} else {
dom = document.createElement(type);
}
if (props) {
updateProps(dom, {}, props);
if (typeof props.children == "object" && props.children.type) {
mount(props.children, dom);
} else if (Array.isArray(props.children)) {
reconcileChildren(props.children, dom);
}
}
vdom.dom = dom;
return dom;
}
// + 添加一个方法就是执行这个函数组件 传递组件上的参数
function mountFunctionComponent(vdom) {
const { type, props } = vdom
const renderVdom = type(props) // type 就是 FunctionComponent 组件函数
return createDOM(renderVdom)
}
function updateProps(dom, oldProps = {}, newProps = {}) {
for (let key in newProps) {
if (key === "children") {
continue;
} else if (key === "style") {
let styleObj = newProps[key];
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
} else {
dom[key] = newProps[key];
}
}
for (let key in oldProps) {
if (!newProps.hasOwnProperty(key)) {
dom[key] = null;
}
}
}
function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i++) {
let childVdom = childrenVdom[i];
mount(childVdom, parentDOM);
}
}
const ReactDOM = {
render,
};
export default ReactDOM;
最终效果:
此时打开浏览器我们就看见了 hello world
本系列代码 | 链接 |
---|---|
GitHub | github.com/shunyue1320… |
Gitee | gitee.com/shunyue/min… |