react复盘-jsx
1、React.createElement实现原理
className 代替class,
class是js保留关键字所以用className
写标签和调用React.createElement区别
babel-jsx插件会自动编译,把 dom标签
(
<div className="title" style={{color:'red'}}>
<span>hello</span>world
</div>
)
转换以下语法,然后交给react去实现虚拟dom 再生成真实dom
React.createElement("div", {
className: "title",
style: {
color: 'red'
}
}
React.createElemen 最终返回一个虚拟dom 如//标记1:
type的话就是div或者类组件的class名字 或者函数组件的函数名
然后遍历children再去执行render()
let element = <h1>Hello</h1>;
// babel.js自动帮你转为下面
//jsx在执行的时候其实是一个函数调用,它是一个创建元素的工厂函数
let element2 = React.createElement("h1", null, "Hello");
console.log(JSON.stringify(element, null, 2));
console.log(JSON.stringify(element2, null, 2));
//标记1: 打印结果
React.createElement 返回是虚拟dom
function createElement(type,config,children){
let ref;//是用来获取虚拟DOM实例的
let key;//用来区分同一个父亲的不同儿子的
if(config){
delete config.__source;
delete config.__self;
ref = config.ref;
delete config.ref;
key = config.key;
delete config.key;
}
let props = {...config};//没有ref和key
if(arguments.length>3){//如果参数大于3个,说明有多个儿子
//核心就是把字符串或者说数字类型的节点转换成对象的形式
props.children = Array.prototype.slice.call(arguments,2).map(wrapToVdom);
}else{
if(typeof children !== 'undefined')
props.children = wrapToVdom(children);
//children可能是一个字符串,也可能是一个数字,也可能是个null undefined,也可能是一个数组
}
return {
type,
props,
ref,
key
}
}
wrapToVdom: 对字符、number类型的children转换为对象类型。
/**
* 不管原来是什么样的元素,都转成对象的形式,方便后续的DOM-DIFF
* @param {*} element
* @returns
*/
export function wrapToVdom(element){
if(typeof element === 'string' || typeof element === 'number'){
//返回的也是React元素,也是虚拟DOM
return {type:REACT_TEXT,props:{content:element}};//虚拟DOM.props.content就是此文件的内容
}else{
return element;
}
}
ReactDOM.rende() 把虚拟dom变成真实dom元素,再挂载
//实现render方法,把React元素变成真实的DOM元素插入页面root里
ReactDOM.render(element,document.getElementById('root'));
function render(vdom,container){
mount(vdom,container);
}
function mount(vdom,container){
let newDOM = createDOM(vdom);
container.appendChild(newDOM);//插入容器中
if(newDOM.componentDidMount)newDOM.componentDidMount();
}
createDOM 创建真实dom, 如果是REACT_TEXT 创建文本 如果是function 执行mountFunctionComponent创建vdom,再创建真实dom
/**
* 把虚拟DOM转成真实DOM
* @param {*} vdom 虚拟DOM
*/
/**
* 把虚拟DOM转成真实DOM
* @param {*} vdom 虚拟DOM
*/
function createDOM(vdom){
let {type,props,ref} = vdom;
let dom;//获取 真实DOM元素
let prevComponent = mountingComponent
mountingComponent = vdom
//如果type.$$typeof属性是REACT_FORWARD_REF_TYPE值
if(type&&type.$$typeof===REACT_MEMO){
return mountMemoComponent(vdom)
}else if(type&&type.$$typeof===REACT_CONTEXT){
return mountContextComponent(vdom)
}else if(type&&type.$$typeof===REACT_PROVIDER){
return mountProviderComponent(vdom)
}else if(type&&type.$$typeof===REACT_FORWARD_REF_TYPE){
return mountForwardComponent(vdom)
}else if(type === REACT_TEXT){//如果是一个文本元素,就创建一个文本节点
dom = document.createTextNode(props.content);
}else if(typeof type === 'function'){//说明这是一个React函数组件的React元素
if(type.isReactComponent){//说明它是一个类组件
return mountClassComponent(vdom);
}else{
return mountFunctionComponent(vdom);
}
}else if(typeof type === 'string'){
dom = document.createElement(type);//原生DOM类型
}else{
throw new Error(`无法处理的元素类型`,type);
}
if(props){
updateProps(dom,{},props);//根据虚拟DOM中的属性更新真实DOM属性
if(typeof props.children == 'object' && props.children.type){//它是个对象 只有一个儿子
render(props.children,dom);
}else if(Array.isArray(props.children)){//如果是一个数组
reconcileChildren(props.children,dom);
}
}
mountingComponent = prevComponent
//让虚拟DOM的dom属生指向它的真实DOM
vdom.dom = dom;
if(ref)ref.current = dom;//让ref.current属性指向真实DOM的实例
return dom;
}
updateProps 更改属性
function updateProps(dom,oldProps,newProps){
for(let key in newProps){
if(key === 'children'){continue;}//后面会单独处理children属性,所以此处跳过去
if(key === 'style'){
let styleObj = newProps[key];
for(let attr in styleObj){
dom.style[attr]=styleObj[attr];
}
}else if(key.startsWith('on')){//onClick
//dom[key.toLocaleLowerCase()]=newProps[key];//dom.οnclick=handleClick
addEvent(dom,key.toLocaleLowerCase(),newProps[key]);
}else{
if(newProps[key])
dom[key]=newProps[key];
}
}
}
// mountFunctionComponent 调用函数组件 然后返回一个虚拟dom 然后重新去走创建真实dom
function mountFunctionComponent(vdom){
let {type,props} = vdom;
let renderVdom = type(props);
return createDOM(renderVdom);
}
vdom格式:
{
"type": "div",
"props": {
"className": "title",
"style": {
"color": "red"
},
"children": [
{
"type": "span",
"props": {
"children": {
"props": {
"content": "hello"
}
}
}
},
{
"props": {
"content": "world"
}
}
]
}
}
如果是函数组件
React.createElement(FunctionComponent, { name: “zhufeng” });