react使用也有一段時間了,你們對這個框架褒獎有加,可是它究竟好在哪裏呢?
讓咱們結合它的源碼,探究一二!(當前源碼爲react16,讀者要對react有必定的瞭解)css
根據react官網上的例子,快速構建react項目html
npx create-react-app my-app cd my-app npm start
打開項目並跑起來之後,暫不關心項目結構及語法糖,看到App.js
裏,這是一個基本的react組件<App/> 咱們console一下,看看有什麼結果。前端
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> </header> </div> ); } } export default App; console.log(<App/>)
能夠看到,<App/>
組件實際上是一個JS對象,並非一個真實的dom。node
ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。有興趣的同窗能夠去阮一峯老師的ES6入門詳細瞭解一下
上面有咱們很熟悉的props
,ref
,key
,咱們稍微修改一下console,看看有什麼變化。react
console.log(<App key={1} abc={2}><div>你好,這裏是App組件</div></App>)
能夠看到,props
,key
都發生了變化,值就是咱們賦予的值,props
中嵌套了children屬性。但是爲何咱們嵌入的是div,實際上倒是一個對象呢?git
/node_modules/react
首先打開index.js
github
'use strict'; if (process.env.NODE_ENV === 'production') { module.exports = require('./cjs/react.production.min.js'); } else { module.exports = require('./cjs/react.development.js'); }
能夠知道目前用上的是./cjs/react.development.js
,直接打開文件。
根據最初的代碼,咱們組件<App/>
用到了React.Component。找到React暴露的接口:npm
接着找到Component: Component
方法,數組
function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = {}; Component.prototype.setState = function (partialState, callback) { !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0; this.updater.enqueueSetState(this, partialState, callback, 'setState'); }; Component.prototype.forceUpdate = function (callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); };
上面就是一些簡單的構造函數,也能夠看到,咱們經常使用的setState是定義在原型上的2個方法。babel
至此,一個<App/>
組件已經有一個大概的雛形:
到此爲止了嗎?這看了等於沒看啊,究竟組件是怎麼變成div的?render嗎?
但是全局搜索,也沒有一個function是render啊。
原來,咱們的jsx語法會被babel
編譯的。
這下清楚了,還用到了React.createElement
createElement: createElementWithValidation,
經過createElementWithValidation
,
function createElementWithValidation(type, props, children) { ······ var element = createElement.apply(this, arguments); return element; }
能夠看到,return了一個element,這個element又是繼承自createElement
,接着往下找:
function createElement(type, config, children) { var propName = void 0; // Reserved names are extracted var props = {}; var key = null; var ref = null; var self = null; var source = null; ······ return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); }
這裏又返回了一個ReactElement
方法,再順着往下找:
var ReactElement = function (type, key, ref, self, source, owner, props) { var element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner }; ······ return element; };
誒,這裏好像返回的就是element
對象,再看咱們最初的<App/>
的結構,是否是很像
驗證一下咱們的探索究竟對不對,再每個方法上咱們都打上console,(注意,將App裏的子元素所有刪空,利於咱們觀察)
React.createElement 、 createElementWithValidation 、 createElement 、 ReactElement,經過這些方法,咱們用class聲明的React組件在變成真實dom以前都是ReactElement
類型的js對象
createElementWithValidation
:
createElement
:
ReactElement
:
ReactElement就比較簡單了,建立一個element對象,參數裏的type、key、ref、props、等放進去,而後return了。最後調用Object.freeze使對象不可再改變。
咱們上面只是簡單的探究了<App/>
的結構和原理,那它到底是怎麼變成真實dom的呢
ReactDOM.render(<App />, document.getElementById('root'));
咱們接着用babel編譯一下:
原來ReactDOM.render
調用的是render方法,同樣,找暴露出來的接口。
var ReactDOM = { ······ render: function (element, container, callback) { return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); }, ······ };
它返回的是一個legacyRenderSubtreeIntoContainer
方法,此次咱們直接打上console.log
這是打印出來的結果,
legacyRenderSubtreeIntoContainer
這個方法除主要作了兩件事:
while (rootSibling = container.lastChild) { { if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) { warned = true; } } container.removeChild(rootSibling); }
源碼暫時只讀到了這裏,關於React16.1~3的新功能,以及新的生命週期的使用和原理、Fiber
到底是什麼,咱們將在後續文章接着介紹。
本文發佈於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。