React源码初步整理-尝试编写实现React框架(⼀)
⽬录
⼀、在了解React⽅⾯的思考
每⼀次思考和整理⼀个框架的框架或者⼀门语⾔的时候,反复问同⼀个问题,它是什么?每次都会有不同的答案。
⾸先,在之前整理的过程中,了解到了React是⼀个ui库。然后进⼀步划分react的相关技术栈,⼜衍⽣到
Typescript,reduce,dva,scss,webpack,react-router等等等,单从react的⽤法⼊⼿,就是react常⽤api,react虚拟dom节点,react⽣命周期,react hook这些。在官⽅⽂档中可以看到⼤部分关于react的api描述和⽤法描述。然后再到react相关编程思想,相关优化思想。这些都是react。
同时在整理javascript的时候,认为react是js这个主⼲的分⽀,可以看作js库的⼀种。横看成岭侧成峰,不同⾓度思考和了解的⾯更⼴⼀些,理解的程度则更深刻⼀些。
2.该做些什么?
好了,这些都是react。那么问题⼜来了,看了很多对React的分析也好,再看很多遍官⽅⽂档之后,问⾃⼰,是否熟练掌握react? 诚恳的来说,那是会⽤,离熟练总感觉差挺远。那么是不是看了源码就能说掌握和精通?还是更感觉差的远了。既然如此,理论成⽴,不如实操⼀波。不然⽼是看别⼈分析源码来,分析源码去。不如整理好按照源码的api和基本逻辑。然后按照基本逻辑和api的作⽤,尝试编写⼀下。
基本逻辑有了,那既然是js库的⼀种,那就尝试写⼀个react框架。
react框架本⾝是由Facebook团队⼤⽜整理和实现的,耗时数年。咱们的⽬标点,暂定在基本实现react的逻辑上。在实现了react框架的常⽤api后,再回过头去对⽐源码中的相同api的实现⽅式,它们考虑到的逻辑和实现的⽅式的区别。会理解的更深刻些,也更有味道⼀些。看源码解析,看来看去,都说好。还是隔着窗⼦在看。咱们写和别⼈写,差别肯定很⼤,但是具体差别在哪,写过之后,再去看肯定有不⼀样的感受。
完全可以yy⼀下,咱们在做的事,可以看作是react框架实现中Facebook团队的⼀员。在⽤想法和点⼦构建⼀个伟⼤的框架。⽽这个框架即将被数以亿计的开发⼈员使⽤。咳咳,此处省略500字。。。(。 ∀ ) 嗨就对了。
⼆、实现react框架的过程整理
2.1 缩⼩和明确需求范围
React的api中常⽤的和不常⽤的有很多,hook中通过function组件实现class组件的状态管理,⽣命周期。react Fiber中新的diff算法和更新机制,react⾃⼰实现的事件层等。从这些api中抽出⼀部分主要的⾻架需求来构建原型。
1. createElement 以及der()也就是虚拟dom到真实dom节点的转换的功能的实现
2. 类组件和function组件转换成虚拟dom节点,再转换成真实dom节点的⽅式模拟
3.differ算法的基本功能的逻辑实现
3. state状态的实现,以及state状态的更新。
4.⽣命周期钩⼦函数的实现
2.2 js实现React框架的相关逻辑和对象的思考和设计
抛开代码细节不看,单从框架中对相关api的功能介绍和其它对源码解读的博客和视频来思考和整理⼀些数据容器及属性,以及相关的⽅法。 这⼀部分的思考另我想到了软件⼯程中的⽤例图和类图以及流程图。之前对这⼀块更多的是为了考试学的。但是现在来看,这些图对于需求分析和编写流程的整理有很⼤作⽤。 类图主要是是表⽰类的静态结构以及和其它类的关系,流程图则是输⼊到输⼊结果的闭环思考。
分别对应着编写代码时的对象和流程涉及。在捡回相关设计图的编写⽅式后,借⽤相关⼯具编写以后的需求逻辑。
(1)react中的虚拟dom节点操作dom节点,最后还是会回归js操作dom节点的本质。也就是ateElement()和
dom.appendChild()这些Dom对象的api调⽤上。既然如此,react编写dom节点的操作⽅式,理论上来说是编写了⼀套js库,这些库中的function接收了相应的参数。然后在function内部做了⼀系列操作,最后通过ateElement()和dom.appendChild()⽅法,实现了控制dom节点变化的效果。
(2)既然是最后回归了js创建dom节点。那么dom节点可以拆分为哪⼏部分呢?⾸先就要有⼀个dom节点对象,然后分析dom节点对象的属性。根据传参的不同,将dom对象的实例化。最后通过js把对象转换成真实的dom节点样式。这个过程中我认为完全可以把之前看似很⾼端⼤⽓上档次的虚拟dom节点,⽤咱们刚才分析的dom节点对象替代。不管多么华丽滴外表,果然脱了都⼀样,咳咳。 万物皆对象蛤。
(3)state是⼀个状态,状态更新了,就会造成dom节点的更新。原来光记着这个框架的概念了。那么照咱们js去写逻辑怎么看它呢?state 是⼀个对象,setstate(state)是⼀个⽅法,这个⽅法可以操作state对象的值,操作完了值调⽤逻辑(1)中的render()⽅法,然后执⾏流程(1)。这逻辑没⽑病,那暂时就可以有个结论了,state是个对象,之所以能更新dom节点为什么呢?就是调⽤了个render⽅法。从基本逻辑和功能来看,state确实是这么个玩意。
(4)⽣命周期钩⼦函数。回想下react中对⽣命周期的介绍,刚开始学的时候,看的很神奇。组件更新,组件即将更新,组件即将挂载,组件挂载后,组件将要卸载,组件完成卸载。这么⼀⼤堆概念,⼜是钩⼦,⼜是⽣命周期。还⼀堆篇幅更长的博客对⽣命周期分析来分析去。忘掉那些⽜逼Plugs。回到react的终点来看,js操作dom节点,不可否认⾥⾯有很多优化代码的精妙的代码实现⽅式。但是基本盘没变。有创造这么多东西吗,还是壳⼦太亮眼。这些概念,可以说存在,也可以说不存在。⽣命周期,就是function⽅法操作dom节点的不同的⽅法体内。因为function调⽤有顺序,⽽对应的funtion的功能不⼀样,所以⽣命周期函数其实就是不同的function。在不同的function内,执⾏对应的传参,传参也是个function。这就是钩⼦函数。暂时放下众多的React辞藻。瞅瞅⽣命周期,钩⼦函数。逻辑可以跑通,基本盘这样看没⽑病。
这四条主线逻辑,就是开始编写React的逻辑线。各⾃的逻辑线肯定要向下延申,对外扩展。来实现React的更深层次的特性以及优化。
2.3 对象和function初步设计
⼤致的⽅法对象
三,开始编写实现React框架
3.1 实现dom节点对象
const app = ( //jsx语法
<div className="reacr-title" title="test">
Hello,React
<span>React</span>
</div>
)
这⼀部分jsx语法的转化是babel实现的最后会转化为
className: "reacr-title",
title: "test"
}, "Hello,React", /*#__PURE__*/ateElement("span", null, "React"));
因此实际上咱们⽤的是
const React = {
createElement
}
function createElement(tag, attrs, ...childrens) {
return {
tag,
attrs,
childrens
}
}
将真实dom节点拆分属性这部分⼯作,babel帮我们实现了。它将dom节点对象拆分成了三部分,tag,attrs和childrens
3.der⽅法
const ReactDom = {//模拟ReactDom对象
render
}
// 渲染
function render(vnode,rootnode) {
if(vnode === undefined) return
if(typeof vnode === 'string'){ //1 如果vnode是字符串创建⽂本节点 let textNode = ateTextNode(vnode)
return rootnode.appendChild(textNode)
}
const {tag,attrs,childrens} = vnode // 2 虚拟dom对象
const dom = ateElement(tag)
if(attrs){
for(key in attrs){
let value = attrs[key]
setAttribute(dom,key,attrs[key])// 3.⽤于渲染节点的⽅法
}
}
}
还需要处理childrens属性
const ReactDom = {//模拟ReactDom对象
render
}
// 渲染
function render(vnode,rootnode) {
if(vnode === undefined) return
if(typeof vnode === 'string'){ //1 如果vnode是字符串创建⽂本节点 let textNode = ateTextNode(vnode)
return rootnode.appendChild(textNode)
}
const {tag,attrs,childrens} = vnode // 2 虚拟dom对象
const dom = ateElement(tag)
if(attrs){
for(key in attrs){
let value = attrs[key]
setAttribute(dom,key,attrs[key])// 3.⽤于渲染节点的⽅法
}
}
childrens&&(childrens.forEach(child=>render(child,dom)))
return rootnode.appendChild(dom);
}
setAttrbute⽅法
function setAttribute(dom,key,value){
// className
if(key==='className'){
key = 'class'
}
// event
if(/on\w+/.test(key)){
key = LowerCase()
dom[key] = value || ''
}else if(key === 'style'){
// style 可以是字符串也可以是对象
if(!value || typeof key === 'string'){
dom.style.cssText = value || ''
react开发框架}else if(value && typeof key === 'object'){
for(let k in value){
if(typeof value[k] === 'number'){
dom.style[k] = value[k] + 'px'
}else{
dom.style[k] = value[k]
}
}
}
}else{
// 其他属性
dom.setAttribute(key,value)
}
3.3 class组件和function组件渲染
function组件是return了⼀个和节点⼀样的jsx对象。那么也等同于jsx语法对象。
但是class类组件如何判断并转换成节点呢? class⾥⾯⼀定有⼀个render. 组件渲染,组件对象和节点对象不⼀样,存在props和state。因此要创建对应的组件对象。
(1)class类对象的原型⽅法中存在render。 class语法糖最后还是会转换成function⽅法
if(comp.prototype && der)//判断是否为class组件
{}
(2)class类对象模拟
class Component{
constructor(props={}){
this.props = props
this.state = {}
}
}
const React = {
createElement,
Component
}
const ReactDom = {
render
}
(3)完成class类和function组件逻辑的实现
在原来的基础上,增加对函数组件和类组件的判断。以及函数组件对象的创建和函数组件对象属性的设置。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论