什么是React
- React是一个用于构建用户界面的JavaScript库,核心专注于视图,目的实现组件化开发
创建项目
- create-react-app xx
- cd xx
- npm run start
什么叫JSX
- JSX其实只是一种语法糖,最终会通过babel.js转译成React.createElement语法
- React元素事实上是普通的JS对象,用来描述你在屏幕上看到的内容
- ReactDOM来确保浏览器中的真是DOM数据和React元素保持一致
JSX
<h1 className="title" style={{color:'red'}}>hello
转译后的代码
React.createEelement("h1",{
className:"title",
style:{
color:"red"
}
), "hello"};
返回的结果
{
type:"h1",
props:{
className:"title",
style:{
color:"red"
}
},
children:"hello"
}
关系图
下面我们开始实现
转换
- 从上面jsx转换成下面,借助的是babel插件plugin-transform-react-jsx,react中会首先进行这种转换,然后经过createElement方法处理成虚拟dom返回
创建react.js文件,导出包含createElement方法的对象
我们通过使用官方的react打印createElement处理后的结果如下
删除没什么用的内部属性,保留ref和key
如果父元素有多个儿子,那么会往createElement的参数后面一个一个追加,所以我们要处理一下children参数问题
- 如果参数大于3说明不止一个儿子
- 否则就只有一个儿子,直接赋值
- 这里我们为什么用截取参数的方式而不是用剩余运算符呢?
- 答案就是,在react源码中,如果没有儿子就是undifined,有一个儿子就会处理成一个对象,有多个儿子就会处理成数组。而剩余运算符得到的一定是数组,就和react源码中不一样了
src下创建constants.js文件,用来存放一些常量
createElement方法最后我们就拼一个虚拟dom返回
我们现在引入自己写的react来看效果
打印结果
react源码中如果children是一个字符串,那么结果就是一个字符串,不会处理成元素
但是我们自己实现的react,为了之后DOM-DIFF方便,我们需要处理一下,把文本字符串包裹成元素
- 此逻辑在react源码里是没有的,是我们为了后面方便DOM-DIFF方便添加的
在createElement方法中,对children进行包装
至此我们拿到了虚拟DOM
下面开始写渲染逻辑,也就是react-dom
创建react-dom.js文件,导出包含render方法的对象
首先,调用createDOM把虚拟dom处理成真实dom,然后插入到容器中
createDOM方法
- 拿到type和props
- 判断type类型,如果是REACT_TEXT,就创建文本节点
- 否则就创建普通元素
- 判断如果存在props,那么我们就更新属性
updateProps方法
- 遍历新属性,如果key是一个children,那么就跳过,因为儿子不在这里处理
- 如果属性是style,那么就循环给dom赋属性
- 否则就直接赋值,默认处理,我们写的虚拟dom里的属性一般跟真实属性是一一对应的
然后遍历老的属性
- 如果老属性在新属性里没有,那么就删掉
然后我们再回到createDOM方法中
- 在调用updateProps方法更新属性后我们还没处理children
- 判断props的children是否是一个对象并且type有值,如果条件成立,就调用render方法,把儿子也变成真实DOM,这是处理一个儿子的情况
- 然后判断儿子是否是一个数组,这是判断多个儿子的情况,条件如果成立,那么我们就调用reconcileChildren,传入children和dom
reconcileChildren方法
- 循环调用render处理children