写在开首
工作中运用react也很长一段时间了,虽然对它的用法,道理有了一定的相识,然则总觉得停留在外表。本着知其然知其所以然的立场,我试着去看了react源码,几天下来,发明并不能看懂,反而越发云里雾里了- -!。既然看不懂,那就看看社区先辈们写的一些源码剖析文章以及完成思绪吧,又这么过了几天,总算是摸盘点思绪,因而在参考了先辈们的基础上,完成了一个简易版的react。
这个系列我盘算分为3节,第一节引见下完成的思绪以及构造,第二节讲衬着,第三节讲更新。
进入正题
尽人皆知,react的中心是Virtual DOM,所以,我们的思绪也是围绕着Virtual DOM睁开,包括Virtual DOM模子的竖立,生命周期的治理,对照差别的diff算法,将Virtual DOM转化为原生DOM并展现的patch要领等,setState异步机制以及react合成事宜因为还没有研讨到,临时先疏忽,事宜处置惩罚跟某位先辈的思绪一样,也是运用jquery事宜替代,这里我们主要以完成衬着,更新为主,相信你在看完这个系列后,能对react的运转道理有一定明白。
项目地点:https://github.com/LuSuguru/f…,以下的一切代码都是经由过程es6编写,切勿用在临盆环境。
Virtual DOM的完成
React的一切都基于Virtual DOM,我们第一步天然先完成它,以下:
/**
* @param type :代表当前的节点属性
* @param key :用来标识element,用于优化今后的更新
* @param props:节点的属性
*/
function VDom(type, key, props) {
this.type = type
this.key = key
this.props = props
}
// 代码地点:src/react/reactElement.js
完成了vDom后,理所须要一个要领来将我们写的元素转化为vDom。平常我们都是JSX来建立元素的,但它只不过是React.createElment的语法糖。所以,接下来,我们要完成的就是createElement要领:
function createElement(type, config, ...children) {
const props = {}
config = config || {}
// 猎取key,用来标识element,轻易今后高效的更新
const { key = null } = config
let propName = ''
// 复制config里的内容到props
for (propName in config) {
if (config.hasOwnProperty(propName) && propName !== 'key') {
props[propName] = config[propName]
}
}
// 转化children
if (children.length === 1 && Array.isArray(children[0])) {
props.children = children[0]
} else {
props.children = children
}
return new VDom(type, key, props)
}
// 代码地点:src/react/reactElement.js
这段代码也异常简朴,依据我们传入的参数,天生对应的vDom
ReactComponent的完成
我们所建立的VDom范例分为3种:
- 文本范例
- 原生DOM范例
- 自定义范例
差别的范例,一定有差别的衬着和更新逻辑,我们把这些逻辑与vDom一同,封装成对应的ReactComponent类,经由过程ReactComponent类掌握vDom,这里我把它们命名为ReactTextComponent,ReactDomComponent,ReactCompositeComponent,离别对应三种范例。
首先是基类ReactComponet:
// component基类,用来处置惩罚差别的假造dom更新,衬着
class Component {
constructor(element) {
this._vDom = element
// 用来标识当前component
this._rootNodeId = null
}
}
// 代码地点:src/react/component/ReactComponent.js
接着再让差别范例的component继续这个基类,每种component范例都有mount和update两个要领,用来实行衬着和更新
class ReactDomComponent extends ReactComponent {
// 衬着
mountComponent() {}
// 更新
updateComponent() {}
}
class ReactCompositeComponent extends ReactComponent {
// 衬着
mountComponent() {}
// 更新
updateComponent() {}
}
class ReactTextComponent extends ReactComponent {
// 衬着
mountComponent() {}
// 更新
updateComponent() {}
}
进口的完成
完成了ReactComponent后,我们天然须要一个进口去获得ReactComponent并挪用它的mount。在运用React时,一般都是经由过程
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
}
ReactDOM.render(<App />, document.getElementById('root'))
这段代码来充任衬着的进口,下面我们来完成这个进口,(为了轻易申明,我把render要领也放在了React对象中)
import Component from './Component'
import createElement from './ReactElement'
import instantiateReactComponent from './component/util'
import $ from 'jquery'
const React = {
nextReactRootIndex: 0, // 标识id,肯定每一个vDom的唯一性
Component, // 一切自定义组件的父类
createElement, // 建立vdom
render(vDom, container) { // 进口
var componentInstance = instantiateReactComponent(vDom) //经由过程vDom天生Component
var markup = componentInstance.mountComponent(this.nextReactRootIndex++)
container.innerHTML = markup
$(document).trigger('mountReady')
}
}
// 代码地点:src/react/index.js
因为衬着和更新都已封装在差别的ReactComponent里,所以,这里也须要一个要领,依据差别的vDom范例天生对应的ReactComponent,下面我们就来完成这个要领:
// component工场,用来返回一个component实例
function instantiateReactComponent(node) {
// 文本节点的状况
if (typeof node === 'string' || typeof node === 'number') {
return new ReactTextComponent(node)
}
// 浏览器默许节点的状况
if (typeof node === 'object' && typeof node.type === 'string') {
return new ReactDomComponent(node)
}
// 自定义的元素节点
if (typeof node === 'object' && typeof node.type === 'function') {
return new ReactCompositeComponent(node)
}
}
然后再挪用进口ReactComponent的mount要领,猎取衬着内容,再将其衬着出来就行。
总结
以上就是完成一个react的整体思绪,下节我们重点放在差别ReactComponet的mount上。
下一节地点:https://segmentfault.com/a/11…
参考资料,谢谢几位先辈的分享:
https://www.cnblogs.com/sven3…
https://github.com/purplebamb…
陈屹 《深切React手艺栈》