一.什么是jsx
jsx是語法糖 它是js和html的組合使用
為什么用jsx語法?
高效定義模版,通過babel編譯后使用 不會帶來性能問題
二.jsx語法轉化為js語法
jsx語法通過babel轉化為js語法 內部調用了React.createElement()方法
html標簽
自定義組件
React.Fragment組件
React.createElement(標簽,屬性props對象,子節點1,子節點2.....)
1.參數:標簽名,屬性對象,子節點 返回值:虛擬dom對象
2.標簽:1.字符串 2.組件(自定義組件:函數組件/class組件,react原生組件:React.Fragment等)
一般組件首字母大寫 如果bable轉化時 發現當前標簽的首字母為大寫 則表示當前的標簽是一個函數名稱 否則當前標簽為一個字符串
3.屬性props對象:寫在標簽上的屬性集合 一般為對象
4.子節點:表示子節點的集合 一般從React.createElement的第三個參數開始,如果對於當前標簽的子節點為字符串則參數值直接為字符串
如果當前節點的子節點不是字符串 則會生成新的React.createElement
如:<ul><li>我是li的子節點</li></ul> 1.ul而言 子節點:li 2.li而言 子節點我是li的子節點
虛擬dom:
調用createElement函數 返回ReactElement:該標簽名放到type上 該標簽的屬性和該標簽的子節點放到與該標簽同級的props上
三.React.createElement(type,config[...children])源碼分析
作用:根據指定的第一個參數 創建一個react元素
源碼解析://type:節點名稱(函數/字符串) config:節點名稱屬性(對象) children:節點名稱的子節點(字符串/新的React.createElement
function createElement(type, config, children) {
var propName; //提取保留名稱 var props = {}; var key = null; var ref = null; var self = null; var source = null; //標簽的屬性不為空時 說明標簽有屬性值 特殊處理:把key和ref賦值給單獨的變量 if (config != null) { //有合理的ref if (hasValidRef(config)) { ref = config.ref; } //有合理的key if (hasValidKey(config)) { key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; //config中剩余屬性,且不是原生屬性(RESERVED_PROPS對象的屬性),則添加到新props對象中 for (propName in config) { if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { props[propName] = config[propName]; //config去除key/ref 其他屬性的放到props對象中 } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. //子元素數量(第三個參數以及之后參數都是子元素 兄弟節點) var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); //聲明一個數組 //依次將children push到數組中 for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } { //凍結array 返回原來的childArray且不能被修改 防止有人修改庫的核心對象 凍結對象大大提高性能 if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; //父組件內部通過this.props.children獲取子組件的值 } //為子組件設置默認值 一般針對的是組件
//class com extends React.component 則com.defaultProps獲取當前組件自己的靜態方法
if (type && type.defaultProps) { //如果當前組件中有默認的defaultProps則把當前組件的默認內容 定義到defaultProps中 var defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { //如果父組件中對應的值為undefinde 則把默認值賦值賦值給props當作props的屬性 props[propName] = defaultProps[propName]; } } } { //一旦ref或者key存在 if (key || ref) { //如果type是組件的話 var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } //props:1.config的屬性值 2.children的屬性(字符串/數組)3.default的屬性值 return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); }
部分代碼解析:
(1) Object.prototype.hasOwnProperty()
//判斷某一個屬性是否在實例對象本身上,而不是在原型上 返回值:布爾值
let obj = { a: 1 };
obj.hasOwnProperty("a") //true
obj.hasOwnProperty("toString")//false
let obj = { foo: 123, get bar() { return '123' } }; //返回對象指定某一個屬性的描述對象 Object.getOwnPropertyDescriptor(obj, 'bar') //返回對象所有屬性的描述對象 Object.getOwnPropertyDescriptors(obj)
返回值
function hasValidKey(config) { { if (hasOwnProperty.call(config, 'key')) { var getter = Object.getOwnPropertyDescriptor(config, 'key').get; if (getter && getter.isReactWarning) { return false; } } } return config.key !== undefined; }
四,ReactElement源碼分析
作用:返回一個虛擬dom對象
源碼:
var ReactElement = function (type, key, ref, self, source, owner, props) { var element = { //因為react最終渲染dom時候,確保是react.createElement類型 需要判斷$$typeof===REACT_ELEMENT_TYPE $$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 }; { // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. //WeakMap element._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should // include every environment we run tests in), so the test framework // ignores it. Object.defineProperty(element._store, 'validated', { configurable: false, enumerable: false, writable: true, value: false }); // self and source are DEV only properties. Object.defineProperty(element, '_self', { configurable: false, enumerable: false, writable: false, value: self }); // Two elements created in two different places should be considered // equal for testing purposes and therefore we hide it from enumeration. Object.defineProperty(element, '_source', { configurable: false, enumerable: false, writable: false, value: source }); if (Object.freeze) { //代碼性能優化:將element的一些屬性配置為不可配置 提高性能 Object.freeze(element.props); Object.freeze(element); } } return element;//返回虛擬dom對象 };
節點名稱