因为react依赖JSX语法,所以最开始需要对JSX的执行过程做大致介绍。至于JSX的相关基础知识,请参照JSX官网。
JSX的执行过程
- 1.写出代码
<h1>Hello</h1>
- 2.用打包工具如webpack,调用babel-loader把JSX语法转换成JavaScript函数,createElement
- 3.运行代码时,浏览器执行createElement,得到虚拟DOM,也就是react元素。其实react元素就是一个纯粹的JavaScript对象,描述了会在页面上显示的DOM的属性。
- 4.把虚拟DOM(react元素)给render函数,render就会把虚拟DOM转换成真实的DOM,并插入到页面中去。
react中的虚拟DOM
上面说道react元素就是虚拟DOM,接着来看一下react元素有哪些属性
// 首先是调用React.createElement生成react元素,并在控制台输出
let element = React.createElement("h1", {
className: 'title',
style: {
color: 'red'
}
}, React.createElement('span', null, 'hello'), ' world');
console.log(JSON.stringify(element,null,2));
ReactDOM.render(
element,
document.getElementById('root')
);
// 下面展示的属性并不是完全的react元素的属性,为了简化,把一些当前阶段用不到的属性进行删除
/**
{
"type": "h1",
"props": {
"className": "title",
"style": {
"color": "red"
},
"children": [
{
"type": "span",
"props": {
"children": "hello"
}
},
" world"
]
}s
}
*/
主要思路
- 1.createElement函数的主要任务就是创建一个JavaScript对象
- 2.render函数的主要任务就是创建文本节点和各种元素,将其插入到页面中去
代码实现
- 1.createElement函数
/**
*
* @param {*} type 元素类型
* @param {*} config 配置对象,一般来说就是属性对象
* @param {*} children 第一个儿子
*/
function createElement(type,config,children){
if(config){
delete config._owner;
delete config._store;
}
let props = {...config};
if(arguments.length > 3){
children = Array.prototype.slice.call(arguments,2);
}
// children可能是数组(多于一个儿子),也可能是一个字符串或数字、也可能是一个null 或 react元素
props.children = children;
return {
type,props
}
}
- 2.render函数
/**
* 虚拟DOM转换成真实DOM 并插入到容器
* @param {*} vdom 虚拟DOM
* @param {*} container 插入到哪个容器
*/
function render(vdom,container){
const dom = createDOM(vdom);
container.appendChild(dom);
}
/**
* 把虚拟DOM变成真实DOM
* @param {} vdom null 数字 字符串 React元素 数组 暂时不支持数组
*/
function createDOM(vdom){
// null就什么都不做
// 如果vdom是一个字符串或者数字的话,创建一个文本的DOM节点返回
if(typeof vdom === 'string' || typeof vdom === 'number'){
return document.createTextNode(vdom);
}
// 如果不存在返回空串 undefined null
if(!vdom){
return '';
}
// 否则就是一个react元素
let {type,props} = vdom;
let dom = document.createElement(type); //span div
updateProps(dom,props); // 把虚拟DOM上的属性设置到真实DOM上
// 处理子节点 如果子节点就是一个单节点 并且是字符串或数字
if (typeof props.children === 'string' || typeof props.children === 'number') {
dom.textContent = props.children; // dom.textContent = 'hello';
// 说明是一个单React元素节点
} else if (typeof props.children === 'object' && props.children.type) {
render(props.children,dom);
// 如果儿子是一个数组,说明有多个子节点
}else if(Array.isArray(props.children)){
reconcileChildren(props.children,dom);
}else{
// 如果出现了其他的意外情况
dom.textContent = props.children ? props.children.toString():'';
}
return dom;
}
/**
* 把子节点从虚拟DOM全部转换成真实DOM 并插入到父节点中去
* @param {*} childrenVdom 子节点的虚拟DOM数组
* @param {*} parentDOM 父节点真实DOM
*/
function reconcileChildren(childrenVdom,parentDOM){
childrenVdom.forEach(childVdom => {
render(childVdom,parentDOM);
});
}
/**
* 把属性对象中的属性设置到DOM元素上
* @param {*} dom
* @param {*} props
*/
function updateProps(dom,props){
for(let key in props){
if(key === 'children') continue;
if(key === 'style'){
let styleObj = props[key];
for(let key in styleObj){
dom.style[key] = styleObj[key]; // dom.style.color = 'red';
}
}else{
dom[key] = props[key]; // dom.className = 'title';
}
}
}
代码仓库
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!