准备阶段
建立项目
- 经过
create-react-app easy_react
脚手架建立一个项目
- 清理
src
目录,这里咱们只留下一个index.js
便可
- 修改
index.js
以下
import React from 'react';
import ReactDOM from 'react-dom';
const style = {
color: 'red',
background: 'yellow',
fontSize:'20px'
}
ReactDOM.render(
<h1 id={'gu_yan'} className={'gu-yan'} style={style}> Guyan </h1>, document.getElementById('root'));
复制代码
启动项目
- 执行
yarn start
页面效果以下
分析阶段
- 将
index.js
的代码拷贝到babel
中进行转换,效果以下
- 分析上图
‖
‖
‖
↓
React.createElement("h1", {
id: 'gu_yan',
className: 'gu-yan',
style: style
}, "Guyan")
复制代码
- 将
index.js
文件,修改以下,发现页面无变化,控制台打印结果以下图
import React from 'react';
import ReactDOM from 'react-dom';
const style = {
color: 'red',
background: 'yellow',
fontSize: '20px'
};
const element = React.createElement("h1", {
id: 'gu_yan',
className: 'gu-yan',
style: style
}, "Guyan")
console.log(element)
ReactDOM.render(element, document.getElementById('root'));
复制代码
- 如上图可知
React.createElement()
执行返回的结果是一个对象,这个对象咱们就称之为虚拟DOM
,到这里咱们就能够总结出虚拟DOM
的由来了
- 【1】在
webpack
打包的时候会调用babel-loader
,将JSX
语法转义为React.createElement(...)
的形式
- 【2】
React.createElement
执行返回一个对象,这对象就是虚拟DOM
- 解析
React.createElement
的执行过程
- 【1】收集属性对象,处理特殊的属性好比
children
,key
,ref
;本文只举例说明children
- 【1-1】建立一个
props
对象
- 【1-2】将传进来的第二个参数对象中的每个属性都挂在
props
上,值为第二个参数对象中相对于的值
- 【1-3】给
props
挂一个children
属性,值为传进来的第三个参数
- 注:
children
这个值是一个字符串或者是一个数组。若是执行React.createElement
的时候传入的参数大于3,那么children
的值就是一个数组,其值为除前两个以外的全部属性
- 【2】将传入的
type
和收集的属性对象做为参数传入到ReactElement
中执行(reactElement(type,props)
)
- 【3】
ReactElement
执行建立一个newElement
对象
- 【4】给
newElement
挂一个$$typeof
属性,这里咱们统一将其值赋为Symbol(react.element)
- 【5】给
newElement
挂一个type
属性,值为传进来的type
- 【6】给
newElement
挂一个props
属性,值为传进来的props
- 【7】返回
newElement
(虚拟DOM
)
- 解析
ReactDom.render
的执行过程
- 【1】判断传入的虚拟
DOM
的类型
- 【1.1】普通文本类型,则第一个参数多是
string
或者number
,则直接建立一个真实文本节点
- 【1.2】普通
html
标签组件,好比<div></div>
,第一个参数对象的type
属性是一个string
类型,建立一个真实的DOM
节点并将第一个参数的props
属性中的一些普通属性挂载到这个真实的DOM
节点上,并对一些特殊的属性好比children
的进行处理,详细见下文的实现阶段
- 【1.3】函数组件(
function Component
),第一个参数对象的type
属性是一个function
类型,type
执行传入第一个参数的props
- 【1.4】类组件(
class Component
),第一个参数对象的type
属性上有一个isReactComponent
属性,new type(props)
建立实例,并让实例的render
方法执行
- 【2】将建立的真实节点插入到父节点中
实现阶段
react.js
class Component {
static isReactComponent = true;
constructor(props){
this.props = props;
}
}
function ReactElement(type,props){
const virtual_dom = {};
virtual_dom.$$typeof = Symbol.for('react.element');
virtual_dom.type = type;
virtual_dom.props = props;
return virtual_dom;
}
function createElement(type,config,children){
const props = {};
for (const propName in config){
if (config.hasOwnProperty(propName)){
props[propName] = config[propName];
}
}
const childrenLength = arguments.length - 2;
if (childrenLength === 1){
props.children = children;
}else if (childrenLength > 2){
props.children = Array.from(arguments).slice(2);
}
return ReactElement(type,props);
}
export default {createElement,Component}
复制代码
react-dom.js
function render(virtual_dom,parent_node){
if (typeof virtual_dom ==='string' || typeof virtual_dom === 'number'){
return parent_node.appendChild(document.createTextNode(virtual_dom));
}
if (virtual_dom.type.isReactComponent){
virtual_dom = new virtual_dom.type(virtual_dom.props).render();
render(virtual_dom,parent_node);
}
if (typeof virtual_dom.type === 'function'){
virtual_dom = virtual_dom.type(virtual_dom.props);
render(virtual_dom,parent_node);
}
let {type , props} = virtual_dom;
let real_dom = document.createElement(type);
for (const propName in props){
if(props.hasOwnProperty(propName)){
const prop = props[propName];
if(propName === 'className'){
real_dom.className = prop;
}else if (propName === 'style'){
let cssText = Object.keys(prop).map(attr=>(`${attr.replace(/([A-Z])/g,function(){ return `-${arguments[1].toLowerCase()}` })}:${prop[attr]}`)).join(';');
real_dom.style.cssText = cssText;
}else if (propName === 'children'){
let childArr = Array.isArray(prop) ? prop : [prop];
childArr.forEach(child=>render(child,real_dom));
}else {
real_dom.setAttribute(propName,prop);
}
}
}
return parent_node.appendChild(real_dom);
}
复制代码
写在最后
- 平常推荐使用函数组件
- 本文中的仅仅在表面层说明原理。并未深度剖析,如何错误还望指教。谢谢!