1、JSX語法轉換到Js語法
從 JSX 轉換到 JS 會用到React.createElement()
,因此先熟悉下 JSX 到 JS 的轉換。html
這邊是 JSX 語法:react
<div id='one' class='two'> <span id="spanOne">this is spanOne</span> <span id="spanTwo">this is spanTwo</span> </div>
這邊是轉化成的 js 語法:git
React.createElement( "div", { id: "one", class: "two" }, React.createElement( "span", { id: "spanOne" }, "this is spanOne"), React.createElement("span", { id: "spanTwo" }, "this is spanTwo") );
React.createElement("標籤名","Object,包含div的props",'children子節點1','children子節點2','...')
es6
這邊是 JSX 語法:github
function Div(){ } <Div id='one' class='two'> <span id="spanOne">this is spanOne</span> <span id="spanTwo">this is spanTwo</span> </Div>
這邊是轉化成的 js 語法:數組
React.createElement(Div, {} , xxx );
若是標籤名大寫,則表示組件 Div(也就是function
),小寫表示 html 的標籤 <div>
微信
也就是說:自定義的組件必須大寫字母開頭app
2、React.createElement()
源碼地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.jside
做用:
建立React.Element
,示例請看1、JSX語法轉換到Js語法
測試
源碼:
//注意:react只寫了3個參數,實際上,從第三個參數日後都是children export function createElement(type, config, children) { let propName; // Reserved names are extracted const props = {}; let key = null; let ref = null; let self = null; let source = null; //賦給標籤的props不爲空時 if (config != null) { if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { //防止是Number key = '' + config.key; } //__self、__source 暫時不知道是幹啥用的屬性 self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { //若是config中的屬性不是標籤原生屬性,則放入props對象中 if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. //子元素數量 const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); //依次將children push進array中 for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } //若是是development環境的話 if (__DEV__) { //凍結array //未在微信發表 //https://www.jianshu.com/p/91e5dc520c0d?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends&from=singlemessage&isappinstalled=0 if (Object.freeze) { Object.freeze(childArray); } } //開發中寫的this.props.children就是子元素的集合 props.children = childArray; } // Resolve default props //爲傳入的props設置默認值,好比: //class Comp extends React.Component{ // static defaultProps = { // aaa: 'one', // bbb: () => {}, // ccc: {}, // }; // // } if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { //若是props數組中未設值,則設置默認值(注意:null也算設置了值) if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } if (__DEV__) { //一旦ref或key存在 if (key || ref) { //若是type是組件的話,賦值displayName const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; //可不看 if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement( type, //'div' key, //null ref, //null self, //null source, //null ReactCurrentOwner.current, //null或Fiber props, //自定義的屬性、方法,注意:props.children=childArray ); }
解析:
(1)hasValidRef()
做用:
判斷是否設置了ref的屬性,true
有,false
沒有
源碼:
//判斷是否設置了ref的屬性,true有,false沒有 function hasValidRef(config) { //若是是development環境的話 if (__DEV__) { //若是config中存在ref屬性的話 //在jQuery中 .call/.apply的更大做用是綁定this if (hasOwnProperty.call(config, 'ref')) { //Object.getOwnPropertyDescriptor() es5 //Object.getOwnPropertyDescriptors() es6 //https://blog.csdn.net/qq_30100043/article/details/53424963 //返回對象config的屬性ref 的get對象 const getter = Object.getOwnPropertyDescriptor(config, 'ref').get; //若是isReactWarning,則忽略ref屬性,返回false if (getter && getter.isReactWarning) { return false; } } } //<div ref={this.optionsTEchart} ></div> return config.ref !== undefined; }
① 注意:__DEV__
表示測試環境,是供React
內部測試的,能夠不看,我簡單地解釋了下
② 在jQuery
中fn.call(xxx,a1,a2,...)
或fn.apply(xxx,array)
的更大做用是綁定this
③ Object.getOwnPropertyDescriptor()
的做用是返回某個對象屬性的描述對象( descriptor )
好比:
var obj = { p: 'a' }; Object.getOwnPropertyDescriptor(obj, 'p') //返回 // Object { value: "a", // writable: true, // enumerable: true, // configurable: true }
關於Object.getOwnPropertyDescriptor()
和Object.getOwnPropertyDescriptors()
的區別,請看:https://blog.csdn.net/qq_30100043/article/details/53424963
(2)hasValidKey
做用:
判斷是否設置了key
,同hasValidRef
,不解釋了
源碼:
function hasValidKey(config) { if (__DEV__) { if (hasOwnProperty.call(config, 'key')) { const getter = Object.getOwnPropertyDescriptor(config, 'key').get; if (getter && getter.isReactWarning) { return false; } } } return config.key !== undefined; }
(3)雖然React.createElement()
只傳三個參數,但從第三個參數開始,利用arguments
來獲取剩下的參數
(4)Object.freeze()
使用Object.freeze()
凍結的對象是最嚴格的防篡改級別,既不可擴展,也是密封的,不可修改屬性。
對於 JS 庫做者而言,凍結對象可防止有人修改庫的核心對象。
關於 JS 凍結對象的方法,請看:JS紅皮書解讀之防篡改對象
(5)最後是 return 了ReactElement()
方法,注意props
中的children
屬性就是React
組件的children
react組件的children屬性不會被覆蓋:
父組件:
return( <DashBoard children={'bbbb'}> aaaa </DashBoard> )
子組件:
console.log(this.props)
結果:
3、ReactElement()
源碼地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.js
做用:
經過工廠模式建立React.Element
對象,你打印一個React
組件的話,會是下面這個樣子:
源碼:
/** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, no instanceof check * will work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @internal */ // type, //'div' // key, //null // ref, //null // self, //null // source, //null // ReactCurrentOwner.current, //null或Fiber // props, //自定義的屬性、方法,注意:props.children=childArray const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element //標識element的類型 //由於jsx都是經過createElement建立的,因此ReactElement的類型固定:爲REACT_ELEMENT_TYPE //重要!由於react最終渲染到DOM上時,須要判斷$$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. //記錄建立react.element的組件(this?) _owner: owner, }; if (__DEV__) { // 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. //驗證flag是不固定的.咱們將其放置在一個store上,從而能凍結整個object //這樣一旦它們被用在開發環境時,用WeakMap代替 //WeakMap // http://es6.ruanyifeng.com/#docs/set-map 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) { Object.freeze(element.props); Object.freeze(element); } } return element; };
解析:
(1)經過$$typeof
確保是React.Element
類型,從而渲染到真正的DOM
樹上
(2)__DEV__
註釋中有提到WeakMap
,
簡單說下WeakMap
的做用:
你往WeakMap
上的對象 a 添加數據,對象 b 引用 對象 a,以後對象 b 不引用 對象 a,a 就被垃圾回收,不用WeakMap
的話,即便對象 b 之後不引用對象 a了,a 也不會被垃圾回收,由於強引用是不會觸發垃圾回收機制的,須要手動刪除,很麻煩。
想更詳細地瞭解的話,能夠參考下這篇文章:
http://es6.ruanyifeng.com/#docs/set-map
關於垃圾回收機制,請看:淺談下垃圾回收機制(1)
(3)該方法比較簡單,就是初始化了一個對象,並將其標記爲React.Element
對象($$typeof=REACT_ELEMENT_TYPE
)
(完)