虛擬DOM就是React節點的對象樹結構,React的工做原理就是基於虛擬DOM完成。這個對象樹結構中每一個節點的內容都包含了一個原生DOM所具備的屬性。如:標籤名稱,屬性,子節點,id等。以下這種模式:node
{
tagName: '',
properties: {
},
children: [],
key: ''
}
複製代碼
在React中虛擬DOM稱爲ReactNode。它有三種類型:ReactElement,ReactFragment,ReactText。ReactElement又分爲ReactComponentElement,ReactDOMElement。react
//ReactNode不用類型節點所需的基礎元素
type ReactNode = ReactElement | ReactFragment | ReactText
type ReactElement = ReactDOMElement | ReactComponentElement
type ReactDOMElemnt = {
type: string,
props: {
children: ReactNodeList,
className: string,
style: object,
...etc
}
key: string | boolean | number | null,
ref: string | null
}
type ReactComponentElement<TProps> = {
type: ReactClass<Tprops>,
props: TProps,
key; string | boolean | number | null,
ref: string | null
}
type ReactFragment = Array<ReactNode | ReactEmpty>
type ReactNodeList = ReactNode | ReactEmpty
type ReactText = string | number
type ReactEmpty = null | undefined | boolean
複製代碼
建立一個React元素,它調用的是React.createElement方法。那麼這個方法作了什麼? 看一下JSX和編譯後的JavaScript文件:數組
const app = <Nav ref="nav" key="1" color="blue"><Profile>click</Profile>我是文本</Nav>;
const app = React.createElement(
Nav,
{
ref: 'nav',
key: 1,
color: 'blue'
},
React.createElement(Profile, {}, "click" )
)
複製代碼
經過JSX建立的虛擬元素最終會編譯成React的createElement方法。 源碼目錄在此:bash
//react中createElement方法來源於 ReactElement.js
//createElement相似一個工廠方法,只是作了簡單的參數修正,返回一個ReactElement實例對象。
/**
* 傳入了以下參數:
* type: "Nav"
* config: { ref: "nav", key: "1" }
* children: 1.react.createElement(...)
* 2.我是文本
*
*/
export function createElement(type, config, children) {
//能夠看到這幾個聲明的變量就是一個初始化參數的做用
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
//這裏判斷若是config不爲空的話則提取config上的內容,賦值給前面初始化的變量
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref; //將config上的ref賦值給ref
}
if (hasValidKey(config)) {
key = '' + config.key; //將config上的key賦值給key
}
//將config上的self賦值給self
self = config.__self === undefined ? null : config.__self;
//將config上的source賦值給source
source = config.__source === undefined ? null : config.__source;
//下面的循環是將config上的內容複製到props上
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
//這裏是對children的操做,若是childrenLength爲1,那麼表示只有一個children,那麼就直接賦值給props的children屬性
//若是childrenLength大於1,則表示有多個children,那麼要作合併操做,將他們放到一個數組裏面。而後賦值給props的children屬性
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// Resolve default props
//這個type是建立的標籤名稱
//這裏處理默認的props,若是存在默認的props,則將默認的props賦值給當前的props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
//最後返回一個ReactElement實例對象
return ReactElement(
type, //這個type表明的是組件名
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
複製代碼
ReactCurrentOwner.current是個啥子東西?app
const ReactCurrentOwner = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Fiber),
currentDispatcher: (null: null | Dispatcher),
};
// 實際上這個current初始時是null,類型能夠是Fiber或null
複製代碼
最後返回的是一個ReactElement實例對象,那麼這個個ReactElement主要作了些什麼呢?ui
//簡化一下,發現最後返回的是這個element對象。這就是最終的虛擬DOM
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
//這個標誌表示這個元素是一個React Element
$$typeof: REACT_ELEMENT_TYPE,
//將屬性注入到元素上
type: type,
key: key,
ref: ref,
props: props,
// 記錄建立此元素的組建
_owner: owner,
};
return element;
};
複製代碼
能夠看到這個方法最後返回的是一個對象,這個對象結構對應着建立一個DOM的條件。這就是建立了一個ReactNode。發現也是很簡單的吧。哈哈。spa
在使用React建立組件以前咱們還有一個操做,就是初始化組建入口。經過判斷不一樣node類型來區分不一樣組件的入口。經過調用instantiateReactComponent方法進行初始化。3d
// Given a ReactNode, create an instance that will actually be mounted
// @param {ReactNode} node //參數是一個ReactNode
//參數是傳一個虛擬DOM節點
function instantiateReactComponent(node) {
var instance;
//若是ReactNode是一個空組件
//經過ReactEmptyComponent.create初始化一個空組件
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
//若是ReactNode是一個對象(DOM標籤或者自定義組件)
} else if (typeof node === 'object') {
var element = node;
invariant(
element && (typeof element.type === 'function' ||
typeof element.type === 'string'),
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
element.type == null ? element.type : typeof element.type,
getDeclarationErrorAddendum(element._owner)
);
//若是element的type類型爲string
//則調用ReactNativeComponent.createInternalComponent
if (typeof element.type === 'string') {
instance = ReactNativeComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
//不然初始化自定義組件
} else {
instance = new ReactCompositeComponentWrapper(element);
}
//若是ReactNode是一個字符串或者數字
//那麼調用ReactNativeComponent.createInstanceForText
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactNativeComponent.createInstanceForText(node);
} else {
invariant(
false,
'Encountered invalid React node of type %s',
typeof node
);
}
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
複製代碼