從頭實現一個簡易版React(一)

寫在開頭

工做中使用react也很長一段時間了,雖然對它的用法,原理有了必定的瞭解,可是總感受停留在表面。本着知其然知其因此然的態度,我試着去看了react源碼,幾天下來,發現並不能看懂,反而更加雲裏霧裏了- -!。既然看不懂,那就看看社區前輩們寫的一些源碼分析文章以及實現思路吧,又這麼過了幾天,總算是摸清點思路,因而在參考了前輩們的基礎上,實現了一個簡易版的react。
這個系列我打算分爲3節,第一節介紹下實現的思路以及結構,第二節講渲染,第三節講更新。javascript

進入正題

衆所周知,react的核心是Virtual DOM,因此,咱們的思路也是圍繞着Virtual DOM展開,包含Virtual DOM模型的創建,生命週期的管理,對比差別的diff算法,將Virtual DOM轉化爲原生DOM並展現的patch方法等,setState異步機制以及react合成事件因爲尚未研究到,暫時先忽略,事件處理跟某位前輩的思路同樣,也是使用jquery事件代替,這裏咱們主要以實現渲染,更新爲主,相信你在看完這個系列後,能對react的運行原理有必定理解。
項目地址:https://github.com/LuSuguru/f...,如下的全部代碼都是經過es6編寫,切勿用在生產環境。html

Virtual DOM的實現

React的一切都基於Virtual DOM,咱們第一步天然先實現它,以下:java

/**
 * @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方法:node

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

這段代碼也很是簡單,根據咱們傳入的參數,生成對應的vDomreact

ReactComponent的實現

咱們所建立的VDom類型分爲3種:jquery

  • 文本類型
  • 原生DOM類型
  • 自定義類型

不一樣的類型,確定有不一樣的渲染和更新邏輯,咱們把這些邏輯與vDom一塊兒,封裝成對應的ReactComponent類,經過ReactComponent類控制vDom,這裏我把它們命名爲ReactTextComponent,ReactDomComponent,ReactCompositeComponent,分別對應三種類型。
首先是基類ReactComponet:git

// component基類,用來處理不一樣的虛擬dom更新,渲染
class Component {
  constructor(element) {
    this._vDom = element
    // 用來標識當前component
    this._rootNodeId = null
  }
}
// 代碼地址:src/react/component/ReactComponent.js

接着再讓不一樣類型的component繼承這個基類,每種component類型都有mount和update兩個方法,用來執行渲染和更新es6

class ReactDomComponent extends ReactComponent {
    // 渲染
  mountComponent() {}

  // 更新
  updateComponent() {}
}
class ReactCompositeComponent extends ReactComponent {
    // 渲染
  mountComponent() {}

  // 更新
  updateComponent() {}
}
class ReactTextComponent extends ReactComponent {
    // 渲染
  mountComponent() {}

  // 更新
  updateComponent() {}
}

入口的實現

實現了ReactComponent後,咱們天然須要一個入口去獲得ReactComponent並調用它的mount。在使用React時,一般都是經過github

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方法,獲取渲染內容,再將其渲染出來就行。

總結

clipboard.png

以上就是實現一個react的整體思路,下節咱們重點放在不一樣ReactComponet的mount上。
下一節地址:https://segmentfault.com/a/11...

參考資料,感謝幾位前輩的分享:
https://www.cnblogs.com/sven3...
https://github.com/purplebamb...陳屹 《深刻React技術棧》

相關文章
相關標籤/搜索